diff --git a/app/models/agents/key_value_store_agent.rb b/app/models/agents/key_value_store_agent.rb index 20a99bf7dd..324fc56346 100644 --- a/app/models/agents/key_value_store_agent.rb +++ b/app/models/agents/key_value_store_agent.rb @@ -40,6 +40,7 @@ class KeyValueStoreAgent < Agent - Keys are always stringified as mandated by the JSON format. - Values are stringified by default. Use the `as_object` filter to store non-string values. + - If the key is evaluated to an empty string, the event is ignored. - If the value is evaluated to either `null` or empty (`""`, `[]`, `{}`) the key gets deleted. - In the `value` template, the existing value (if any) can be accessed via the variable `_value_`. - In the `key` and `value` templates, the whole event payload can be accessed via the variable `_event_`. @@ -99,13 +100,19 @@ def receive(incoming_events) interpolation_context['_event_'] = event.payload key = interpolate_options(options)['key'].to_s + next if key.empty? storage = memory interpolation_context['_value_'] = storage.delete(key) - storage[key] = interpolate_options(options)['value'] + value = interpolate_options(options)['value'] - storage.shift while storage.size > max_keys + if value.nil? || value.try(:empty?) + storage.delete(key) + else + storage[key] = value + storage.shift while storage.size > max_keys + end update!(memory: storage) end diff --git a/db/migrate/20230622140301_delete_empty_keys_and_values_from_key_value_store_agents.rb b/db/migrate/20230622140301_delete_empty_keys_and_values_from_key_value_store_agents.rb new file mode 100644 index 0000000000..788eb6a97b --- /dev/null +++ b/db/migrate/20230622140301_delete_empty_keys_and_values_from_key_value_store_agents.rb @@ -0,0 +1,8 @@ +class DeleteEmptyKeysAndValuesFromKeyValueStoreAgents < ActiveRecord::Migration[6.1] + def up + Agents::KeyValueStoreAgent.find_each do |agent| + agent.memory.delete_if { |key, value| key.empty? || value.nil? || value.try(:empty?) } + agent.save! + end + end +end diff --git a/spec/models/agents/key_value_store_agent_spec.rb b/spec/models/agents/key_value_store_agent_spec.rb index a6a5de6c04..ccd81b57d6 100644 --- a/spec/models/agents/key_value_store_agent_spec.rb +++ b/spec/models/agents/key_value_store_agent_spec.rb @@ -21,7 +21,7 @@ end def create_event(payload) - source_agent.events.create!(payload: payload) + source_agent.events.create!(payload:) end let(:events) do @@ -112,6 +112,47 @@ def create_event(payload) "4" => { id: 4, name: "quux" }, } ) + + expect { + agent.receive([create_event({ name: "empty key" })]) + }.not_to(change { agent.reload.memory }) + end + + describe "empty value" do + let(:value_template) { "{{ name | as_object }}" } + + it "deletes the key" do + agent.receive(events[0..2]) + + expect(agent.reload.memory).to match( + { + "1" => "foo", + "2" => "bar", + "3" => "baz", + } + ) + + agent.receive([create_event({ id: 1, name: "" })]) + + expect(agent.reload.memory).to match( + { + "2" => "bar", + "3" => "baz", + } + ) + + agent.receive([create_event({ id: 2, name: [] })]) + + expect(agent.reload.memory).to match( + { + "3" => "baz", + } + ) + + agent.receive([create_event({ id: 3, name: {} })]) + + expect(agent.reload.memory).to eq({}) + end end describe "using _value_" do