diff --git a/app/concerns/agent_controller_concern.rb b/app/concerns/agent_controller_concern.rb index 15ca9cca..f5ea8b78 100644 --- a/app/concerns/agent_controller_concern.rb +++ b/app/concerns/agent_controller_concern.rb @@ -39,45 +39,47 @@ module AgentControllerConcern def control! control_targets.each { |target| - begin - case control_action - when 'run' - case - when target.cannot_be_scheduled? - error "'#{target.name}' cannot run without an incoming event" - when target.disabled? - log "Agent run ignored for disabled Agent '#{target.name}'" - else - Agent.async_check(target.id) - log "Agent run queued for '#{target.name}'" - end - when 'enable' - case - when target.disabled? - target.update!(disabled: false) - log "Agent '#{target.name}' is enabled" - else - log "Agent '#{target.name}' is already enabled" - end - when 'disable' - case - when target.disabled? - log "Agent '#{target.name}' is alread disabled" - else - target.update!(disabled: true) - log "Agent '#{target.name}' is disabled" - end - when 'configure' - target.update! options: target.options.deep_merge(interpolated['configure_options']) - log "Agent '#{target.name}' is configured with #{interpolated['configure_options'].inspect}" - when '' + interpolate_with('target' => target) { + begin + case action = control_action + when 'run' + case + when target.cannot_be_scheduled? + error "'#{target.name}' cannot run without an incoming event" + when target.disabled? + log "Agent run ignored for disabled Agent '#{target.name}'" + else + Agent.async_check(target.id) + log "Agent run queued for '#{target.name}'" + end + when 'enable' + case + when target.disabled? + target.update!(disabled: false) + log "Agent '#{target.name}' is enabled" + else + log "Agent '#{target.name}' is already enabled" + end + when 'disable' + case + when target.disabled? + log "Agent '#{target.name}' is alread disabled" + else + target.update!(disabled: true) + log "Agent '#{target.name}' is disabled" + end + when 'configure' + target.update! options: target.options.deep_merge(interpolated['configure_options']) + log "Agent '#{target.name}' is configured with #{interpolated['configure_options'].inspect}" + when '' # Do nothing - else - error "Unsupported action '#{control_action}' ignored for '#{target.name}'" + else + error "Unsupported action '#{action}' ignored for '#{target.name}'" + end + rescue => e + error "Failed to #{action} '#{target.name}': #{e.message}" end - rescue => e - error "Failed to #{control_action} '#{target.name}': #{e.message}" - end + } } end end diff --git a/app/models/agents/commander_agent.rb b/app/models/agents/commander_agent.rb index ede7760e..b2386496 100644 --- a/app/models/agents/commander_agent.rb +++ b/app/models/agents/commander_agent.rb @@ -27,6 +27,8 @@ module Agents - If you want to update a WeatherAgent based on a UserLocationAgent, you could use `'action': 'configure'` and set 'configure_options' to `{ 'location': '{{_location_.latlng}}' }`. + - In templating, you can use the variable `target` to refer to each target agent, which has the following attributes: #{AgentDrop.instance_methods(false).map { |m| "`#{m}`" }.to_sentence}. + # Targets Select Agents that you want to control from this CommanderAgent. diff --git a/spec/models/agents/commander_agent_spec.rb b/spec/models/agents/commander_agent_spec.rb index 01ecc2e6..acf24f27 100644 --- a/spec/models/agents/commander_agent_spec.rb +++ b/spec/models/agents/commander_agent_spec.rb @@ -1,6 +1,10 @@ require 'rails_helper' describe Agents::CommanderAgent do + let(:target) { + agents(:bob_website_agent) + } + let(:valid_params) { { name: 'Example', @@ -8,27 +12,27 @@ describe Agents::CommanderAgent do options: { 'action' => 'run', }, + user: users(:bob), + control_targets: [target] } } let(:agent) { - described_class.create!(valid_params) { |agent| - agent.user = users(:bob) - } + described_class.create!(valid_params) } it_behaves_like AgentControllerConcern describe "check" do it "should command targets" do - stub(agent).control!.once { nil } + stub(Agent).async_check(target.id).once { nil } agent.check end end describe "receive_events" do it "should command targets" do - stub(agent).control!.once { nil } + stub(Agent).async_check(target.id).once { nil } event = Event.new event.agent = agents(:bob_rain_notifier_agent) @@ -38,5 +42,62 @@ describe Agents::CommanderAgent do } agent.receive([event]) end + + context "to configure" do + let(:real_target) { + Agents::TriggerAgent.create!( + name: "somename", + options: { + expected_receive_period_in_days: 2, + rules: [ + { + 'type' => 'field '200.0', + 'path' => 'price', + } + ], + keep_event: 'true' + }, + user: users(:bob) + ) + } + + let(:valid_params) { + { + name: 'Example', + schedule: 'never', + options: { + 'action' => '{% if target.id == agent_id %}configure{% endif %}', + 'configure_options' => { + 'rules' => [ + { + 'type' => 'field "{{price}}", + 'path' => 'price', + } + ] + } + }, + user: users(:bob), + control_targets: [target, real_target] + } + } + + it "should conditionally configure targets interpolating agent attributes" do + expect { + event = Event.new + event.agent = agents(:bob_website_agent) + event.payload = { + 'price' => '198.0', + 'agent_id' => real_target.id + } + agent.receive([event]) + }.to change { + real_target.options['rules'][0]['value'] + }.from('200.0').to('198.0') & not_change { + target.options + } + end + end end end diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb new file mode 100644 index 00000000..93f5b66e --- /dev/null +++ b/spec/support/matchers.rb @@ -0,0 +1 @@ +RSpec::Matchers.define_negated_matcher :not_change, :change