From b0d6941005fc39b6e4a20e9feb97f712e55096e3 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Thu, 27 Aug 2026 17:19:05 +0200 Subject: [PATCH] feat(data-collection): Allow booleans as shorthand for KeyValueCollection fields --- sentry-ruby/lib/sentry/data_collection.rb | 38 ++++++++++++++----- .../data_collection/key_value_collection.rb | 20 +++++++++- .../key_value_collection_spec.rb | 28 ++++++++++++++ .../spec/sentry/data_collection_spec.rb | 12 ++++++ 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/sentry-ruby/lib/sentry/data_collection.rb b/sentry-ruby/lib/sentry/data_collection.rb index 99ffb8301..b7198a7f1 100644 --- a/sentry-ruby/lib/sentry/data_collection.rb +++ b/sentry-ruby/lib/sentry/data_collection.rb @@ -42,14 +42,22 @@ class DataCollection class HttpHeaders # @return [KeyValueCollection] - attr_accessor :request + attr_reader :request # @return [KeyValueCollection] - attr_accessor :response + attr_reader :response def initialize(request:, response:) - @request = request - @response = response + self.request = request + self.response = response + end + + def request=(value) + @request = KeyValueCollection.from(value) + end + + def response=(value) + @response = KeyValueCollection.from(value) end end @@ -70,9 +78,10 @@ def initialize(document:, variables:) # @default `true` attr_accessor :user_info - # @return [KeyValueCollection] + # @return [Boolean, KeyValueCollection] + # A boolean is shorthand for `mode: :deny_list` (`true`) or `mode: :off` (`false`). # @default `mode: :deny_list, terms: nil` - attr_accessor :cookies + attr_reader :cookies # @return [HttpHeaders] # @default request and response use `mode: :deny_list, terms: nil` @@ -82,9 +91,10 @@ def initialize(document:, variables:) # @default `nil` (all valid body types) attr_accessor :http_bodies - # @return [KeyValueCollection] + # @return [Boolean, KeyValueCollection] + # A boolean is shorthand for `mode: :deny_list` (`true`) or `mode: :off` (`false`). # @default `mode: :deny_list, terms: nil` - attr_accessor :url_query_params + attr_reader :url_query_params # @return [Boolean] # @default `true` @@ -142,13 +152,13 @@ def self.backfill(configuration) def initialize @user_info = true - @cookies = KeyValueCollection.new(mode: :deny_list, terms: nil) + self.cookies = KeyValueCollection.new(mode: :deny_list, terms: nil) @http_headers = HttpHeaders.new( request: KeyValueCollection.new(mode: :deny_list, terms: nil), response: KeyValueCollection.new(mode: :deny_list, terms: nil) ) @http_bodies = BODY_TYPES.dup - @url_query_params = KeyValueCollection.new(mode: :deny_list, terms: nil) + self.url_query_params = KeyValueCollection.new(mode: :deny_list, terms: nil) @database_query_data = true @graphql = GraphQL.new(document: true, variables: true) @queues = true @@ -156,6 +166,14 @@ def initialize @frame_context_lines = 3 end + def cookies=(value) + @cookies = KeyValueCollection.from(value) + end + + def url_query_params=(value) + @url_query_params = KeyValueCollection.from(value) + end + # Returns whether incoming HTTP request bodies should be collected. # nil implies all BODY_TYPES according to spec def collect_incoming_http_body? diff --git a/sentry-ruby/lib/sentry/data_collection/key_value_collection.rb b/sentry-ruby/lib/sentry/data_collection/key_value_collection.rb index ed911c887..e747b3c6f 100644 --- a/sentry-ruby/lib/sentry/data_collection/key_value_collection.rb +++ b/sentry-ruby/lib/sentry/data_collection/key_value_collection.rb @@ -57,18 +57,34 @@ class KeyValueCollection # - `:off` disables collection. # - `:deny_list` collects values except those matching `terms`. # - `:allow_list` collects only values matching `terms`. + # Boolean values are accepted as shorthand for `:deny_list` and `:off`. # @return [:off, :deny_list, :allow_list] - attr_accessor :mode + attr_reader :mode # `terms` contains the keys or patterns used by the selected mode. # @return [Array, nil] attr_reader :terms def initialize(mode:, terms:) - @mode = mode + self.mode = mode self.terms = terms end + def mode=(mode) + @mode = case mode + when true then :deny_list + when false then :off + else mode + end + end + + # Converts the boolean shorthand into a collection configuration. + def self.from(value) + return new(mode: value, terms: nil) if value.equal?(true) || value.equal?(false) + + value + end + def terms=(terms) @terms = terms&.map { |term| term.is_a?(Regexp) ? term : term.to_s.downcase } &.reject { |term| !term.is_a?(Regexp) && term.strip.empty? } diff --git a/sentry-ruby/spec/sentry/data_collection/key_value_collection_spec.rb b/sentry-ruby/spec/sentry/data_collection/key_value_collection_spec.rb index db12c3705..1896feaec 100644 --- a/sentry-ruby/spec/sentry/data_collection/key_value_collection_spec.rb +++ b/sentry-ruby/spec/sentry/data_collection/key_value_collection_spec.rb @@ -16,6 +16,34 @@ let(:mode) { :deny_list } let(:terms) { nil } + describe ".from" do + it "maps true to deny-list mode" do + expect(described_class.from(true).mode).to eq(:deny_list) + end + + it "maps false to off mode" do + expect(described_class.from(false).mode).to eq(:off) + end + + it "returns an existing collection unchanged" do + expect(described_class.from(collection)).to be(collection) + end + end + + describe "#mode=" do + it "maps true to deny-list mode" do + collection.mode = true + + expect(collection.mode).to eq(:deny_list) + end + + it "maps false to off mode" do + collection.mode = false + + expect(collection.mode).to eq(:off) + end + end + describe "#filter" do it "uses the collection configuration" do expect(collection.filter(values)).to eq( diff --git a/sentry-ruby/spec/sentry/data_collection_spec.rb b/sentry-ruby/spec/sentry/data_collection_spec.rb index e21996916..71983ae32 100644 --- a/sentry-ruby/spec/sentry/data_collection_spec.rb +++ b/sentry-ruby/spec/sentry/data_collection_spec.rb @@ -111,6 +111,18 @@ expect(data_collection.cookies.terms).to eq(["page"]) end + it "supports using booleans as a shorthand for key-value collections" do + data_collection.cookies = true + data_collection.http_headers.request = false + data_collection.http_headers.response = true + data_collection.url_query_params = false + + expect(data_collection.cookies.mode).to eq(:deny_list) + expect(data_collection.http_headers.request.mode).to eq(:off) + expect(data_collection.http_headers.response.mode).to eq(:deny_list) + expect(data_collection.url_query_params.mode).to eq(:off) + end + it "supports configuring request and response headers independently" do data_collection.http_headers.request.mode = :off data_collection.http_headers.response.terms = ["x-request-id"]