graphql-metrics 2.0.1 → 3.0.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 +5 -5
- data/.github/workflows/ruby.yml +20 -0
- data/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile.lock +23 -20
- data/README.md +179 -107
- data/Rakefile +6 -0
- data/bin/console +4 -6
- data/graphql_metrics.gemspec +7 -7
- data/lib/graphql/metrics.rb +80 -0
- data/lib/graphql/metrics/analyzer.rb +129 -0
- data/lib/graphql/metrics/instrumentation.rb +54 -0
- data/lib/graphql/metrics/tracer.rb +109 -0
- data/lib/graphql/metrics/version.rb +7 -0
- metadata +55 -24
- data/.travis.yml +0 -5
- data/lib/graphql_metrics.rb +0 -6
- data/lib/graphql_metrics/extractor.rb +0 -277
- data/lib/graphql_metrics/instrumentation.rb +0 -107
- data/lib/graphql_metrics/timed_batch_executor.rb +0 -80
- data/lib/graphql_metrics/version.rb +0 -5
metadata
CHANGED
@@ -1,29 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-metrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christopher Butcher
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: graphql
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.9.15
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.9.15
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
16
44
|
requirements:
|
17
45
|
- - "~>"
|
18
46
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
47
|
+
version: 2.0.2
|
20
48
|
type: :development
|
21
49
|
prerelease: false
|
22
50
|
version_requirements: !ruby/object:Gem::Requirement
|
23
51
|
requirements:
|
24
52
|
- - "~>"
|
25
53
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
54
|
+
version: 2.0.2
|
27
55
|
- !ruby/object:Gem::Dependency
|
28
56
|
name: rake
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,35 +95,35 @@ dependencies:
|
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
98
|
+
name: activesupport
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - "~>"
|
74
102
|
- !ruby/object:Gem::Version
|
75
|
-
version: 1.
|
103
|
+
version: 5.1.5
|
76
104
|
type: :development
|
77
105
|
prerelease: false
|
78
106
|
version_requirements: !ruby/object:Gem::Requirement
|
79
107
|
requirements:
|
80
108
|
- - "~>"
|
81
109
|
- !ruby/object:Gem::Version
|
82
|
-
version: 1.
|
110
|
+
version: 5.1.5
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
112
|
+
name: pry
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
86
114
|
requirements:
|
87
|
-
- - "
|
115
|
+
- - ">="
|
88
116
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
117
|
+
version: '0'
|
90
118
|
type: :development
|
91
119
|
prerelease: false
|
92
120
|
version_requirements: !ruby/object:Gem::Requirement
|
93
121
|
requirements:
|
94
|
-
- - "
|
122
|
+
- - ">="
|
95
123
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
124
|
+
version: '0'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
|
-
name: pry
|
126
|
+
name: pry-byebug
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
100
128
|
requirements:
|
101
129
|
- - ">="
|
@@ -109,7 +137,7 @@ dependencies:
|
|
109
137
|
- !ruby/object:Gem::Version
|
110
138
|
version: '0'
|
111
139
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
140
|
+
name: mocha
|
113
141
|
requirement: !ruby/object:Gem::Requirement
|
114
142
|
requirements:
|
115
143
|
- - ">="
|
@@ -123,7 +151,7 @@ dependencies:
|
|
123
151
|
- !ruby/object:Gem::Version
|
124
152
|
version: '0'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
154
|
+
name: diffy
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
128
156
|
requirements:
|
129
157
|
- - ">="
|
@@ -137,7 +165,7 @@ dependencies:
|
|
137
165
|
- !ruby/object:Gem::Version
|
138
166
|
version: '0'
|
139
167
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
168
|
+
name: hashdiff
|
141
169
|
requirement: !ruby/object:Gem::Requirement
|
142
170
|
requirements:
|
143
171
|
- - ">="
|
@@ -187,8 +215,12 @@ executables: []
|
|
187
215
|
extensions: []
|
188
216
|
extra_rdoc_files: []
|
189
217
|
files:
|
218
|
+
- ".github/workflows/ruby.yml"
|
190
219
|
- ".gitignore"
|
191
|
-
- ".
|
220
|
+
- ".rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml"
|
221
|
+
- ".rubocop.yml"
|
222
|
+
- ".ruby-version"
|
223
|
+
- CHANGELOG.md
|
192
224
|
- CODE_OF_CONDUCT.md
|
193
225
|
- Gemfile
|
194
226
|
- Gemfile.lock
|
@@ -198,11 +230,11 @@ files:
|
|
198
230
|
- bin/console
|
199
231
|
- bin/setup
|
200
232
|
- graphql_metrics.gemspec
|
201
|
-
- lib/
|
202
|
-
- lib/
|
203
|
-
- lib/
|
204
|
-
- lib/
|
205
|
-
- lib/
|
233
|
+
- lib/graphql/metrics.rb
|
234
|
+
- lib/graphql/metrics/analyzer.rb
|
235
|
+
- lib/graphql/metrics/instrumentation.rb
|
236
|
+
- lib/graphql/metrics/tracer.rb
|
237
|
+
- lib/graphql/metrics/version.rb
|
206
238
|
homepage: https://github.com/Shopify/graphql-metrics
|
207
239
|
licenses:
|
208
240
|
- MIT
|
@@ -222,8 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
254
|
- !ruby/object:Gem::Version
|
223
255
|
version: '0'
|
224
256
|
requirements: []
|
225
|
-
|
226
|
-
rubygems_version: 2.5.2.3
|
257
|
+
rubygems_version: 3.0.3
|
227
258
|
signing_key:
|
228
259
|
specification_version: 4
|
229
260
|
summary: GraphQL Metrics Extractor
|
data/.travis.yml
DELETED
data/lib/graphql_metrics.rb
DELETED
@@ -1,277 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQLMetrics
|
4
|
-
class Extractor
|
5
|
-
class DummyInstrumentor
|
6
|
-
def after_query_start_and_end_time
|
7
|
-
[nil, nil]
|
8
|
-
end
|
9
|
-
|
10
|
-
def after_query_resolver_times(_ast_node)
|
11
|
-
[]
|
12
|
-
end
|
13
|
-
|
14
|
-
def ctx_namespace
|
15
|
-
{}
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
EXPLICIT_NULL = 'EXPLICIT_NULL'
|
20
|
-
IMPLICIT_NULL = 'IMPLICIT_NULL'
|
21
|
-
NON_NULL = 'NON_NULL'
|
22
|
-
|
23
|
-
attr_reader :query
|
24
|
-
|
25
|
-
def initialize(instrumentor = DummyInstrumentor.new)
|
26
|
-
@instrumentor = instrumentor
|
27
|
-
end
|
28
|
-
|
29
|
-
def instrumentor
|
30
|
-
@instrumentor ||= DummyInstrumentor.new
|
31
|
-
end
|
32
|
-
|
33
|
-
def extract!(query)
|
34
|
-
@query = query
|
35
|
-
|
36
|
-
return unless query.valid?
|
37
|
-
return unless query.irep_selection
|
38
|
-
|
39
|
-
extract_query
|
40
|
-
|
41
|
-
used_variables = extract_used_variables
|
42
|
-
|
43
|
-
query.operations.each_value do |operation|
|
44
|
-
extract_variables(operation, used_variables)
|
45
|
-
end
|
46
|
-
|
47
|
-
extract_node(query.irep_selection)
|
48
|
-
extract_batch_loaders
|
49
|
-
end
|
50
|
-
|
51
|
-
def extractor_defines_any_visitors?
|
52
|
-
[self, instrumentor].any? do |extractor_definer|
|
53
|
-
extractor_definer.respond_to?(:query_extracted) ||
|
54
|
-
extractor_definer.respond_to?(:field_extracted) ||
|
55
|
-
extractor_definer.respond_to?(:argument_extracted) ||
|
56
|
-
extractor_definer.respond_to?(:variable_extracted) ||
|
57
|
-
extractor_definer.respond_to?(:batch_loaded_field_extracted) ||
|
58
|
-
extractor_definer.respond_to?(:before_query_extracted)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def handle_extraction_exception(ex)
|
63
|
-
raise ex
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def extract_batch_loaders
|
69
|
-
return unless batch_loaded_field_extracted_method = extraction_method(:batch_loaded_field_extracted)
|
70
|
-
|
71
|
-
TimedBatchExecutor.timings.each do |key, resolve_meta|
|
72
|
-
key, identifiers = TimedBatchExecutor.serialize_loader_key(key)
|
73
|
-
|
74
|
-
batch_loaded_field_extracted_method.call(
|
75
|
-
{
|
76
|
-
key: key,
|
77
|
-
identifiers: identifiers,
|
78
|
-
times: resolve_meta[:times],
|
79
|
-
perform_queue_sizes: resolve_meta[:perform_queue_sizes],
|
80
|
-
},
|
81
|
-
{
|
82
|
-
query: query,
|
83
|
-
}
|
84
|
-
)
|
85
|
-
end
|
86
|
-
rescue StandardError => ex
|
87
|
-
handle_extraction_exception(ex)
|
88
|
-
ensure
|
89
|
-
TimedBatchExecutor.clear_timings
|
90
|
-
end
|
91
|
-
|
92
|
-
def extract_query
|
93
|
-
return unless query_extracted_method = extraction_method(:query_extracted)
|
94
|
-
|
95
|
-
start_time, end_time = instrumentor.after_query_start_and_end_time
|
96
|
-
duration = start_time && end_time ? end_time - start_time : nil
|
97
|
-
|
98
|
-
query_extracted_method.call(
|
99
|
-
{
|
100
|
-
query_string: query.document.to_query_string,
|
101
|
-
operation_type: query.selected_operation.operation_type,
|
102
|
-
operation_name: query.selected_operation_name,
|
103
|
-
duration: duration
|
104
|
-
},
|
105
|
-
{
|
106
|
-
query: query,
|
107
|
-
start_time: start_time,
|
108
|
-
end_time: end_time
|
109
|
-
}
|
110
|
-
)
|
111
|
-
rescue StandardError => ex
|
112
|
-
handle_extraction_exception(ex)
|
113
|
-
end
|
114
|
-
|
115
|
-
def extract_field(irep_node)
|
116
|
-
return unless field_extracted_method = extraction_method(:field_extracted)
|
117
|
-
return unless irep_node.definition
|
118
|
-
|
119
|
-
resolver_times = instrumentor.after_query_resolver_times(irep_node.ast_node)
|
120
|
-
|
121
|
-
field_extracted_method.call(
|
122
|
-
{
|
123
|
-
type_name: irep_node.owner_type.name,
|
124
|
-
field_name: irep_node.definition.name,
|
125
|
-
deprecated: irep_node.definition.deprecation_reason.present?,
|
126
|
-
resolver_times: resolver_times || [],
|
127
|
-
},
|
128
|
-
{
|
129
|
-
irep_node: irep_node,
|
130
|
-
query: query,
|
131
|
-
ctx_namespace: instrumentor.ctx_namespace
|
132
|
-
}
|
133
|
-
)
|
134
|
-
|
135
|
-
rescue StandardError => ex
|
136
|
-
handle_extraction_exception(ex)
|
137
|
-
end
|
138
|
-
|
139
|
-
def extract_argument(value, irep_node, types)
|
140
|
-
return unless argument_extracted_method = extraction_method(:argument_extracted)
|
141
|
-
|
142
|
-
argument_extracted_method.call(
|
143
|
-
{
|
144
|
-
name: value.definition.expose_as,
|
145
|
-
type: value.definition.type.unwrap.to_s,
|
146
|
-
value_is_null: value.value.nil?,
|
147
|
-
default_used: value.default_used?,
|
148
|
-
parent_input_type: types.map(&:unwrap).last&.to_s,
|
149
|
-
field_name: irep_node.definition.name,
|
150
|
-
field_base_type: irep_node&.owner_type.to_s
|
151
|
-
},
|
152
|
-
{
|
153
|
-
query: query,
|
154
|
-
irep_node: irep_node,
|
155
|
-
value: value,
|
156
|
-
}
|
157
|
-
)
|
158
|
-
rescue StandardError => ex
|
159
|
-
handle_extraction_exception(ex)
|
160
|
-
end
|
161
|
-
|
162
|
-
def extract_variables(operation, used_variables)
|
163
|
-
return unless variable_extracted_method = extraction_method(:variable_extracted)
|
164
|
-
|
165
|
-
operation.variables.each do |variable|
|
166
|
-
value_provided = query.provided_variables.key?(variable.name)
|
167
|
-
|
168
|
-
default_value_type = case variable.default_value
|
169
|
-
when GraphQL::Language::Nodes::NullValue
|
170
|
-
EXPLICIT_NULL
|
171
|
-
when nil
|
172
|
-
IMPLICIT_NULL
|
173
|
-
else
|
174
|
-
NON_NULL
|
175
|
-
end
|
176
|
-
|
177
|
-
default_used = !value_provided && default_value_type != IMPLICIT_NULL
|
178
|
-
|
179
|
-
variable_extracted_method.call(
|
180
|
-
{
|
181
|
-
operation_name: operation.name,
|
182
|
-
unwrapped_type_name: unwrapped_type(variable.type),
|
183
|
-
type: variable.type.to_query_string,
|
184
|
-
default_value_type: default_value_type,
|
185
|
-
provided_value: value_provided,
|
186
|
-
default_used: default_used,
|
187
|
-
used_in_operation: used_variables.include?(variable.name)
|
188
|
-
},
|
189
|
-
{
|
190
|
-
query: query
|
191
|
-
}
|
192
|
-
)
|
193
|
-
end
|
194
|
-
rescue StandardError => ex
|
195
|
-
handle_extraction_exception(ex)
|
196
|
-
end
|
197
|
-
|
198
|
-
def extract_used_variables
|
199
|
-
query.irep_selection.ast_node.variables.each_with_object(Set.new) { |v, set| set << v.name }
|
200
|
-
end
|
201
|
-
|
202
|
-
def extract_arguments(irep_node)
|
203
|
-
return unless irep_node.ast_node.is_a?(GraphQL::Language::Nodes::Field)
|
204
|
-
|
205
|
-
traverse_arguments(irep_node.arguments.argument_values.values, irep_node)
|
206
|
-
rescue GraphQL::ExecutionError
|
207
|
-
# no-op. See https://github.com/rmosolgo/graphql-ruby/issues/982.
|
208
|
-
nil
|
209
|
-
rescue StandardError => ex
|
210
|
-
handle_extraction_exception(ex)
|
211
|
-
end
|
212
|
-
|
213
|
-
def extract_node(irep_node)
|
214
|
-
extract_field(irep_node)
|
215
|
-
extract_arguments(irep_node)
|
216
|
-
|
217
|
-
irep_node.scoped_children.each_value do |children|
|
218
|
-
children.each_value do |child_irep_node|
|
219
|
-
extract_node(child_irep_node)
|
220
|
-
end
|
221
|
-
end
|
222
|
-
rescue StandardError => ex
|
223
|
-
handle_extraction_exception(ex)
|
224
|
-
end
|
225
|
-
|
226
|
-
def traverse_arguments(value, irep_node, types = [])
|
227
|
-
case value
|
228
|
-
when Array
|
229
|
-
value.each do |v|
|
230
|
-
traverse_arguments(v, irep_node, types)
|
231
|
-
end
|
232
|
-
when Hash
|
233
|
-
value.each_value do |v|
|
234
|
-
traverse_arguments(v, irep_node, types)
|
235
|
-
end
|
236
|
-
when ::GraphQL::Query::Arguments
|
237
|
-
value.each_value do |arg_val|
|
238
|
-
traverse_arguments(arg_val, irep_node, types)
|
239
|
-
end
|
240
|
-
when ::GraphQL::Query::Arguments::ArgumentValue
|
241
|
-
extract_argument(value, irep_node, types)
|
242
|
-
traverse_arguments(value.value, irep_node, types + [value.definition.type])
|
243
|
-
when ::GraphQL::Schema::InputObject
|
244
|
-
traverse_arguments(value.arguments.argument_values.values, irep_node, types)
|
245
|
-
end
|
246
|
-
rescue StandardError => ex
|
247
|
-
handle_extraction_exception(ex)
|
248
|
-
end
|
249
|
-
|
250
|
-
def unwrapped_type(type)
|
251
|
-
if type.is_a?(GraphQL::Language::Nodes::WrapperType)
|
252
|
-
unwrapped_type(type.of_type)
|
253
|
-
else
|
254
|
-
type.name
|
255
|
-
end
|
256
|
-
rescue StandardError => ex
|
257
|
-
handle_extraction_exception(ex)
|
258
|
-
end
|
259
|
-
|
260
|
-
def extraction_method(method_name)
|
261
|
-
@extraction_method_cache ||= {}
|
262
|
-
return @extraction_method_cache[method_name] if @extraction_method_cache.has_key?(method_name)
|
263
|
-
|
264
|
-
method = if respond_to?(method_name)
|
265
|
-
method(method_name)
|
266
|
-
elsif instrumentor && instrumentor.respond_to?(method_name)
|
267
|
-
instrumentor.method(method_name)
|
268
|
-
else
|
269
|
-
nil
|
270
|
-
end
|
271
|
-
|
272
|
-
method.tap do |method|
|
273
|
-
@extraction_method_cache[method_name] = method
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|