123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123 |
- # frozen_string_literal: true
- module Agents
- class KeyValueStoreAgent < Agent
- can_control_other_agents!
- cannot_be_scheduled!
- cannot_create_events!
- description <<~MD
- The Key-Value Store Agent is a data storage that keeps an associative array in its memory. It receives events to store values and provides the data to other agents as an object via Liquid Templating.
- Liquid templates specified in the `key` and `value` options are evaluated for each received event to be stored in the memory.
- The `variable` option specifies the name by which agents that use the storage refers to the data in Liquid templating.
- The `max_keys` option specifies up to how many keys to keep in the storage. When the number of keys goes beyond this, the oldest key-value pair gets removed. (default: 100)
- ### Storing data
- For example, say your agent receives these incoming events:
- {
- "city": "Tokyo",
- "weather": "cloudy"
- }
- {
- "city": "Osaka",
- "weather": "sunny"
- }
- Then you could configure the agent with `{ "key": "{{ city }}", "value": "{{ weather }}" }` to get the following data stored:
- {
- "Tokyo": "cloudy",
- "Osaka": "sunny"
- }
- Here are some specifications:
- - 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_`.
- ### Extracting data
- To allow other agents to use the data of a Key-Value Store Agent, designate the agent as a controller.
- You can do that by adding those agents to the "Controller targets" of the agent.
- The target agents can refer to the storage via the variable specified by the `variable` option value. So, if the store agent in the above example had an option `"variable": "weather"`, they can say something like `{{ weather[city] | default: "unknown" }}` in their templates to get the weather of a city stored in the variable `city`.
- MD
- def validate_options
- options[:key].is_a?(String) or
- errors.add(:base, "key is required and must be a string.")
- options[:value] or
- errors.add(:base, "value is required.")
- /\A(?!\d)\w+\z/ === options[:variable] or
- errors.add(:base, "variable is required and must be valid as a variable name.")
- max_keys > 0 or
- errors.add(:base, "max_keys must be a positive number.")
- end
- def default_options
- {
- 'key' => '{{ id }}',
- 'value' => '{{ _event_ | as_object }}',
- 'variable' => 'var',
- }
- end
- def working?
- !recent_error_logs?
- end
- def control_action
- 'provide'
- end
- def max_keys
- if value = options[:max_keys].presence
- value.to_i
- else
- 100
- end
- end
- def receive(incoming_events)
- max_keys = max_keys()
- incoming_events.each do |event|
- interpolate_with(event) do
- interpolation_context.stack do
- interpolation_context['_event_'] = event.payload
- key = interpolate_options(options)['key'].to_s
- next if key.empty?
- storage = memory
- interpolation_context['_value_'] = storage.delete(key)
- 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
- end
- end
- end
- end
- end
|