Jelajahi Sumber

Merge pull request #2053 from huginn/command_agent_can_refer_to_target

Expose `target` to CommanderAgent in templating
Akinori MUSHA 7 tahun lalu
induk
melakukan
94d6d930fd

+ 38 - 36
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"
+      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
-            target.update!(disabled: true)
-            log "Agent '#{target.name}' is disabled"
+            error "Unsupported action '#{action}' ignored for '#{target.name}'"
           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}'"
+        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

+ 2 - 0
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.

+ 66 - 5
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<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 - 0
spec/support/matchers.rb

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