graphql-hive 0.4.2 → 0.5.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
  SHA256:
3
- metadata.gz: b2df4ea1405ff374b1d534b4b771e95a6e28765d0befce95492b482d675c0905
4
- data.tar.gz: 305c9e57226ea9943fb8f14d1b6b85a757455b2be8ccead3ade9d6c4c2bdf2b5
3
+ metadata.gz: b4c1afad31aef320c5c3994eeadba81a4e87fcf92c44f7bb3a1a1559ff1e3429
4
+ data.tar.gz: 604b0f65a3292a15154fb4f3808b4cf62f7d31d9a8c157c920d4882988ece36f
5
5
  SHA512:
6
- metadata.gz: 66216491e6445d6e3abe05689a820fe881da34048f2f676970e81e24ae5fea21300d1f0c1924d53e0a5925cfc4b715338cee4a8f913cef888538c9ad6855ef72
7
- data.tar.gz: f56b6f52dd900b2073fb229364a6cb9ab3d74f482ea7ae81e90be906d6912c195a7b6de08c91ac987c2029ae3abbe84951a561615b1152f1e9441bb92fce3b21
6
+ metadata.gz: 3a6e1de7d61c15382ab4270769f8f9e6647dadb58d6eeffe9c042663cc448cb0c263ef79dbeed85db220abb1c8b97e54df1a0fadba8f550538b0b6ce489eb83d
7
+ data.tar.gz: bd16af0fba744194dd093cef087f1bb04c4170a7f4d491939e6ceb8f4d0fc380e179ce7b0b721c655e2f606f463f82da0945df0308572c4c46fd20d8f5176608
@@ -8,33 +8,26 @@ on:
8
8
  - master
9
9
 
10
10
  jobs:
11
- rubocop:
11
+ standard:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v4
15
-
16
- - uses: ruby/setup-ruby@2a9a743e19810b9f3c38060637daf594dbd7b37f
17
- with:
18
- ruby-version: '3.3'
19
- bundler-cache: true
20
-
21
- - run: bundle exec rake rubocop
22
-
14
+ - name: Standard Ruby
15
+ uses: standardrb/standard-ruby-action@v1
23
16
  test:
24
17
  strategy:
25
18
  fail-fast: false
26
19
  matrix:
27
20
  ruby-version:
28
- - '3.1'
29
- - '3.2'
30
- - '3.3'
21
+ - "3.1"
22
+ - "3.2"
23
+ - "3.3"
31
24
  runs-on: ubuntu-latest
32
25
  steps:
33
- - uses: actions/checkout@v4
26
+ - uses: actions/checkout@v4
34
27
 
35
- - uses: ruby/setup-ruby@2a9a743e19810b9f3c38060637daf594dbd7b37f
36
- with:
37
- ruby-version: ${{ matrix.ruby-version }}
38
- bundler-cache: true
28
+ - uses: ruby/setup-ruby@2a9a743e19810b9f3c38060637daf594dbd7b37f
29
+ with:
30
+ ruby-version: ${{ matrix.ruby-version }}
31
+ bundler-cache: true
39
32
 
40
- - run: bundle exec rake spec
33
+ - run: bundle exec rake spec
data/.gitignore CHANGED
@@ -11,3 +11,9 @@
11
11
  .rspec_status
12
12
  *.gem
13
13
  k6/node_modules/
14
+
15
+ # IDEs
16
+ .idea
17
+
18
+ # k6
19
+ node_modules
data/.rubocop.yml CHANGED
@@ -1,41 +1,10 @@
1
- AllCops:
2
- TargetRubyVersion: 2.6
3
- NewCops: enable
4
- Exclude:
5
- - 'examples/**/*'
6
- - 'k6/**/*'
7
- - 'gemfiles/**/*'
8
- - 'tmp/**/*'
9
- - 'vendor/**/*'
10
-
11
- Metrics/BlockLength:
12
- Exclude:
13
- - 'spec/**/*'
14
-
15
- Layout/LineLength:
16
- Exclude:
17
- - 'spec/**/*'
18
-
19
- Naming/FileName:
20
- Enabled: false
21
-
22
- Style/ClassVars:
23
- Enabled: false
24
-
25
- Metrics/AbcSize:
26
- Enabled: false
27
-
28
- Metrics/PerceivedComplexity:
29
- Enabled: false
30
-
31
- Metrics/CyclomaticComplexity:
32
- Enabled: false
33
-
34
- Metrics/MethodLength:
35
- Enabled: false
36
-
37
- Metrics/ClassLength:
38
- Enabled: false
39
-
40
- Gemspec/DevelopmentDependencies:
41
- EnforcedStyle: 'gemspec'
1
+ require:
2
+ - standard
3
+ - standard-custom
4
+ - standard-performance
5
+ - rubocop-performance
6
+
7
+ inherit_gem:
8
+ standard: config/base.yml
9
+ standard-custom: config/base.yml
10
+ standard-performance: config/base.yml
data/Gemfile CHANGED
@@ -1,8 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source 'https://rubygems.org'
3
+ source "https://rubygems.org"
4
4
 
5
5
  git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  # Specify your gem's dependencies in graphql-hive.gemspec
8
8
  gemspec
9
+
10
+ group :development do
11
+ gem "bundler", "~> 2"
12
+ gem "rake", "~> 13"
13
+ gem "rspec", "~> 3"
14
+ gem "standardrb", "~> 1"
15
+ end
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql-hive (0.4.2)
4
+ graphql-hive (0.5.0)
5
5
  graphql (>= 2.3, < 3)
6
6
 
7
7
  GEM
@@ -14,16 +14,16 @@ GEM
14
14
  base64
15
15
  json (2.7.2)
16
16
  language_server-protocol (3.17.0.3)
17
- parallel (1.25.1)
18
- parser (3.3.3.0)
17
+ lint_roller (1.1.0)
18
+ parallel (1.26.3)
19
+ parser (3.3.5.0)
19
20
  ast (~> 2.4.1)
20
21
  racc
21
- racc (1.8.0)
22
+ racc (1.8.1)
22
23
  rainbow (3.1.1)
23
24
  rake (13.2.1)
24
25
  regexp_parser (2.9.2)
25
- rexml (3.3.1)
26
- strscan
26
+ rexml (3.3.8)
27
27
  rspec (3.13.0)
28
28
  rspec-core (~> 3.13.0)
29
29
  rspec-expectations (~> 3.13.0)
@@ -37,22 +37,38 @@ GEM
37
37
  diff-lcs (>= 1.2.0, < 2.0)
38
38
  rspec-support (~> 3.13.0)
39
39
  rspec-support (3.13.1)
40
- rubocop (1.64.1)
40
+ rubocop (1.65.1)
41
41
  json (~> 2.3)
42
42
  language_server-protocol (>= 3.17.0)
43
43
  parallel (~> 1.10)
44
44
  parser (>= 3.3.0.2)
45
45
  rainbow (>= 2.2.2, < 4.0)
46
- regexp_parser (>= 1.8, < 3.0)
46
+ regexp_parser (>= 2.4, < 3.0)
47
47
  rexml (>= 3.2.5, < 4.0)
48
48
  rubocop-ast (>= 1.31.1, < 2.0)
49
49
  ruby-progressbar (~> 1.7)
50
50
  unicode-display_width (>= 2.4.0, < 3.0)
51
- rubocop-ast (1.31.3)
51
+ rubocop-ast (1.32.3)
52
52
  parser (>= 3.3.1.0)
53
+ rubocop-performance (1.21.1)
54
+ rubocop (>= 1.48.1, < 2.0)
55
+ rubocop-ast (>= 1.31.1, < 2.0)
53
56
  ruby-progressbar (1.13.0)
54
- strscan (3.1.0)
55
- unicode-display_width (2.5.0)
57
+ standard (1.40.1)
58
+ language_server-protocol (~> 3.17.0.2)
59
+ lint_roller (~> 1.0)
60
+ rubocop (~> 1.65.0)
61
+ standard-custom (~> 1.0.0)
62
+ standard-performance (~> 1.4)
63
+ standard-custom (1.0.2)
64
+ lint_roller (~> 1.0)
65
+ rubocop (~> 1.50)
66
+ standard-performance (1.4.0)
67
+ lint_roller (~> 1.1)
68
+ rubocop-performance (~> 1.21.0)
69
+ standardrb (1.0.1)
70
+ standard
71
+ unicode-display_width (2.6.0)
56
72
 
57
73
  PLATFORMS
58
74
  arm64-darwin-23
@@ -63,7 +79,7 @@ DEPENDENCIES
63
79
  graphql-hive!
64
80
  rake (~> 13)
65
81
  rspec (~> 3)
66
- rubocop (~> 1)
82
+ standardrb (~> 1)
67
83
 
68
84
  BUNDLED WITH
69
85
  2.5.15
data/README.md CHANGED
@@ -154,9 +154,9 @@ class MySchema < GraphQL::Schema
154
154
  debug: false, # verbose logs
155
155
  logger: MyLogger.new,
156
156
  endpoint: 'app.graphql-hive.com',
157
- port: 80,
158
- buffer_size: 50, # forward the operations data to Hive every 50 requests
159
-
157
+ port: 80,
158
+ buffer_size: 50, # how many operations can be sent to hive in a single batch (AFTER sampling)
159
+
160
160
  collect_usage: true, # report usage to Hive
161
161
  collect_usage_sampling: {
162
162
  # optional members of `collect_usage_sampling`
@@ -164,8 +164,7 @@ class MySchema < GraphQL::Schema
164
164
  sampler: proc { |context| context.operation_name.includes?('someQuery') 1 : 0.5 }, # assign custom sampling rates (overrides `sampling rate`)
165
165
  at_least_once: true, # sample every distinct operation at least once
166
166
  key_generator: proc { |context| context.operation_name } # assign custom keys to distinguish between distinct operations
167
- }
168
-
167
+ },
169
168
  report_schema: true, # publish schema to Hive
170
169
  # mandatory if `report_schema: true`
171
170
  reporting: {
data/Rakefile CHANGED
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/gem_tasks'
4
- require 'rspec/core/rake_task'
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
5
 
6
6
  RSpec::Core::RakeTask.new(:spec)
7
7
 
8
- require 'rubocop/rake_task'
8
+ require "rubocop/rake_task"
9
9
  RuboCop::RakeTask.new
10
10
 
11
11
  task(default: %i[spec rubocop])
@@ -1,8 +1,8 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- gem 'graphql'
4
- gem 'graphql-hive', path: '../../'
5
- gem 'puma'
6
- gem 'rack-contrib'
7
- gem 'sinatra'
8
- gem 'sinatra-contrib'
3
+ gem "graphql"
4
+ gem "graphql-hive", path: "../../"
5
+ gem "puma"
6
+ gem "rack-contrib"
7
+ gem "sinatra"
8
+ gem "sinatra-contrib"
@@ -1,8 +1,8 @@
1
- require 'sinatra'
2
- require 'sinatra/json'
3
- require 'rack/contrib'
1
+ require "sinatra"
2
+ require "sinatra/json"
3
+ require "rack/contrib"
4
4
 
5
- require_relative 'schema'
5
+ require_relative "schema"
6
6
 
7
7
  # Test query:
8
8
  #
@@ -16,14 +16,14 @@ require_relative 'schema'
16
16
  class DemoApp < Sinatra::Base
17
17
  use Rack::JSONBodyParser
18
18
 
19
- post '/graphql' do
19
+ post "/graphql" do
20
20
  result = Schema.execute(
21
- params['query'],
21
+ params["query"],
22
22
  variables: params[:variables],
23
23
  operation_name: params[:operationName],
24
24
  context: {
25
- client_name: 'GraphQL Client',
26
- client_version: '1.0'
25
+ client_name: "GraphQL Client",
26
+ client_version: "1.0"
27
27
  }
28
28
  )
29
29
  json result
@@ -1,2 +1,2 @@
1
- require './app'
1
+ require "./app"
2
2
  run DemoApp
@@ -1,9 +1,9 @@
1
- require 'graphql'
2
- require 'graphql-hive'
1
+ require "graphql"
2
+ require "graphql-hive"
3
3
 
4
4
  module Types
5
5
  class PostType < GraphQL::Schema::Object
6
- description 'A blog post'
6
+ description "A blog post"
7
7
  field :id, ID, null: false
8
8
  field :title, String, null: false
9
9
  # fields should be queried in camel-case (this will be `truncatedPreview`)
@@ -12,29 +12,29 @@ module Types
12
12
  end
13
13
 
14
14
  class Types::PostInput < GraphQL::Schema::InputObject
15
- description 'Query Post arguments'
15
+ description "Query Post arguments"
16
16
  argument :id, ID, required: true
17
17
  end
18
18
 
19
19
  class Types::TestEnum < GraphQL::Schema::Enum
20
- value 'TEST1'
21
- value 'TEST2'
22
- value 'TEST3'
20
+ value "TEST1"
21
+ value "TEST2"
22
+ value "TEST3"
23
23
  end
24
24
 
25
25
  class QueryType < GraphQL::Schema::Object
26
- description 'The query root of this schema'
26
+ description "The query root of this schema"
27
27
 
28
28
  # First describe the field signature:
29
- field :post, Types::PostType, 'Find a post by ID' do
29
+ field :post, Types::PostType, "Find a post by ID" do
30
30
  argument :input, [Types::PostInput]
31
31
  argument :test, Types::TestEnum
32
32
  end
33
33
 
34
34
  # Then provide an implementation:
35
35
  def post(input:, test:)
36
- { id: 1, title: 'GraphQL Hive with `graphql-ruby`',
37
- truncated_preview: 'Monitor operations, inspect your queries and publish your GraphQL schema with GraphQL Hive' }
36
+ {id: 1, title: "GraphQL Hive with `graphql-ruby`",
37
+ truncated_preview: "Monitor operations, inspect your queries and publish your GraphQL schema with GraphQL Hive"}
38
38
  end
39
39
  end
40
40
 
@@ -44,9 +44,9 @@ class Schema < GraphQL::Schema
44
44
  use(
45
45
  GraphQL::Hive,
46
46
  buffer_size: 2,
47
- token: 'YOUR_TOKEN',
47
+ token: "YOUR_TOKEN",
48
48
  debug: true,
49
- reporting: { author: 'Charly Poly', commit: '109bb1e748bae21bdfe663c0ffc7e830' },
50
- client_info: proc { |context|{ name: context[:client_name], version: context[:client_version] }}
49
+ reporting: {author: "Charly Poly", commit: "109bb1e748bae21bdfe663c0ffc7e830"},
50
+ client_info: proc { |context| {name: context[:client_name], version: context[:client_version]} }
51
51
  )
52
52
  end
data/graphql-hive.gemspec CHANGED
@@ -1,33 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path('lib', __dir__)
3
+ lib = File.expand_path("lib", __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'graphql-hive/version'
5
+ require "graphql-hive/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
- spec.name = 'graphql-hive'
9
- spec.version = Graphql::Hive::VERSION
10
- spec.authors = ['Charly POLY']
11
- spec.email = ['cpoly55@gmail.com']
8
+ spec.name = "graphql-hive"
9
+ spec.version = Graphql::Hive::VERSION
10
+ spec.authors = ["Charly POLY"]
11
+ spec.email = ["cpoly55@gmail.com"]
12
12
 
13
- spec.summary = '"GraphQL Hive integration for `graphql-ruby`"'
14
- spec.description = '"Monitor operations, inspect your queries and publish your GraphQL schema with GraphQL Hive"'
15
- spec.homepage = 'https://docs.graphql-hive.com/specs/integrations'
16
- spec.license = 'MIT'
13
+ spec.summary = '"GraphQL Hive integration for `graphql-ruby`"'
14
+ spec.description = '"Monitor operations, inspect your queries and publish your GraphQL schema with GraphQL Hive"'
15
+ spec.homepage = "https://docs.graphql-hive.com/specs/integrations"
16
+ spec.license = "MIT"
17
17
 
18
- spec.metadata = { 'rubygems_mfa_required' => 'true' }
18
+ spec.metadata = {"rubygems_mfa_required" => "true"}
19
19
 
20
- spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
20
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
21
21
 
22
- spec.require_paths = ['lib']
23
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ spec.require_paths = ["lib"]
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
24
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
25
  end
26
26
 
27
- spec.add_dependency 'graphql', '>= 2.3', '< 3'
28
-
29
- spec.add_development_dependency 'bundler', '~> 2'
30
- spec.add_development_dependency 'rake', '~> 13'
31
- spec.add_development_dependency 'rspec', '~> 3'
32
- spec.add_development_dependency 'rubocop', '~> 1'
27
+ spec.add_dependency "graphql", ">= 2.3", "< 3"
33
28
  end
data/k6/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # k6 (Integration Tests)
2
+
3
+ This directory contains integration tests for the k6 project. In
4
+ summary, this folder does the following:
5
+ 1. Start a ruby server, with hive enabled
6
+ 2. Start a ruby server, with hive disabled
7
+ 3. Start a mock GraphQL Hive `/usage` api
8
+ 4. Run GraphQL Requests against the 2 servers
9
+ 5. Compare the results
10
+
11
+ ## Prerequisites
12
+ 1. Ruby > 3.0.0
13
+ 2. Node > 14.0.0
14
+ 3. [k6 installed](https://grafana.com/docs/k6/latest/set-up/install-k6/)
15
+
16
+ ## Running the tests (locally)
17
+
18
+ Start the ruby server with HIVE_ENABLED=true
19
+ ```bash
20
+ cd graphql-api
21
+ HIVE_ENABLED=true bundle exec puma -t 0:1 -p 9292
22
+ ```
23
+
24
+ Start the ruby server with HIVE_ENABLED=false
25
+ ```bash
26
+ HIVE_ENABLED=false bundle exec puma -t 0:1 -p 9291
27
+ ```
28
+
29
+ Start the usage mock server
30
+ ```bash
31
+ node usage-mock.js
32
+ ```
33
+
34
+ Run k6 tests:
35
+ ```bash
36
+ k6 run k6.js
37
+ ```
@@ -1,9 +1,9 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
 
3
- gem 'graphql', '~> 2'
4
- gem 'graphql-hive', path: '../../'
5
- gem 'puma', '~> 6'
6
- gem 'racc'
7
- gem 'rack-contrib', '~> 2'
8
- gem 'sinatra', '~> 2'
9
- gem 'sinatra-contrib'
3
+ gem "graphql", "~> 2"
4
+ gem "graphql-hive", path: "../../"
5
+ gem "puma", "~> 6"
6
+ gem "racc"
7
+ gem "rack-contrib", "~> 2"
8
+ gem "sinatra", "~> 2"
9
+ gem "sinatra-contrib"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ../..
3
3
  specs:
4
- graphql-hive (0.4.2)
4
+ graphql-hive (0.5.0)
5
5
  graphql (>= 2.3, < 3)
6
6
 
7
7
  GEM
@@ -1,24 +1,24 @@
1
- require 'sinatra'
2
- require 'sinatra/json'
3
- require 'rack/contrib'
1
+ require "sinatra"
2
+ require "sinatra/json"
3
+ require "rack/contrib"
4
4
 
5
- require_relative 'schema'
5
+ require_relative "schema"
6
6
 
7
7
  class DemoApp < Sinatra::Base
8
8
  use Rack::JSONBodyParser
9
9
 
10
- get '/' do
10
+ get "/" do
11
11
  status 200
12
12
  end
13
13
 
14
- post '/graphql' do
14
+ post "/graphql" do
15
15
  result = Schema.execute(
16
- params['query'],
16
+ params["query"],
17
17
  variables: params[:variables],
18
18
  operation_name: params[:operationName],
19
19
  context: {
20
- client_name: 'GraphQL Client',
21
- client_version: '1.0'
20
+ client_name: "GraphQL Client",
21
+ client_version: "1.0"
22
22
  }
23
23
  )
24
24
  json result
@@ -1,2 +1,2 @@
1
- require './app'
1
+ require "./app"
2
2
  run DemoApp
@@ -1,9 +1,9 @@
1
- require 'graphql'
2
- require 'graphql-hive'
1
+ require "graphql"
2
+ require "graphql-hive"
3
3
 
4
4
  module Types
5
5
  class PostType < GraphQL::Schema::Object
6
- description 'A blog post'
6
+ description "A blog post"
7
7
  field :id, ID, null: false
8
8
  field :title, String, null: false
9
9
  # fields should be queried in camel-case (this will be `truncatedPreview`)
@@ -12,17 +12,17 @@ module Types
12
12
  end
13
13
 
14
14
  class QueryType < GraphQL::Schema::Object
15
- description 'The query root of this schema'
15
+ description "The query root of this schema"
16
16
 
17
17
  # First describe the field signature:
18
- field :post, Types::PostType, 'Find a post by ID' do
18
+ field :post, Types::PostType, "Find a post by ID" do
19
19
  argument :id, [ID]
20
20
  end
21
21
 
22
22
  # Then provide an implementation:
23
23
  def post(id:)
24
- { id: 1, title: 'GraphQL Hive with `graphql-ruby`',
25
- truncated_preview: 'Monitor operations, inspect your queries and publish your GraphQL schema with GraphQL Hive' }
24
+ {id: 1, title: "GraphQL Hive with `graphql-ruby`",
25
+ truncated_preview: "Monitor operations, inspect your queries and publish your GraphQL schema with GraphQL Hive"}
26
26
  end
27
27
  end
28
28
 
@@ -31,11 +31,11 @@ class Schema < GraphQL::Schema
31
31
 
32
32
  use(
33
33
  GraphQL::Hive,
34
- enabled: ENV['HIVE_ENABLED'] === 'true',
35
- endpoint: 'localhost',
34
+ enabled: ENV["HIVE_ENABLED"] === "true",
35
+ endpoint: "localhost",
36
36
  debug: true,
37
37
  port: 8888,
38
- token: 'stress-token',
38
+ token: "stress-token",
39
39
  report_schema: false
40
40
  )
41
41
  end
@@ -73,7 +73,7 @@ module GraphQL
73
73
  end
74
74
 
75
75
  def make_id(*tokens)
76
- tokens.join('.')
76
+ tokens.join(".")
77
77
  end
78
78
  end
79
79
  end
@@ -0,0 +1,26 @@
1
+ module GraphQL
2
+ class Hive < GraphQL::Tracing::PlatformTracing
3
+ # BoundedQueue is being used so that the queue does not grow indefinitely
4
+ # We do not use `SizedQueue` because it blocks the thread when the queue is full with a .wait call
5
+ # This would go against us not impacting the application performance with the usage reporter
6
+ class BoundedQueue < Thread::Queue
7
+ def initialize(bound:, logger:)
8
+ @bound = bound
9
+ @logger = logger
10
+ @lock = Mutex.new
11
+
12
+ super()
13
+ end
14
+
15
+ def push(item)
16
+ @lock.synchronize do
17
+ if size >= @bound
18
+ @logger.error("BoundedQueue is full, discarding operation")
19
+ return
20
+ end
21
+ super
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'net/http'
4
- require 'uri'
3
+ require "net/http"
4
+ require "uri"
5
5
 
6
6
  module GraphQL
7
7
  class Hive < GraphQL::Tracing::PlatformTracing
@@ -14,9 +14,9 @@ module GraphQL
14
14
  def send(path, body, _log_type)
15
15
  uri =
16
16
  URI::HTTP.build(
17
- scheme: @options[:port].to_s == '443' ? 'https' : 'http',
18
- host: @options[:endpoint] || 'app.graphql-hive.com',
19
- port: @options[:port] || '443',
17
+ scheme: (@options[:port].to_s == "443") ? "https" : "http",
18
+ host: @options[:endpoint] || "app.graphql-hive.com",
19
+ port: @options[:port] || "443",
20
20
  path: path
21
21
  )
22
22
 
@@ -26,25 +26,25 @@ module GraphQL
26
26
 
27
27
  @options[:logger].debug(response.inspect)
28
28
  @options[:logger].debug(response.body.inspect)
29
- rescue StandardError => e
29
+ rescue => e
30
30
  @options[:logger].fatal("Failed to send data: #{e}")
31
31
  end
32
32
 
33
33
  def setup_http(uri)
34
34
  http = ::Net::HTTP.new(uri.host, uri.port)
35
- http.use_ssl = @options[:port].to_s == '443'
35
+ http.use_ssl = @options[:port].to_s == "443"
36
36
  http.read_timeout = 2
37
37
  http
38
38
  end
39
39
 
40
40
  def build_request(uri, body)
41
41
  request = Net::HTTP::Post.new(uri.request_uri)
42
- request['Authorization'] = @options[:token]
43
- request['X-Usage-API-Version'] = '2'
44
- request['content-type'] = 'application/json'
45
- request['User-Agent'] = "Hive@#{Graphql::Hive::VERSION}"
46
- request['graphql-client-name'] = 'Hive Ruby Client'
47
- request['graphql-client-version'] = Graphql::Hive::VERSION
42
+ request["Authorization"] = @options[:token]
43
+ request["X-Usage-API-Version"] = "2"
44
+ request["content-type"] = "application/json"
45
+ request["User-Agent"] = "Hive@#{Graphql::Hive::VERSION}"
46
+ request["graphql-client-name"] = "Hive Ruby Client"
47
+ request["graphql-client-version"] = Graphql::Hive::VERSION
48
48
  request.body = JSON.generate(body)
49
49
  request
50
50
  end
@@ -10,12 +10,12 @@ module GraphQL
10
10
  @out.append(str)
11
11
  end
12
12
 
13
- def print_node(node, indent: '')
13
+ def print_node(node, indent: "")
14
14
  case node
15
15
  when Float, Integer
16
- print_string '0'
16
+ print_string "0"
17
17
  when String
18
- print_string ''
18
+ print_string ""
19
19
  else
20
20
  super
21
21
  end
@@ -23,16 +23,16 @@ module GraphQL
23
23
 
24
24
  # from GraphQL::Language::Printer with sort_by name
25
25
  # ignores aliases
26
- def print_field(field, indent: '')
26
+ def print_field(field, indent: "")
27
27
  print_string(indent)
28
28
  print_string(field.name)
29
29
  if field.arguments.any?
30
- print_string('(')
30
+ print_string("(")
31
31
  field.arguments.sort_by(&:name).each_with_index do |a, i|
32
32
  print_argument(a)
33
- print_string(', ') if i < field.arguments.size - 1
33
+ print_string(", ") if i < field.arguments.size - 1
34
34
  end
35
- print_string(')')
35
+ print_string(")")
36
36
  end
37
37
  print_directives(field.directives)
38
38
  print_selections(field.selections, indent: indent)
@@ -43,7 +43,7 @@ module GraphQL
43
43
  end
44
44
 
45
45
  # from GraphQL::Language::Printer with sort_by name
46
- def print_selections(selections, indent: '')
46
+ def print_selections(selections, indent: "")
47
47
  sorted_nodes = selections.sort_by do |s|
48
48
  next s.name if s.respond_to?(:name)
49
49
  next s.type.name if s.respond_to?(:type)
@@ -55,35 +55,35 @@ module GraphQL
55
55
 
56
56
  # from GraphQL::Language::Printer with sort_by name
57
57
  def print_directive(directive)
58
- print_string('@')
58
+ print_string("@")
59
59
  print_string(directive.name)
60
60
 
61
61
  return if directive.arguments.blank?
62
62
 
63
- print_string('(')
63
+ print_string("(")
64
64
  directive.arguments.sort_by(&:name).each_with_index do |a, i|
65
65
  print_argument(a)
66
- print_string(', ') if i < directive.arguments.size - 1
66
+ print_string(", ") if i < directive.arguments.size - 1
67
67
  end
68
- print_string(')')
68
+ print_string(")")
69
69
  end
70
70
 
71
71
  # from GraphQL::Language::Printer with sort_by name
72
- def print_operation_definition(operation_definition, indent: '')
72
+ def print_operation_definition(operation_definition, indent: "")
73
73
  print_string(indent)
74
74
  print_string(operation_definition.operation_type)
75
75
  if operation_definition.name
76
- print_string(' ')
76
+ print_string(" ")
77
77
  print_string(operation_definition.name)
78
78
  end
79
79
 
80
80
  if operation_definition.variables.any?
81
- print_string('(')
81
+ print_string("(")
82
82
  operation_definition.variables.sort_by(&:name).each_with_index do |v, i|
83
83
  print_variable_definition(v)
84
- print_string(', ') if i < operation_definition.variables.size - 1
84
+ print_string(", ") if i < operation_definition.variables.size - 1
85
85
  end
86
- print_string(')')
86
+ print_string(")")
87
87
  end
88
88
 
89
89
  print_directives(operation_definition.directives)
@@ -8,28 +8,28 @@ module GraphQL
8
8
  # backwards compatibility with old `collect_usage_sampling` field
9
9
  if sampling_options.is_a?(Numeric)
10
10
  logger&.warn(
11
- '`collect_usage_sampling` is deprecated for fixed sampling rates, ' \
12
- 'use `collect_usage_sampling: { sample_rate: XX }` instead'
11
+ "`collect_usage_sampling` is deprecated for fixed sampling rates, " \
12
+ "use `collect_usage_sampling: { sample_rate: XX }` instead"
13
13
  )
14
14
  passed_sampling_rate = sampling_options
15
- sampling_options = { sample_rate: passed_sampling_rate }
15
+ sampling_options = {sample_rate: passed_sampling_rate}
16
16
  end
17
17
 
18
18
  sampling_options ||= {}
19
19
 
20
20
  @sampler = if sampling_options[:sampler]
21
- Sampling::DynamicSampler.new(
22
- sampling_options[:sampler],
23
- sampling_options[:at_least_once],
24
- sampling_options[:key_generator]
25
- )
26
- else
27
- Sampling::BasicSampler.new(
28
- sampling_options[:sample_rate],
29
- sampling_options[:at_least_once],
30
- sampling_options[:key_generator]
31
- )
32
- end
21
+ Sampling::DynamicSampler.new(
22
+ sampling_options[:sampler],
23
+ sampling_options[:at_least_once],
24
+ sampling_options[:key_generator]
25
+ )
26
+ else
27
+ Sampling::BasicSampler.new(
28
+ sampling_options[:sample_rate],
29
+ sampling_options[:at_least_once],
30
+ sampling_options[:key_generator]
31
+ )
32
+ end
33
33
  end
34
34
 
35
35
  def sample?(operation)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql-hive/sampling/sampling_context'
3
+ require "graphql-hive/sampling/sampling_context"
4
4
 
5
5
  module GraphQL
6
6
  class Hive
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'graphql-hive/sampling/sampling_context'
3
+ require "graphql-hive/sampling/sampling_context"
4
4
 
5
5
  module GraphQL
6
6
  class Hive
@@ -16,7 +16,7 @@ module GraphQL
16
16
  def get_sample_context(operation)
17
17
  _, queries, results, = operation
18
18
 
19
- operation_name = queries.map(&:operations).map(&:keys).flatten.compact.join(', ')
19
+ operation_name = queries.map(&:operations).map(&:keys).flatten.compact.join(", ")
20
20
 
21
21
  parsed_definitions = []
22
22
  queries.each do |query|
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'digest'
4
- require 'graphql-hive/analyzer'
5
- require 'graphql-hive/printer'
3
+ require "digest"
4
+ require "graphql-hive/analyzer"
5
+ require "graphql-hive/printer"
6
+ require "graphql-hive/bounded_queue"
6
7
 
7
8
  module GraphQL
8
9
  class Hive < GraphQL::Tracing::PlatformTracing
@@ -16,14 +17,11 @@ module GraphQL
16
17
 
17
18
  def initialize(options, client)
18
19
  @@instance = self
19
-
20
20
  @options = options
21
21
  @client = client
22
-
23
22
  @options_mutex = Mutex.new
24
- @queue = Queue.new
25
-
26
23
  @sampler = Sampler.new(options[:collect_usage_sampling], options[:logger]) # NOTE: logs for deprecated field
24
+ @queue = BoundedQueue.new(bound: options[:buffer_size], logger: options[:logger])
27
25
 
28
26
  start_thread
29
27
  end
@@ -45,30 +43,35 @@ module GraphQL
45
43
 
46
44
  def start_thread
47
45
  if @thread&.alive?
48
- @options[:logger].warn('Tried to start operations flushing thread but it was already alive')
46
+ @options[:logger].warn("Tried to start operations flushing thread but it was already alive")
49
47
  return
50
48
  end
51
49
 
52
50
  @thread = Thread.new do
53
51
  buffer = []
54
52
  while (operation = @queue.pop(false))
55
- @options[:logger].debug("processing operation from queue: #{operation}")
56
- buffer << operation if @sampler.sample?(operation)
57
-
58
- @options_mutex.synchronize do
59
- if buffer.size >= @options[:buffer_size]
60
- @options[:logger].debug('buffer is full, sending!')
61
- process_operations(buffer)
62
- buffer = []
53
+ begin
54
+ @options[:logger].debug("processing operation from queue: #{operation}")
55
+ buffer << operation if @sampler.sample?(operation)
56
+
57
+ @options_mutex.synchronize do
58
+ if buffer.size >= @options[:buffer_size]
59
+ @options[:logger].debug("buffer is full, sending!")
60
+ process_operations(buffer)
61
+ buffer = []
62
+ end
63
63
  end
64
+ rescue => e
65
+ buffer = []
66
+ @options[:logger].error(e)
64
67
  end
65
68
  end
66
69
 
67
70
  unless buffer.empty?
68
- @options[:logger].debug('shuting down with buffer, sending!')
71
+ @options[:logger].debug("shuting down with buffer, sending!")
69
72
  process_operations(buffer)
70
73
  end
71
- rescue StandardError => e
74
+ rescue => e
72
75
  # ensure configured logger receives exception as well in setups where STDERR might not be
73
76
  # monitored.
74
77
  @options[:logger].error(e)
@@ -88,7 +91,7 @@ module GraphQL
88
91
 
89
92
  @options[:logger].debug("sending report: #{report}")
90
93
 
91
- @client.send('/usage', report, :usage)
94
+ @client.send(:"/usage", report, :usage)
92
95
  end
93
96
 
94
97
  def add_operation_to_report(report, operation)
@@ -96,8 +99,8 @@ module GraphQL
96
99
 
97
100
  errors = errors_from_results(results)
98
101
 
99
- operation_name = queries.map(&:operations).map(&:keys).flatten.compact.join(', ')
100
- operation = ''
102
+ operation_name = queries.map(&:operations).map(&:keys).flatten.compact.join(", ")
103
+ operation = ""
101
104
  fields = Set.new
102
105
 
103
106
  queries.each do |query|
@@ -131,7 +134,7 @@ module GraphQL
131
134
 
132
135
  if results[0]
133
136
  context = results[0].query.context
134
- operation_record[:metadata] = { client: @options[:client_info].call(context) } if @options[:client_info]
137
+ operation_record[:metadata] = {client: @options[:client_info].call(context)} if @options[:client_info]
135
138
  end
136
139
 
137
140
  report[:map][operation_map_key] = {
@@ -144,9 +147,9 @@ module GraphQL
144
147
  end
145
148
 
146
149
  def errors_from_results(results)
147
- acc = { errorsTotal: 0 }
150
+ acc = {errorsTotal: 0}
148
151
  results.each do |result|
149
- errors = result.to_h.fetch('errors', [])
152
+ errors = result.to_h.fetch("errors", [])
150
153
  errors.each do
151
154
  acc[:errorsTotal] += 1
152
155
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Graphql
4
4
  module Hive
5
- VERSION = '0.4.2'
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
data/lib/graphql-hive.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'logger'
4
- require 'securerandom'
3
+ require "logger"
4
+ require "securerandom"
5
5
 
6
- require 'graphql-hive/version'
7
- require 'graphql-hive/usage_reporter'
8
- require 'graphql-hive/client'
6
+ require "graphql-hive/version"
7
+ require "graphql-hive/usage_reporter"
8
+ require "graphql-hive/client"
9
9
 
10
- require 'graphql-hive/sampler'
11
- require 'graphql-hive/sampling/basic_sampler'
12
- require 'graphql-hive/sampling/dynamic_sampler'
10
+ require "graphql-hive/sampler"
11
+ require "graphql-hive/sampling/basic_sampler"
12
+ require "graphql-hive/sampling/dynamic_sampler"
13
13
 
14
14
  module GraphQL
15
15
  # GraphQL Hive usage collector and schema reporter
@@ -31,7 +31,7 @@ module GraphQL
31
31
  DEFAULT_OPTIONS = {
32
32
  enabled: true,
33
33
  debug: false,
34
- port: '443',
34
+ port: "443",
35
35
  collect_usage: true,
36
36
  read_operations: true,
37
37
  report_schema: true,
@@ -41,14 +41,14 @@ module GraphQL
41
41
  }.freeze
42
42
 
43
43
  self.platform_keys = {
44
- 'lex' => 'lex',
45
- 'parse' => 'parse',
46
- 'validate' => 'validate',
47
- 'analyze_query' => 'analyze_query',
48
- 'analyze_multiplex' => 'analyze_multiplex',
49
- 'execute_multiplex' => 'execute_multiplex',
50
- 'execute_query' => 'execute_query',
51
- 'execute_query_lazy' => 'execute_query_lazy'
44
+ "lex" => "lex",
45
+ "parse" => "parse",
46
+ "validate" => "validate",
47
+ "analyze_query" => "analyze_query",
48
+ "analyze_multiplex" => "analyze_multiplex",
49
+ "execute_multiplex" => "execute_multiplex",
50
+ "execute_query" => "execute_query",
51
+ "execute_query_lazy" => "execute_query_lazy"
52
52
  }
53
53
 
54
54
  def initialize(options = {})
@@ -84,7 +84,7 @@ module GraphQL
84
84
  def platform_trace(platform_key, _key, data)
85
85
  return yield unless @options[:enabled] && @options[:collect_usage]
86
86
 
87
- if platform_key == 'execute_multiplex'
87
+ if platform_key == "execute_multiplex"
88
88
  if data[:multiplex]
89
89
  queries = data[:multiplex].queries
90
90
  timestamp = (Time.now.utc.to_f * 1000).to_i
@@ -134,25 +134,26 @@ module GraphQL
134
134
  options[:logger] = Logger.new($stderr)
135
135
  original_formatter = Logger::Formatter.new
136
136
  options[:logger].formatter = proc { |severity, datetime, progname, msg|
137
- original_formatter.call(severity, datetime, progname, "[hive] #{msg.dump}")
137
+ msg = msg.respond_to?(:dump) ? msg.dump : msg
138
+ original_formatter.call(severity, datetime, progname, "[hive] #{msg}")
138
139
  }
139
140
  options[:logger].level = options[:debug] ? Logger::DEBUG : Logger::INFO
140
141
  end
141
142
  if !options.include?(:token) && (!options.include?(:enabled) || options.enabled)
142
- options[:logger].warn('`token` options is missing')
143
+ options[:logger].warn("`token` options is missing")
143
144
  options[:enabled] = false
144
145
  false
145
146
  elsif options[:report_schema] &&
147
+ (
148
+ !options.include?(:reporting) ||
146
149
  (
147
- !options.include?(:reporting) ||
148
- (
149
- options.include?(:reporting) && (
150
- !options[:reporting].include?(:author) || !options[:reporting].include?(:commit)
151
- )
150
+ options.include?(:reporting) && (
151
+ !options[:reporting].include?(:author) || !options[:reporting].include?(:commit)
152
152
  )
153
153
  )
154
+ )
154
155
 
155
- options[:logger].warn('`reporting.author` and `reporting.commit` options are required')
156
+ options[:logger].warn("`reporting.author` and `reporting.commit` options are required")
156
157
  false
157
158
  end
158
159
  true
@@ -167,7 +168,7 @@ module GraphQL
167
168
 
168
169
  body = {
169
170
  query: REPORT_SCHEMA_MUTATION,
170
- operationName: 'schemaPublish',
171
+ operationName: "schemaPublish",
171
172
  variables: {
172
173
  input: {
173
174
  sdl: sdl,
@@ -180,7 +181,7 @@ module GraphQL
180
181
  }
181
182
  }
182
183
 
183
- @client.send('/registry', body, :'report-schema')
184
+ @client.send(:"/registry", body, :"report-schema")
184
185
  end
185
186
  end
186
187
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-hive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charly POLY
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-04 00:00:00.000000000 Z
11
+ date: 2024-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -30,62 +30,6 @@ dependencies:
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '3'
33
- - !ruby/object:Gem::Dependency
34
- name: bundler
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '2'
40
- type: :development
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '2'
47
- - !ruby/object:Gem::Dependency
48
- name: rake
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '13'
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '13'
61
- - !ruby/object:Gem::Dependency
62
- name: rspec
63
- requirement: !ruby/object:Gem::Requirement
64
- requirements:
65
- - - "~>"
66
- - !ruby/object:Gem::Version
67
- version: '3'
68
- type: :development
69
- prerelease: false
70
- version_requirements: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: '3'
75
- - !ruby/object:Gem::Dependency
76
- name: rubocop
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - "~>"
80
- - !ruby/object:Gem::Version
81
- version: '1'
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '1'
89
33
  description: '"Monitor operations, inspect your queries and publish your GraphQL schema
90
34
  with GraphQL Hive"'
91
35
  email:
@@ -112,6 +56,7 @@ files:
112
56
  - examples/simple-api/config.ru
113
57
  - examples/simple-api/schema.rb
114
58
  - graphql-hive.gemspec
59
+ - k6/README.md
115
60
  - k6/graphql-api/Gemfile
116
61
  - k6/graphql-api/Gemfile.lock
117
62
  - k6/graphql-api/app.rb
@@ -123,6 +68,7 @@ files:
123
68
  - k6/yarn.lock
124
69
  - lib/graphql-hive.rb
125
70
  - lib/graphql-hive/analyzer.rb
71
+ - lib/graphql-hive/bounded_queue.rb
126
72
  - lib/graphql-hive/client.rb
127
73
  - lib/graphql-hive/printer.rb
128
74
  - lib/graphql-hive/sampler.rb