graphql-hive 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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: []
|