graphql-hive 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +27 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +37 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +58 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +144 -0
- data/Rakefile +11 -0
- data/cover.png +0 -0
- data/examples/simple-api/Gemfile +8 -0
- data/examples/simple-api/Gemfile.lock +48 -0
- data/examples/simple-api/app.rb +31 -0
- data/examples/simple-api/config.ru +2 -0
- data/examples/simple-api/schema.rb +47 -0
- data/graphql-hive.gemspec +33 -0
- data/lib/graphql-hive/analyzer.rb +40 -0
- data/lib/graphql-hive/client.rb +42 -0
- data/lib/graphql-hive/printer.rb +65 -0
- data/lib/graphql-hive/usage_reporter.rb +142 -0
- data/lib/graphql-hive/version.rb +7 -0
- data/lib/graphql-hive.rb +206 -0
- data/operations-dashboard.png +0 -0
- metadata +139 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 63bc735d6516da78a9f7cab670defd21e68a8295132dba4f871d0b4fa6f8b944
|
4
|
+
data.tar.gz: df3580ad1ebabd31c5ec3960811c1d4defc1702c020c1152ee029ac2879574cc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0624adc8d88ef8ef468ec08b4fa9a0c3f375bd35a2a700dac3a4bca22631c1f88aec175405d598210b48d330c145a878af11188dc396c45f52dd1706bb99b784
|
7
|
+
data.tar.gz: 7c2cc7f8327471969eb9512e5ab4550f9f9093042a930fe1c497adf2958c01f868f468ed6aac3ec2def5d750b5352e7543dc9c1168e0d049a89134939460476f
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: CI
|
2
|
+
on:
|
3
|
+
- pull_request
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
rubocop:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v3
|
10
|
+
- uses: ruby/setup-ruby@359bebbc29cbe6c87da6bc9ea3bc930432750108
|
11
|
+
with:
|
12
|
+
ruby-version: 2.6
|
13
|
+
bundler-cache: true
|
14
|
+
- run: bundle exec rake rubocop
|
15
|
+
test:
|
16
|
+
strategy:
|
17
|
+
fail-fast: false
|
18
|
+
matrix:
|
19
|
+
ruby-version: ['3.1', '3.0', '2.7', '2.6']
|
20
|
+
runs-on: ubuntu-latest
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v3
|
23
|
+
- uses: ruby/setup-ruby@359bebbc29cbe6c87da6bc9ea3bc930432750108
|
24
|
+
with:
|
25
|
+
ruby-version: ${{ matrix.ruby-version }}
|
26
|
+
bundler-cache: true
|
27
|
+
- run: bundle exec rake spec
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
NewCops: enable
|
4
|
+
Exclude:
|
5
|
+
- 'examples/**/*'
|
6
|
+
- 'gemfiles/**/*'
|
7
|
+
- 'tmp/**/*'
|
8
|
+
- 'vendor/**/*'
|
9
|
+
|
10
|
+
Metrics/BlockLength:
|
11
|
+
Exclude:
|
12
|
+
- 'spec/**/*'
|
13
|
+
|
14
|
+
Layout/LineLength:
|
15
|
+
Exclude:
|
16
|
+
- 'spec/**/*'
|
17
|
+
|
18
|
+
Naming/FileName:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/ClassVars:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Metrics/AbcSize:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Metrics/PerceivedComplexity:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Metrics/CyclomaticComplexity:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Metrics/MethodLength:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
Metrics/ClassLength:
|
37
|
+
Enabled: false
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
graphql-hive (0.1.0)
|
5
|
+
graphql (~> 2.0.9)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
ast (2.4.2)
|
11
|
+
diff-lcs (1.5.0)
|
12
|
+
graphql (2.0.9)
|
13
|
+
parallel (1.22.1)
|
14
|
+
parser (3.1.2.0)
|
15
|
+
ast (~> 2.4.1)
|
16
|
+
rainbow (3.1.1)
|
17
|
+
rake (10.5.0)
|
18
|
+
regexp_parser (2.5.0)
|
19
|
+
rexml (3.2.5)
|
20
|
+
rspec (3.11.0)
|
21
|
+
rspec-core (~> 3.11.0)
|
22
|
+
rspec-expectations (~> 3.11.0)
|
23
|
+
rspec-mocks (~> 3.11.0)
|
24
|
+
rspec-core (3.11.0)
|
25
|
+
rspec-support (~> 3.11.0)
|
26
|
+
rspec-expectations (3.11.0)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.11.0)
|
29
|
+
rspec-mocks (3.11.1)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.11.0)
|
32
|
+
rspec-support (3.11.0)
|
33
|
+
rubocop (1.30.1)
|
34
|
+
parallel (~> 1.10)
|
35
|
+
parser (>= 3.1.0.0)
|
36
|
+
rainbow (>= 2.2.2, < 4.0)
|
37
|
+
regexp_parser (>= 1.8, < 3.0)
|
38
|
+
rexml (>= 3.2.5, < 4.0)
|
39
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
40
|
+
ruby-progressbar (~> 1.7)
|
41
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
42
|
+
rubocop-ast (1.18.0)
|
43
|
+
parser (>= 3.1.1.0)
|
44
|
+
ruby-progressbar (1.11.0)
|
45
|
+
unicode-display_width (2.1.0)
|
46
|
+
|
47
|
+
PLATFORMS
|
48
|
+
ruby
|
49
|
+
|
50
|
+
DEPENDENCIES
|
51
|
+
bundler (~> 1.17)
|
52
|
+
graphql-hive!
|
53
|
+
rake (~> 10.0)
|
54
|
+
rspec (~> 3.0)
|
55
|
+
rubocop (~> 1.30)
|
56
|
+
|
57
|
+
BUNDLED WITH
|
58
|
+
1.17.3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 Charly POLY
|
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 all
|
13
|
+
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 THE
|
21
|
+
SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 Charly POLY
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
# GraphQL Hive: `graphql-ruby` integration
|
2
|
+
|
3
|
+
<p align="center">
|
4
|
+
<img src="cover.png" width="500" alt="GraphQL Hive" />
|
5
|
+
</p>
|
6
|
+
|
7
|
+
|
8
|
+
|
9
|
+
[GraphQL Hive](https://graphql-hive.com/) provides all the tools to get visibility of your GraphQL architecture at all stages, from standalone APIs to composed schemas (Federation, Stitching):
|
10
|
+
- **Schema Registry** with custom breaking changes detection
|
11
|
+
- **Monitoring** of RPM, latency, error rate and more
|
12
|
+
- **Integrations** with your favorite tools (Slack, Github Actions and more)
|
13
|
+
|
14
|
+
|
15
|
+
<br/>
|
16
|
+
|
17
|
+
----
|
18
|
+
|
19
|
+
<br/>
|
20
|
+
|
21
|
+
|
22
|
+
# Getting started
|
23
|
+
|
24
|
+
|
25
|
+
## 0. Get your Hive token
|
26
|
+
|
27
|
+
If you are using Hive as a service, please refer to our documentation: https://docs.graphql-hive.com/features/tokens.
|
28
|
+
|
29
|
+
## 1. Install the `graphql-hive` gem
|
30
|
+
|
31
|
+
```
|
32
|
+
gem install graphql-hive
|
33
|
+
```
|
34
|
+
|
35
|
+
<br/>
|
36
|
+
|
37
|
+
## 2. Configure `GraphQL::Hive` in your Schema
|
38
|
+
|
39
|
+
Add `GraphQL::Hive` **at the end** of your schema definition:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
class Schema < GraphQL::Schema
|
43
|
+
query QueryType
|
44
|
+
|
45
|
+
use(
|
46
|
+
GraphQL::Hive,
|
47
|
+
{
|
48
|
+
token: '<YOUR_TOKEN>',
|
49
|
+
reporting: {
|
50
|
+
author: ENV['GITHUB_USER'],
|
51
|
+
commit: ENV['GITHUB_COMMIT']
|
52
|
+
},
|
53
|
+
}
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
```
|
58
|
+
|
59
|
+
The `reporting` configuration is required to push your GraphQL Schema to the Hive registry.
|
60
|
+
Doing so will help better detect breaking changes and more upcoming features.
|
61
|
+
If you only want to use the operations monitoring, replace the `monitoring` option with the following `report_schema: false`.
|
62
|
+
|
63
|
+
<br/>
|
64
|
+
|
65
|
+
|
66
|
+
**You are all set! 🚀**
|
67
|
+
|
68
|
+
When deploying or starting up your GraphQL API, `graphql-hive` will immediately:
|
69
|
+
- publish the schema to the Hive registry
|
70
|
+
- forward the operations metrics to Hive
|
71
|
+
|
72
|
+
|
73
|
+
<br/>
|
74
|
+
|
75
|
+
## 3. See how your GraphQL API is operating
|
76
|
+
|
77
|
+
You should now see operations information (RPM, error rate, queries performed) on your [GraphQL Hive dashboard](https://app.graphql-hive.com/):
|
78
|
+
|
79
|
+
<p align="center">
|
80
|
+
<img src="operations-dashboard.png" width="500" alt="GraphQL Hive" />
|
81
|
+
</p>
|
82
|
+
|
83
|
+
|
84
|
+
<br/>
|
85
|
+
|
86
|
+
|
87
|
+
## 4. Going further: use the Hive Github app
|
88
|
+
|
89
|
+
Stay on top of your GraphQL Schema changes by installing the Hive Github Application and enabling Slack notifications about breaking changes:
|
90
|
+
|
91
|
+
https://docs.graphql-hive.com/features/integrations#github
|
92
|
+
|
93
|
+
<br/>
|
94
|
+
|
95
|
+
----
|
96
|
+
|
97
|
+
<br/>
|
98
|
+
|
99
|
+
|
100
|
+
# Configuration
|
101
|
+
|
102
|
+
You will find below the complete list of options of `GraphQL::Hive`:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
class MySchema < GraphQL::Schema
|
106
|
+
use(
|
107
|
+
GraphQL::Hive,
|
108
|
+
{
|
109
|
+
token: 'YOUR-TOKEN',
|
110
|
+
collect_usage: true, # optional
|
111
|
+
report_schema: true, # optional
|
112
|
+
enabled: true, # Enable/Disable Hive Client (optional)
|
113
|
+
debug: false, # verbose logs
|
114
|
+
logger: MyLogger.new, # optional
|
115
|
+
endpoint: 'app.graphql-hive.com', # optional
|
116
|
+
port: 80, # optional
|
117
|
+
buffer_size: 50, # forward the operations data to Hive every 50 requests
|
118
|
+
reporting: { # mandatory if `report_schema: true`
|
119
|
+
# mandatory member of `reporting`
|
120
|
+
author: 'Author of the latest change',
|
121
|
+
# mandatory member of `reporting`
|
122
|
+
commit: 'git sha or any identifier',
|
123
|
+
service_name: '', # optional
|
124
|
+
service_url: '', # optional
|
125
|
+
},
|
126
|
+
# you can pass an optional proc that will help identify the client (ex: Apollo web app) that performed the query
|
127
|
+
client_info: Proc.new { |context| { name: context.client_name, version: context.client_version } }
|
128
|
+
}
|
129
|
+
)
|
130
|
+
|
131
|
+
# ...
|
132
|
+
|
133
|
+
end
|
134
|
+
```
|
135
|
+
|
136
|
+
<br/>
|
137
|
+
|
138
|
+
**A note on `buffer_size` and performances**
|
139
|
+
|
140
|
+
The `graphql-hive` usage reporter, responsible for sending the operations data to Hive, is running in a separate `Thread` to avoid any major impact on your GraphQL API performances.
|
141
|
+
|
142
|
+
If your GraphQL API has a high RPM, we encourage you to increase the `buffer_size` value.
|
143
|
+
|
144
|
+
However, please note that a higher `buffer_size` value will introduce some peak of increase of memory comsumption.
|
data/Rakefile
ADDED
data/cover.png
ADDED
Binary file
|
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../..
|
3
|
+
specs:
|
4
|
+
graphql-hive (0.1.0)
|
5
|
+
graphql (~> 2.0.9)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
graphql (2.0.9)
|
11
|
+
multi_json (1.15.0)
|
12
|
+
mustermann (1.1.1)
|
13
|
+
ruby2_keywords (~> 0.0.1)
|
14
|
+
nio4r (2.5.8)
|
15
|
+
puma (5.6.4)
|
16
|
+
nio4r (~> 2.0)
|
17
|
+
rack (2.2.3.1)
|
18
|
+
rack-contrib (2.3.0)
|
19
|
+
rack (~> 2.0)
|
20
|
+
rack-protection (2.2.0)
|
21
|
+
rack
|
22
|
+
ruby2_keywords (0.0.5)
|
23
|
+
sinatra (2.2.0)
|
24
|
+
mustermann (~> 1.0)
|
25
|
+
rack (~> 2.2)
|
26
|
+
rack-protection (= 2.2.0)
|
27
|
+
tilt (~> 2.0)
|
28
|
+
sinatra-contrib (2.2.0)
|
29
|
+
multi_json
|
30
|
+
mustermann (~> 1.0)
|
31
|
+
rack-protection (= 2.2.0)
|
32
|
+
sinatra (= 2.2.0)
|
33
|
+
tilt (~> 2.0)
|
34
|
+
tilt (2.0.10)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
graphql
|
41
|
+
graphql-hive!
|
42
|
+
puma
|
43
|
+
rack-contrib
|
44
|
+
sinatra
|
45
|
+
sinatra-contrib
|
46
|
+
|
47
|
+
BUNDLED WITH
|
48
|
+
1.17.3
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sinatra/json'
|
3
|
+
require 'rack/contrib'
|
4
|
+
|
5
|
+
require_relative 'schema'
|
6
|
+
|
7
|
+
# Test query:
|
8
|
+
#
|
9
|
+
# query GetPost($input: [PostInput!]!) {
|
10
|
+
# post(input: $input, test: TEST1) {
|
11
|
+
# title
|
12
|
+
# myId: id
|
13
|
+
# }
|
14
|
+
# }
|
15
|
+
|
16
|
+
class DemoApp < Sinatra::Base
|
17
|
+
use Rack::JSONBodyParser
|
18
|
+
|
19
|
+
post '/graphql' do
|
20
|
+
result = Schema.execute(
|
21
|
+
params['query'],
|
22
|
+
variables: params[:variables],
|
23
|
+
operation_name: params[:operationName],
|
24
|
+
context: {
|
25
|
+
client_name: 'GraphQL Client',
|
26
|
+
client_version: '1.0'
|
27
|
+
}
|
28
|
+
)
|
29
|
+
json result
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'graphql'
|
2
|
+
require 'graphql-hive'
|
3
|
+
|
4
|
+
module Types
|
5
|
+
class PostType < GraphQL::Schema::Object
|
6
|
+
description 'A blog post'
|
7
|
+
field :id, ID, null: false
|
8
|
+
field :title, String, null: false
|
9
|
+
# fields should be queried in camel-case (this will be `truncatedPreview`)
|
10
|
+
field :truncated_preview, String, null: false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Types::PostInput < GraphQL::Schema::InputObject
|
15
|
+
description 'Query Post arguments'
|
16
|
+
argument :id, ID, required: true
|
17
|
+
end
|
18
|
+
|
19
|
+
class Types::TestEnum < GraphQL::Schema::Enum
|
20
|
+
value 'TEST1'
|
21
|
+
value 'TEST2'
|
22
|
+
value 'TEST3'
|
23
|
+
end
|
24
|
+
|
25
|
+
class QueryType < GraphQL::Schema::Object
|
26
|
+
description 'The query root of this schema'
|
27
|
+
|
28
|
+
# First describe the field signature:
|
29
|
+
field :post, Types::PostType, 'Find a post by ID' do
|
30
|
+
argument :input, [Types::PostInput]
|
31
|
+
argument :test, Types::TestEnum
|
32
|
+
end
|
33
|
+
|
34
|
+
# Then provide an implementation:
|
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' }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Schema < GraphQL::Schema
|
42
|
+
query QueryType
|
43
|
+
|
44
|
+
use(GraphQL::Hive, { buffer_size: 2, token: 'c3715c1afab97070c5c5b9bce8d02c7b', debug: true, reporting: { author: 'Charly Poly', commit: '109bb1e748bae21bdfe663c0ffc7e830' }, client_info: proc { |context|
|
45
|
+
{ name: context[:client_name], version: context[:client_version] }
|
46
|
+
} })
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'graphql-hive/version'
|
6
|
+
|
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']
|
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'
|
17
|
+
|
18
|
+
spec.metadata = { 'rubygems_mfa_required' => 'true' }
|
19
|
+
|
20
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
|
21
|
+
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
|
27
|
+
spec.add_dependency 'graphql', '~> 2.0.9'
|
28
|
+
|
29
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
30
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
32
|
+
spec.add_development_dependency 'rubocop', '~> 1.30'
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Hive < GraphQL::Tracing::PlatformTracing
|
5
|
+
# Fetch all users fields, input objects and enums
|
6
|
+
class Analyzer < GraphQL::Analysis::AST::Analyzer
|
7
|
+
def initialize(query_or_multiplex)
|
8
|
+
puts query_or_multiplex.inspect
|
9
|
+
super
|
10
|
+
@used_fields = Set.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_leave_field(node, _parent, visitor)
|
14
|
+
@used_fields.add(visitor.parent_type_definition.graphql_name)
|
15
|
+
@used_fields.add([visitor.parent_type_definition.graphql_name, node.name].join('.'))
|
16
|
+
end
|
17
|
+
|
18
|
+
def on_leave_argument(node, parent, visitor)
|
19
|
+
@used_fields.add([visitor.parent_type_definition.graphql_name, parent.name, node.name].join('.'))
|
20
|
+
|
21
|
+
arg_type = visitor.argument_definition.type.unwrap
|
22
|
+
arg_type_kind = visitor.argument_definition.type.unwrap.kind
|
23
|
+
if arg_type_kind.input_object?
|
24
|
+
@used_fields.add(arg_type.graphql_name)
|
25
|
+
arg_type.arguments.each do |arg|
|
26
|
+
@used_fields.add([arg_type.graphql_name, arg[0]].join('.'))
|
27
|
+
end
|
28
|
+
elsif arg_type_kind.enum?
|
29
|
+
@used_fields.add([arg_type.graphql_name, node.value.name].join('.'))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :used_fields
|
34
|
+
|
35
|
+
def result
|
36
|
+
@used_fields
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module GraphQL
|
7
|
+
class Hive < GraphQL::Tracing::PlatformTracing
|
8
|
+
# API client
|
9
|
+
class Client
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def send(path, body, _log_type)
|
15
|
+
uri =
|
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',
|
20
|
+
path: path
|
21
|
+
)
|
22
|
+
|
23
|
+
http = ::Net::HTTP.new(uri.host, uri.port)
|
24
|
+
http.use_ssl = true
|
25
|
+
http.read_timeout = 2
|
26
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
27
|
+
request['content-type'] = 'application/json'
|
28
|
+
request['x-api-token'] = @options[:token]
|
29
|
+
request['User-Agent'] = "Hive@#{Graphql::Hive::VERSION}"
|
30
|
+
request['graphql-client-name'] = 'Hive Ruby Client'
|
31
|
+
request['graphql-client-version'] = Graphql::Hive::VERSION
|
32
|
+
request.body = JSON.generate(body)
|
33
|
+
response = http.request(request)
|
34
|
+
|
35
|
+
@options[:logger].debug(response.inspect)
|
36
|
+
@options[:logger].debug(response.body.inspect)
|
37
|
+
rescue StandardError => e
|
38
|
+
@options[:logger].fatal("Failed to send data: #{e}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Hive < GraphQL::Tracing::PlatformTracing
|
5
|
+
# - removes literals
|
6
|
+
# - removes aliases
|
7
|
+
# - sort nodes and directives (files, arguments, variables)
|
8
|
+
class Printer < GraphQL::Language::Printer
|
9
|
+
def print_node(node, indent: '')
|
10
|
+
case node
|
11
|
+
when Float, Integer
|
12
|
+
'0'
|
13
|
+
when String
|
14
|
+
''
|
15
|
+
else
|
16
|
+
super(node, indent: indent)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# rubocop:disable Style/RedundantInterpolation
|
21
|
+
def print_field(field, indent: '')
|
22
|
+
out = "#{indent}".dup
|
23
|
+
out << "#{field.name}"
|
24
|
+
out << "(#{field.arguments.sort_by(&:name).map { |a| print_argument(a) }.join(', ')})" if field.arguments.any?
|
25
|
+
out << print_directives(field.directives)
|
26
|
+
out << print_selections(field.selections, indent: indent)
|
27
|
+
out
|
28
|
+
end
|
29
|
+
# rubocop:enable Style/RedundantInterpolation
|
30
|
+
|
31
|
+
def print_directives(directives)
|
32
|
+
super(directives.sort_by(&:name))
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_selections(selections, indent: '')
|
36
|
+
super(selections.sort_by(&:name), indent: indent)
|
37
|
+
end
|
38
|
+
|
39
|
+
def print_directive(directive)
|
40
|
+
out = "@#{directive.name}".dup
|
41
|
+
|
42
|
+
if directive.arguments.any?
|
43
|
+
out << "(#{directive.arguments.sort_by(&:name).map { |a| print_argument(a) }.join(', ')})"
|
44
|
+
end
|
45
|
+
|
46
|
+
out
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_operation_definition(operation_definition, indent: '')
|
50
|
+
out = "#{indent}#{operation_definition.operation_type}".dup
|
51
|
+
out << " #{operation_definition.name}" if operation_definition.name
|
52
|
+
|
53
|
+
# rubocop:disable Layout/LineLength
|
54
|
+
if operation_definition.variables.any?
|
55
|
+
out << "(#{operation_definition.variables.sort_by(&:name).map { |v| print_variable_definition(v) }.join(', ')})"
|
56
|
+
end
|
57
|
+
# rubocop:enable Layout/LineLength
|
58
|
+
|
59
|
+
out << print_directives(operation_definition.directives)
|
60
|
+
out << print_selections(operation_definition.selections, indent: indent)
|
61
|
+
out
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require 'graphql-hive/analyzer'
|
5
|
+
require 'graphql-hive/printer'
|
6
|
+
|
7
|
+
module GraphQL
|
8
|
+
class Hive < GraphQL::Tracing::PlatformTracing
|
9
|
+
# Report usage to Hive API without impacting application performances
|
10
|
+
class UsageReporter
|
11
|
+
@@instance = nil
|
12
|
+
|
13
|
+
@queue = nil
|
14
|
+
@thread = nil
|
15
|
+
@operations_buffer = nil
|
16
|
+
@client = nil
|
17
|
+
@logger = nil
|
18
|
+
|
19
|
+
def self.instance
|
20
|
+
@@instance
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(options, client)
|
24
|
+
@@instance = self
|
25
|
+
|
26
|
+
@buffer_size = options[:buffer_size]
|
27
|
+
@logger = options[:logger]
|
28
|
+
@client = client
|
29
|
+
@operations_buffer = []
|
30
|
+
|
31
|
+
@queue = Queue.new
|
32
|
+
|
33
|
+
@thread = Thread.new do
|
34
|
+
while !@queue.empty? || !@queue.closed?
|
35
|
+
operations = @queue.pop(false)
|
36
|
+
process_operations operations
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_operation(operation)
|
42
|
+
@logger.debug("add operation to buffer: #{operation}")
|
43
|
+
|
44
|
+
@operations_buffer << operation
|
45
|
+
|
46
|
+
return unless @operations_buffer.size >= @buffer_size
|
47
|
+
|
48
|
+
@logger.debug('buffer is full, sending!')
|
49
|
+
@queue.push @operations_buffer
|
50
|
+
@operations_buffer = []
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_exit
|
54
|
+
@queue.push @operations_buffer unless @operations_buffer.empty?
|
55
|
+
@queue.close
|
56
|
+
@thread.join
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def process_operations(operations)
|
62
|
+
report = {
|
63
|
+
size: 0,
|
64
|
+
map: {},
|
65
|
+
operations: []
|
66
|
+
}
|
67
|
+
|
68
|
+
operations.each do |operation|
|
69
|
+
add_operation_to_report(report, operation)
|
70
|
+
end
|
71
|
+
|
72
|
+
@logger.debug("sending report: #{report}")
|
73
|
+
|
74
|
+
@client.send('/usage', report, :usage)
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_operation_to_report(report, operation)
|
78
|
+
timestamp, queries, results, duration = operation
|
79
|
+
|
80
|
+
errors = errors_from_results(results)
|
81
|
+
|
82
|
+
operation_name = queries.map(&:operations).map(&:keys).flatten.compact.join(', ')
|
83
|
+
operation = ''
|
84
|
+
fields = Set.new
|
85
|
+
|
86
|
+
queries.each do |query|
|
87
|
+
analyzer = GraphQL::Hive::Analyzer.new(query)
|
88
|
+
visitor = GraphQL::Analysis::AST::Visitor.new(
|
89
|
+
query: query,
|
90
|
+
analyzers: [analyzer]
|
91
|
+
)
|
92
|
+
|
93
|
+
visitor.visit
|
94
|
+
|
95
|
+
fields.merge(analyzer.result)
|
96
|
+
|
97
|
+
operation += "\n" unless operation.empty?
|
98
|
+
operation += GraphQL::Hive::Printer.new.print(visitor.result)
|
99
|
+
end
|
100
|
+
|
101
|
+
md5 = Digest::MD5.new
|
102
|
+
md5.update operation
|
103
|
+
operation_map_key = md5.hexdigest
|
104
|
+
|
105
|
+
operation_record = {
|
106
|
+
operationMapKey: operation_map_key,
|
107
|
+
timestamp: timestamp.to_i,
|
108
|
+
execution: {
|
109
|
+
ok: errors[:errorsTotal].zero?,
|
110
|
+
duration: duration,
|
111
|
+
errorsTotal: errors[:errorsTotal],
|
112
|
+
errors: errors[:errors]
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
# context = results[0].query.context
|
117
|
+
# TBD
|
118
|
+
# operation_record[:metadata] = { client: @options[:client_info].call(context) } if @options[:client_info]
|
119
|
+
|
120
|
+
report[:map][operation_map_key] = {
|
121
|
+
fields: fields.to_a,
|
122
|
+
operationName: operation_name,
|
123
|
+
operation: operation
|
124
|
+
}
|
125
|
+
report[:operations] << operation_record
|
126
|
+
report[:size] += 1
|
127
|
+
end
|
128
|
+
|
129
|
+
def errors_from_results(results)
|
130
|
+
acc = { errorsTotal: 0, errors: [] }
|
131
|
+
results.each do |result|
|
132
|
+
errors = result.to_h.fetch('errors', [])
|
133
|
+
errors.each do |error|
|
134
|
+
acc[:errorsTotal] += 1
|
135
|
+
acc[:errors] << { message: error['message'], path: error['path'].join('.') }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
acc
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
data/lib/graphql-hive.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
require 'graphql-hive/version'
|
6
|
+
require 'graphql-hive/usage_reporter'
|
7
|
+
require 'graphql-hive/client'
|
8
|
+
|
9
|
+
# class MySchema < GraphQL::Schema
|
10
|
+
# use(
|
11
|
+
# GraphQL::Hive,
|
12
|
+
# {
|
13
|
+
# token: 'YOUR-TOKEN',
|
14
|
+
# collect_usage: true,
|
15
|
+
# report_schema: true,
|
16
|
+
# enabled: true, // Enable/Disable Hive Client
|
17
|
+
# debug: true, // Debugging mode
|
18
|
+
# logger: MyLogger.new,
|
19
|
+
# endpoint: 'app.graphql-hive.com',
|
20
|
+
# port: 80,
|
21
|
+
# reporting: {
|
22
|
+
# author: 'Author of the latest change',
|
23
|
+
# commit: 'git sha or any identifier',
|
24
|
+
# service_name: '',
|
25
|
+
# service_url: '',
|
26
|
+
# },
|
27
|
+
# client_info: Proc.new { |context| { name: context.client_name, version: context.client_version } }
|
28
|
+
# }
|
29
|
+
# )
|
30
|
+
#
|
31
|
+
# # ...
|
32
|
+
#
|
33
|
+
# end
|
34
|
+
|
35
|
+
module GraphQL
|
36
|
+
# GraphQL Hive usage collector and schema reporter
|
37
|
+
class Hive < GraphQL::Tracing::PlatformTracing
|
38
|
+
@@schema = nil
|
39
|
+
@@instance = nil
|
40
|
+
|
41
|
+
@usage_reporter = nil
|
42
|
+
@client = nil
|
43
|
+
|
44
|
+
REPORT_SCHEMA_MUTATION = <<~MUTATION
|
45
|
+
mutation schemaPublish($input: SchemaPublishInput!) {
|
46
|
+
schemaPublish(input: $input) {
|
47
|
+
__typename
|
48
|
+
}
|
49
|
+
}
|
50
|
+
MUTATION
|
51
|
+
|
52
|
+
DEFAULT_OPTIONS = {
|
53
|
+
enabled: true,
|
54
|
+
collect_usage: true,
|
55
|
+
read_operations: true,
|
56
|
+
report_schema: true,
|
57
|
+
buffer_size: 50,
|
58
|
+
logger: nil
|
59
|
+
}.freeze
|
60
|
+
|
61
|
+
self.platform_keys = {
|
62
|
+
'lex' => 'lex',
|
63
|
+
'parse' => 'parse',
|
64
|
+
'validate' => 'validate',
|
65
|
+
'analyze_query' => 'analyze_query',
|
66
|
+
'analyze_multiplex' => 'analyze_multiplex',
|
67
|
+
'execute_multiplex' => 'execute_multiplex',
|
68
|
+
'execute_query' => 'execute_query',
|
69
|
+
'execute_query_lazy' => 'execute_query_lazy'
|
70
|
+
}
|
71
|
+
|
72
|
+
def initialize(options = {})
|
73
|
+
opts = DEFAULT_OPTIONS.merge(options)
|
74
|
+
validate_options!(opts)
|
75
|
+
super(opts)
|
76
|
+
|
77
|
+
@@instance = self
|
78
|
+
|
79
|
+
@client = GraphQL::Hive::Client.new(opts)
|
80
|
+
@usage_reporter = GraphQL::Hive::UsageReporter.new(opts, @client)
|
81
|
+
|
82
|
+
# buffer
|
83
|
+
@report = {
|
84
|
+
size: 0,
|
85
|
+
map: {},
|
86
|
+
operations: []
|
87
|
+
}
|
88
|
+
|
89
|
+
send_report_schema(@@schema) if @@schema && opts[:report_schema] && @options[:enabled]
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.instance
|
93
|
+
@@instance
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.use(schema, **kwargs)
|
97
|
+
@@schema = schema
|
98
|
+
super
|
99
|
+
end
|
100
|
+
|
101
|
+
# called on trace events
|
102
|
+
def platform_trace(platform_key, _key, data)
|
103
|
+
return yield unless @options[:enabled] && @options[:collect_usage]
|
104
|
+
|
105
|
+
if platform_key == 'execute_multiplex'
|
106
|
+
if data[:multiplex]
|
107
|
+
queries = data[:multiplex].queries
|
108
|
+
timestamp = (Time.now.utc.to_f * 1000).to_i
|
109
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
110
|
+
results = yield
|
111
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
112
|
+
elapsed = ending - starting
|
113
|
+
duration = (elapsed.to_f * (10**9)).to_i
|
114
|
+
|
115
|
+
report_usage(timestamp, queries, results, duration) unless queries.empty?
|
116
|
+
|
117
|
+
results
|
118
|
+
else
|
119
|
+
yield
|
120
|
+
end
|
121
|
+
else
|
122
|
+
yield
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# compat
|
127
|
+
def platform_authorized_key(type)
|
128
|
+
"#{type.graphql_name}.authorized.graphql"
|
129
|
+
end
|
130
|
+
|
131
|
+
# compat
|
132
|
+
def platform_resolve_type_key(type)
|
133
|
+
"#{type.graphql_name}.resolve_type.graphql"
|
134
|
+
end
|
135
|
+
|
136
|
+
# compat
|
137
|
+
def platform_field_key(type, field)
|
138
|
+
"graphql.#{type.name}.#{field.name}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def on_exit
|
142
|
+
@usage_reporter.on_exit
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
|
147
|
+
def validate_options!(options)
|
148
|
+
if options[:logger].nil?
|
149
|
+
options[:logger] = Logger.new($stdout)
|
150
|
+
original_formatter = Logger::Formatter.new
|
151
|
+
options[:logger].formatter = proc { |severity, datetime, progname, msg|
|
152
|
+
original_formatter.call(severity, datetime, progname, "[hive] #{msg.dump}")
|
153
|
+
}
|
154
|
+
end
|
155
|
+
if !options.include?(:token) && (!options.include?(:enabled) || options.enabled)
|
156
|
+
options[:logger].warn('`token` options is missing')
|
157
|
+
options[:enabled] = false
|
158
|
+
false
|
159
|
+
elsif options[:report_schema] &&
|
160
|
+
(
|
161
|
+
!options.include?(:reporting) ||
|
162
|
+
(
|
163
|
+
options.include?(:reporting) && (
|
164
|
+
!options[:reporting].include?(:author) || !options[:reporting].include?(:commit)
|
165
|
+
)
|
166
|
+
)
|
167
|
+
)
|
168
|
+
|
169
|
+
options[:logger].warn('`reporting.author` and `reporting.commit` options are required')
|
170
|
+
false
|
171
|
+
end
|
172
|
+
true
|
173
|
+
end
|
174
|
+
|
175
|
+
def report_usage(timestamp, queries, results, duration)
|
176
|
+
@usage_reporter.add_operation([timestamp, queries, results, duration])
|
177
|
+
end
|
178
|
+
|
179
|
+
def send_report_schema(schema)
|
180
|
+
sdl = GraphQL::Schema::Printer.new(schema).print_schema
|
181
|
+
|
182
|
+
body = {
|
183
|
+
query: REPORT_SCHEMA_MUTATION,
|
184
|
+
operationName: 'schemaPublish',
|
185
|
+
variables: {
|
186
|
+
input: {
|
187
|
+
sdl: sdl,
|
188
|
+
author: @options[:reporting][:author],
|
189
|
+
commit: @options[:reporting][:commit],
|
190
|
+
service: @options[:reporting][:service_name],
|
191
|
+
url: @options[:reporting][:service_url],
|
192
|
+
force: true
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
puts(JSON.generate(body))
|
198
|
+
|
199
|
+
@client.send('/registry', body, :'report-schema')
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
at_exit do
|
205
|
+
GraphQL::Hive.instance&.on_exit
|
206
|
+
end
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: graphql-hive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Charly POLY
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: graphql
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.9
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.9
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.17'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.17'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.30'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.30'
|
83
|
+
description: '"Monitor operations, inspect your queries and publish your GraphQL schema
|
84
|
+
with GraphQL Hive"'
|
85
|
+
email:
|
86
|
+
- cpoly55@gmail.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".github/workflows/ci.yml"
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- ".rubocop.yml"
|
95
|
+
- Gemfile
|
96
|
+
- Gemfile.lock
|
97
|
+
- LICENSE
|
98
|
+
- LICENSE.txt
|
99
|
+
- README.md
|
100
|
+
- Rakefile
|
101
|
+
- cover.png
|
102
|
+
- examples/simple-api/Gemfile
|
103
|
+
- examples/simple-api/Gemfile.lock
|
104
|
+
- examples/simple-api/app.rb
|
105
|
+
- examples/simple-api/config.ru
|
106
|
+
- examples/simple-api/schema.rb
|
107
|
+
- graphql-hive.gemspec
|
108
|
+
- lib/graphql-hive.rb
|
109
|
+
- lib/graphql-hive/analyzer.rb
|
110
|
+
- lib/graphql-hive/client.rb
|
111
|
+
- lib/graphql-hive/printer.rb
|
112
|
+
- lib/graphql-hive/usage_reporter.rb
|
113
|
+
- lib/graphql-hive/version.rb
|
114
|
+
- operations-dashboard.png
|
115
|
+
homepage: https://docs.graphql-hive.com/specs/integrations
|
116
|
+
licenses:
|
117
|
+
- MIT
|
118
|
+
metadata:
|
119
|
+
rubygems_mfa_required: 'true'
|
120
|
+
post_install_message:
|
121
|
+
rdoc_options: []
|
122
|
+
require_paths:
|
123
|
+
- lib
|
124
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 2.6.0
|
129
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
requirements: []
|
135
|
+
rubygems_version: 3.0.6
|
136
|
+
signing_key:
|
137
|
+
specification_version: 4
|
138
|
+
summary: '"GraphQL Hive integration for `graphql-ruby`"'
|
139
|
+
test_files: []
|