graphql-hive 0.6.4 → 0.6.5
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 +4 -4
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +1 -1
- data/README.md +10 -0
- data/lib/graphql-hive/analyzer.rb +62 -10
- data/lib/graphql-hive/usage_reporter.rb +10 -2
- data/lib/graphql-hive/version.rb +1 -1
- data/lib/graphql-hive.rb +2 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1fa33a96e20e7429e87a7a1a3d7dea4d42a826c7aa58275234f1765bb2d990dc
|
|
4
|
+
data.tar.gz: a6366ab15aa1b76609406a01430eb71877d8f634a3e587a6a75bf5416c21fccb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d798387a6a48648f5ef8a07c30e27aeb645327f5c334ff85684f3268cb12e99ab53131c6c8f72d9f24e0a62800271984c80006813a185de7a570411c3988b881
|
|
7
|
+
data.tar.gz: 262294726fdc7e9e22ab7079c04e143b34ab9334b47fc80797828b3aff9c5dab45b848c39c5e29122f40801cdc9b9310e00e554ce1eb3e1da9f38e3422a63254
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.5](https://github.com/wealthsimple-community/graphql-ruby-hive/compare/v0.6.4...v0.6.5) (2026-09-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* support processVariables ([#14](https://github.com/wealthsimple-community/graphql-ruby-hive/issues/14)) ([94cc38f](https://github.com/wealthsimple-community/graphql-ruby-hive/commit/94cc38f9e379c5aa7dd85e40d46f8add17a9fb57))
|
|
9
|
+
|
|
3
10
|
## [0.6.4](https://github.com/wealthsimple-community/graphql-ruby-hive/compare/v0.6.3...v0.6.4) (2026-08-17)
|
|
4
11
|
|
|
5
12
|
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -179,6 +179,10 @@ class MySchema < GraphQL::Schema
|
|
|
179
179
|
queue_size: 1000,
|
|
180
180
|
# Report usage to Hive.
|
|
181
181
|
collect_usage: true,
|
|
182
|
+
# Inspect actual variable payloads to report only the input fields and enum values
|
|
183
|
+
# a client actually provided, rather than every field that could theoretically be
|
|
184
|
+
# used. See the `process_variables` section below for details and trade-offs.
|
|
185
|
+
process_variables: false,
|
|
182
186
|
# Usage sampling configurations.
|
|
183
187
|
collect_usage_sampling: {
|
|
184
188
|
# % of operations recorded.
|
|
@@ -225,3 +229,9 @@ See default options for the optional parameters [here](https://github.com/wealth
|
|
|
225
229
|
> `queue_size` is the size of the queue used to send operations to the buffer before sampling.
|
|
226
230
|
> Adjust these values according to your application's memory constraints and throughput.
|
|
227
231
|
> High throughput applications will need a larger `queue_size`.
|
|
232
|
+
|
|
233
|
+
## `process_variables`
|
|
234
|
+
|
|
235
|
+
By default, when an input object or enum is passed through a `$variable`, every field of that input type (or every value of that enum) is reported as used — the conservative assumption. Setting `process_variables: true` reports only the coordinates a client actually populated, using both the plain form (`InputType.field`) and a `!`-suffixed form (`InputType.field!`). Hive uses the `!` form to power granular, per-input-field [conditional breaking-change](https://the-guild.dev/graphql/hive/docs/management/targets#conditional-breaking-changes) decisions.
|
|
236
|
+
|
|
237
|
+
Only schema coordinates derived from the payload structure are sent — never the variable values themselves.
|
|
@@ -4,9 +4,11 @@ module GraphQL
|
|
|
4
4
|
class Hive < GraphQL::Tracing::PlatformTracing
|
|
5
5
|
# Fetch all users fields, input objects and enums
|
|
6
6
|
class Analyzer < GraphQL::Analysis::AST::Analyzer
|
|
7
|
-
def initialize(query_or_multiplex)
|
|
8
|
-
super
|
|
7
|
+
def initialize(query_or_multiplex, options = {})
|
|
8
|
+
super(query_or_multiplex)
|
|
9
9
|
@used_fields = Set.new
|
|
10
|
+
@process_variables = options.fetch(:process_variables, false)
|
|
11
|
+
@provided_variables = (@process_variables && query_or_multiplex.respond_to?(:provided_variables)) ? (query_or_multiplex.provided_variables || {}) : {}
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
def on_enter_field(node, _parent, visitor)
|
|
@@ -29,9 +31,9 @@ module GraphQL
|
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
if arg_type.kind.input_object?
|
|
32
|
-
collect_input_object_fields(node, arg_type)
|
|
34
|
+
collect_input_object_fields(node, arg_type, visitor.argument_definition.type)
|
|
33
35
|
elsif arg_type.kind.enum?
|
|
34
|
-
collect_enum_values(node, arg_type)
|
|
36
|
+
collect_enum_values(node, arg_type, visitor.argument_definition.type)
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
|
|
@@ -43,11 +45,15 @@ module GraphQL
|
|
|
43
45
|
|
|
44
46
|
private
|
|
45
47
|
|
|
46
|
-
def collect_input_object_fields(node, input_type)
|
|
48
|
+
def collect_input_object_fields(node, input_type, full_type)
|
|
47
49
|
case node.value
|
|
48
50
|
when GraphQL::Language::Nodes::VariableIdentifier
|
|
49
|
-
|
|
50
|
-
@
|
|
51
|
+
if @process_variables
|
|
52
|
+
walk_variable_value(full_type, @provided_variables[node.value.name])
|
|
53
|
+
else
|
|
54
|
+
input_type.all_argument_definitions.map(&:graphql_name).each do |n|
|
|
55
|
+
@used_fields.add(make_id(input_type.graphql_name, n))
|
|
56
|
+
end
|
|
51
57
|
end
|
|
52
58
|
when Array
|
|
53
59
|
node.value.flat_map(&:arguments).map(&:name).each do |n|
|
|
@@ -60,11 +66,15 @@ module GraphQL
|
|
|
60
66
|
end
|
|
61
67
|
end
|
|
62
68
|
|
|
63
|
-
def collect_enum_values(node, enum_type)
|
|
69
|
+
def collect_enum_values(node, enum_type, full_type)
|
|
64
70
|
case node.value
|
|
65
71
|
when GraphQL::Language::Nodes::VariableIdentifier
|
|
66
|
-
|
|
67
|
-
@
|
|
72
|
+
if @process_variables
|
|
73
|
+
walk_variable_value(full_type, @provided_variables[node.value.name])
|
|
74
|
+
else
|
|
75
|
+
enum_type.values.values.map(&:graphql_name).each do |n|
|
|
76
|
+
@used_fields.add(make_id(enum_type.graphql_name, n))
|
|
77
|
+
end
|
|
68
78
|
end
|
|
69
79
|
when Array
|
|
70
80
|
node.value.map(&:name).each do |n|
|
|
@@ -75,6 +85,48 @@ module GraphQL
|
|
|
75
85
|
end
|
|
76
86
|
end
|
|
77
87
|
|
|
88
|
+
# Recursively walk a variable's runtime value, marking only the schema
|
|
89
|
+
# coordinates that were actually provided. Mirrors the JS client's
|
|
90
|
+
# processVariables behavior, including the `!` suffix for input object
|
|
91
|
+
# fields that received a non-null value.
|
|
92
|
+
def walk_variable_value(type, value)
|
|
93
|
+
return if value.nil?
|
|
94
|
+
|
|
95
|
+
t = type
|
|
96
|
+
t = t.of_type while t.non_null?
|
|
97
|
+
|
|
98
|
+
if t.list?
|
|
99
|
+
return unless value.is_a?(Array)
|
|
100
|
+
inner = t.of_type
|
|
101
|
+
value.each { |item| walk_variable_value(inner, item) }
|
|
102
|
+
return
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if t.kind.input_object?
|
|
106
|
+
walk_input_object(t, value)
|
|
107
|
+
elsif t.kind.enum?
|
|
108
|
+
@used_fields.add(make_id(t.graphql_name, value.to_s))
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def walk_input_object(input_type, value)
|
|
113
|
+
return unless value.is_a?(Hash)
|
|
114
|
+
|
|
115
|
+
arg_defs = input_type.all_argument_definitions.each_with_object({}) { |a, h| h[a.graphql_name] = a }
|
|
116
|
+
|
|
117
|
+
value.each do |field_name, field_value|
|
|
118
|
+
key = field_name.to_s
|
|
119
|
+
arg = arg_defs[key]
|
|
120
|
+
next unless arg
|
|
121
|
+
|
|
122
|
+
coord = make_id(input_type.graphql_name, key)
|
|
123
|
+
@used_fields.add(coord)
|
|
124
|
+
@used_fields.add("#{coord}!") unless field_value.nil?
|
|
125
|
+
|
|
126
|
+
walk_variable_value(arg.type, field_value)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
78
130
|
def make_id(*tokens)
|
|
79
131
|
tokens.join(".")
|
|
80
132
|
end
|
|
@@ -105,7 +105,7 @@ module GraphQL
|
|
|
105
105
|
fields = Set.new
|
|
106
106
|
|
|
107
107
|
queries.each do |query|
|
|
108
|
-
analyzer = GraphQL::Hive::Analyzer.new(query)
|
|
108
|
+
analyzer = GraphQL::Hive::Analyzer.new(query, process_variables: @options[:process_variables])
|
|
109
109
|
visitor = GraphQL::Analysis::AST::Visitor.new(
|
|
110
110
|
query: query,
|
|
111
111
|
analyzers: [analyzer],
|
|
@@ -139,8 +139,16 @@ module GraphQL
|
|
|
139
139
|
operation_record[:metadata] = {client: @options[:client_info].call(context)} if @options[:client_info]
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
# Union with any existing fields for this key. Operations that share a document
|
|
143
|
+
# but differ in variables produce the same operation_map_key (MD5 of the printed
|
|
144
|
+
# operation body, which does not include variable values) but can produce
|
|
145
|
+
# different coord sets under process_variables. Overwriting would drop the
|
|
146
|
+
# earlier op's coord evidence for this batch.
|
|
147
|
+
existing_fields = report[:map][operation_map_key]&.fetch(:fields, nil)
|
|
148
|
+
merged_fields = existing_fields ? (Set.new(existing_fields) | fields) : fields
|
|
149
|
+
|
|
142
150
|
report[:map][operation_map_key] = {
|
|
143
|
-
fields:
|
|
151
|
+
fields: merged_fields.to_a,
|
|
144
152
|
operationName: operation_name,
|
|
145
153
|
operation: operation
|
|
146
154
|
}
|
data/lib/graphql-hive/version.rb
CHANGED
data/lib/graphql-hive.rb
CHANGED