From 72193453d4508061e8757d53245b2e219bfe99a3 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Thu, 22 Jun 2023 23:05:53 +0900 Subject: [PATCH 1/3] KeyValueStoreAgent deletes a key if the value is empty as documented --- app/models/agents/key_value_store_agent.rb | 11 ++++-- .../agents/key_value_store_agent_spec.rb | 39 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/app/models/agents/key_value_store_agent.rb b/app/models/agents/key_value_store_agent.rb index 20a99bf7dd..94b1a191e2 100644 --- a/app/models/agents/key_value_store_agent.rb +++ b/app/models/agents/key_value_store_agent.rb @@ -103,9 +103,14 @@ def receive(incoming_events) storage = memory interpolation_context['_value_'] = storage.delete(key) - storage[key] = interpolate_options(options)['value'] - - storage.shift while storage.size > max_keys + value = interpolate_options(options)['value'] + + 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/spec/models/agents/key_value_store_agent_spec.rb b/spec/models/agents/key_value_store_agent_spec.rb index a6a5de6c04..60f9b3275d 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 @@ -114,6 +114,43 @@ def create_event(payload) ) 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 let(:value_template) { "{% if _value_ %}{{ _value_ }}, {% endif %}{{ name }}" } From 5ab6261f99b0bb19d15293f7dbc6a4f9ba830a58 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Thu, 22 Jun 2023 23:07:13 +0900 Subject: [PATCH 2/3] KeyValueStoreAgent now ignores an empty key --- app/models/agents/key_value_store_agent.rb | 2 ++ spec/models/agents/key_value_store_agent_spec.rb | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/app/models/agents/key_value_store_agent.rb b/app/models/agents/key_value_store_agent.rb index 94b1a191e2..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,6 +100,7 @@ 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) diff --git a/spec/models/agents/key_value_store_agent_spec.rb b/spec/models/agents/key_value_store_agent_spec.rb index 60f9b3275d..ccd81b57d6 100644 --- a/spec/models/agents/key_value_store_agent_spec.rb +++ b/spec/models/agents/key_value_store_agent_spec.rb @@ -112,6 +112,10 @@ 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 From 7eabed25b4d72e65e3036cdf97c4602f385cac67 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Thu, 22 Jun 2023 23:07:26 +0900 Subject: [PATCH 3/3] Add a migration to delete empty keys and values from KeyValueStoreAgents --- ...e_empty_keys_and_values_from_key_value_store_agents.rb | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 db/migrate/20230622140301_delete_empty_keys_and_values_from_key_value_store_agents.rb 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