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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdf8bbcc4676ce4b0cebd4c2deb071fe065cda4d325e9c1cafb4b6781dfb2651
4
- data.tar.gz: 5eee4c6d212c8b5461ac20fb68ccb35236e3e2f31c446faad1dd8e99980e6e71
3
+ metadata.gz: 1fa33a96e20e7429e87a7a1a3d7dea4d42a826c7aa58275234f1765bb2d990dc
4
+ data.tar.gz: a6366ab15aa1b76609406a01430eb71877d8f634a3e587a6a75bf5416c21fccb
5
5
  SHA512:
6
- metadata.gz: 640103e353f961efe91e91ce005089670fa7ac1df9bbec11f78b1a8e51a2b9528977519e9281e91e8a6778309d8033ac3d043fdcc93aad375a58a8aee2d9bbbc
7
- data.tar.gz: b8ffc2808008d65f1295b8ff4c2c3198f051808e2bd4b53cc3919d895782d124c29ad0a763079e9a34095a7e9cdbf3bcde7048da39fa0210c83863fb7fbdf882
6
+ metadata.gz: d798387a6a48648f5ef8a07c30e27aeb645327f5c334ff85684f3268cb12e99ab53131c6c8f72d9f24e0a62800271984c80006813a185de7a570411c3988b881
7
+ data.tar.gz: 262294726fdc7e9e22ab7079c04e143b34ab9334b47fc80797828b3aff9c5dab45b848c39c5e29122f40801cdc9b9310e00e554ce1eb3e1da9f38e3422a63254
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "0.6.4"
2
+ ".": "0.6.5"
3
3
  }
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphql-hive (0.6.4)
4
+ graphql-hive (0.6.5)
5
5
  graphql (>= 2.4.12, < 3)
6
6
 
7
7
  GEM
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
- input_type.all_argument_definitions.map(&:graphql_name).each do |n|
50
- @used_fields.add(make_id(input_type.graphql_name, n))
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
- enum_type.values.values.map(&:graphql_name).each do |n|
67
- @used_fields.add(make_id(enum_type.graphql_name, n))
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: fields.to_a,
151
+ fields: merged_fields.to_a,
144
152
  operationName: operation_name,
145
153
  operation: operation
146
154
  }
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Graphql
4
4
  module Hive
5
- VERSION = "0.6.4"
5
+ VERSION = "0.6.5"
6
6
  end
7
7
  end
data/lib/graphql-hive.rb CHANGED
@@ -42,7 +42,8 @@ module GraphQL
42
42
  collect_usage_sampling: 1.0,
43
43
  target: nil,
44
44
  log_request_details: false,
45
- warn_on_hive_errors: false
45
+ warn_on_hive_errors: false,
46
+ process_variables: false
46
47
  }.freeze
47
48
 
48
49
  self.platform_keys = {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql-hive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charly Poly