Expose target to CommanderAgent in templating

This allows a CommanderAgent to control only some of the targets
depending on the agent attributes.  For example, you can update the
threshold value of a target agent only if the current value is
lower (or higher) than the suggested new value.
This commit is contained in:
Akinori MUSHA 2017-07-09 03:24:26 +09:00
parent a7f3a75277
commit 8645323dfe
4 changed files with 108 additions and 42 deletions

View file

@ -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

View file

@ -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.

View file

@ -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<value',
'value' => '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<value',
'value' => "{{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

1
spec/support/matchers.rb Normal file
View file

@ -0,0 +1 @@
RSpec::Matchers.define_negated_matcher :not_change, :change