mirror of
https://github.com/Fishwaldo/huginn.git
synced 2025-07-06 21:08:42 +00:00
Merge pull request #939 from cantino/dry_run_with_event
Add support for sending an event in a "Dry Run"
This commit is contained in:
commit
67ff37a9f8
9 changed files with 150 additions and 28 deletions
|
@ -34,10 +34,67 @@ class @Utils
|
||||||
body?(modal.querySelector('.modal-body'))
|
body?(modal.querySelector('.modal-body'))
|
||||||
$(modal).modal('show')
|
$(modal).modal('show')
|
||||||
|
|
||||||
@handleDryRunButton: (button, data = $(button.form).serialize()) ->
|
@handleDryRunButton: (button, data = if button.form then $(':input[name!="_method"]', button.form).serialize() else '') ->
|
||||||
$(button).prop('disabled', true)
|
$(button).prop('disabled', true)
|
||||||
|
cleanup = -> $(button).prop('disabled', false)
|
||||||
|
|
||||||
|
url = $(button).data('action-url')
|
||||||
|
with_event_mode = $(button).data('with-event-mode')
|
||||||
|
|
||||||
|
if with_event_mode is 'no'
|
||||||
|
return @invokeDryRun(url, data, cleanup)
|
||||||
|
|
||||||
|
Utils.showDynamicModal """
|
||||||
|
<h5>Event to send#{if with_event_mode is 'maybe' then ' (Optional)' else ''}</h5>
|
||||||
|
<form class="dry-run-form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea rows="10" name="event" class="payload-editor" data-height="200">
|
||||||
|
{}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input value="Dry Run" class="btn btn-primary" type="submit" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
""",
|
||||||
|
body: (body) =>
|
||||||
|
form = $(body).find('.dry-run-form')
|
||||||
|
payload_editor = form.find('.payload-editor')
|
||||||
|
if previous = $(button).data('payload')
|
||||||
|
payload_editor.text(previous)
|
||||||
|
window.setupJsonEditor(payload_editor)
|
||||||
|
form.submit (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
json = $(e.target).find('.payload-editor').val()
|
||||||
|
json = '{}' if json == ''
|
||||||
|
try
|
||||||
|
payload = JSON.parse(json)
|
||||||
|
throw true unless payload.constructor is Object
|
||||||
|
if Object.keys(payload).length == 0
|
||||||
|
json = ''
|
||||||
|
else
|
||||||
|
json = JSON.stringify(payload)
|
||||||
|
catch
|
||||||
|
alert 'Invalid JSON object.'
|
||||||
|
return
|
||||||
|
if json == ''
|
||||||
|
if with_event_mode is 'yes'
|
||||||
|
alert 'Event is required for this agent to run.'
|
||||||
|
return
|
||||||
|
dry_run_data = data
|
||||||
|
$(button).data('payload', null)
|
||||||
|
else
|
||||||
|
dry_run_data = "event=#{encodeURIComponent(json)}&#{data}"
|
||||||
|
$(button).data('payload', json)
|
||||||
|
$(body).closest('[role=dialog]').on 'hidden.bs.modal', =>
|
||||||
|
@invokeDryRun(url, dry_run_data, cleanup)
|
||||||
|
.modal('hide')
|
||||||
|
title: 'Dry Run'
|
||||||
|
onHide: cleanup
|
||||||
|
|
||||||
|
@invokeDryRun: (url, data, callback) ->
|
||||||
$('body').css(cursor: 'progress')
|
$('body').css(cursor: 'progress')
|
||||||
$.ajax type: 'POST', url: $(button).data('action-url'), dataType: 'json', data: data
|
$.ajax type: 'POST', url: url, dataType: 'json', data: data
|
||||||
.always =>
|
.always =>
|
||||||
$('body').css(cursor: 'auto')
|
$('body').css(cursor: 'auto')
|
||||||
.done (json) =>
|
.done (json) =>
|
||||||
|
@ -55,7 +112,7 @@ class @Utils
|
||||||
find('.agent-dry-run-events').text(json.events).end().
|
find('.agent-dry-run-events').text(json.events).end().
|
||||||
find('.agent-dry-run-memory').text(json.memory)
|
find('.agent-dry-run-memory').text(json.memory)
|
||||||
title: 'Dry Run Results',
|
title: 'Dry Run Results',
|
||||||
onHide: -> $(button).prop('disabled', false)
|
onHide: callback
|
||||||
.fail (xhr, status, error) ->
|
.fail (xhr, status, error) ->
|
||||||
alert('Error: ' + error)
|
alert('Error: ' + error)
|
||||||
$(button).prop('disabled', false)
|
callback()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
module DryRunnable
|
module DryRunnable
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
def dry_run!
|
def dry_run!(event = nil)
|
||||||
@dry_run = true
|
@dry_run = true
|
||||||
|
|
||||||
log = StringIO.new
|
log = StringIO.new
|
||||||
|
@ -13,7 +13,12 @@ module DryRunnable
|
||||||
begin
|
begin
|
||||||
raise "#{short_type} does not support dry-run" unless can_dry_run?
|
raise "#{short_type} does not support dry-run" unless can_dry_run?
|
||||||
readonly!
|
readonly!
|
||||||
check
|
if event
|
||||||
|
raise "This agent cannot receive an event!" unless can_receive_events?
|
||||||
|
receive([event])
|
||||||
|
else
|
||||||
|
check
|
||||||
|
end
|
||||||
rescue => e
|
rescue => e
|
||||||
error "Exception during dry-run. #{e.message}: #{e.backtrace.join("\n")}"
|
error "Exception during dry-run. #{e.message}: #{e.backtrace.join("\n")}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,7 @@ class AgentsController < ApplicationController
|
||||||
def dry_run
|
def dry_run
|
||||||
attrs = params[:agent] || {}
|
attrs = params[:agent] || {}
|
||||||
if agent = current_user.agents.find_by(id: params[:id])
|
if agent = current_user.agents.find_by(id: params[:id])
|
||||||
# PUT /agents/:id/dry_run
|
# POST /agents/:id/dry_run
|
||||||
if attrs.present?
|
if attrs.present?
|
||||||
type = agent.type
|
type = agent.type
|
||||||
agent = Agent.build_for_type(type, current_user, attrs)
|
agent = Agent.build_for_type(type, current_user, attrs)
|
||||||
|
@ -50,7 +50,13 @@ class AgentsController < ApplicationController
|
||||||
agent.name ||= '(Untitled)'
|
agent.name ||= '(Untitled)'
|
||||||
|
|
||||||
if agent.valid?
|
if agent.valid?
|
||||||
results = agent.dry_run!
|
if event_payload = params[:event]
|
||||||
|
dummy_agent = Agent.build_for_type('ManualEventAgent', current_user, name: 'Dry-Runner')
|
||||||
|
dummy_agent.readonly!
|
||||||
|
event = dummy_agent.events.build(user: current_user, payload: event_payload)
|
||||||
|
end
|
||||||
|
|
||||||
|
results = agent.dry_run!(event)
|
||||||
|
|
||||||
render json: {
|
render json: {
|
||||||
log: results[:log],
|
log: results[:log],
|
||||||
|
|
|
@ -37,4 +37,16 @@ module AgentHelper
|
||||||
}.join(delimiter).html_safe
|
}.join(delimiter).html_safe
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def agent_dry_run_with_event_mode(agent)
|
||||||
|
case
|
||||||
|
when agent.cannot_receive_events?
|
||||||
|
'no'.freeze
|
||||||
|
when agent.cannot_be_scheduled?
|
||||||
|
# incoming event is the only trigger for the agent
|
||||||
|
'yes'.freeze
|
||||||
|
else
|
||||||
|
'maybe'.freeze
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
<% if agent.can_dry_run? %>
|
<% if agent.can_dry_run? %>
|
||||||
<li>
|
<li>
|
||||||
<%= link_to icon_tag('glyphicon-refresh') + ' Dry Run', '#', 'data-action-url' => dry_run_agent_path(agent), tabindex: "-1", onclick: "Utils.handleDryRunButton(this, '_method=PUT')" %>
|
<%= link_to icon_tag('glyphicon-refresh') + ' Dry Run', '#', 'data-action-url' => dry_run_agent_path(agent), 'data-with-event-mode' => agent_dry_run_with_event_mode(agent), tabindex: "-1", onclick: "Utils.handleDryRunButton(this)" %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,6 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<%= submit_tag "Save", :class => "btn btn-primary" %>
|
<%= submit_tag "Save", :class => "btn btn-primary" %>
|
||||||
<% if agent.can_dry_run? %>
|
<% if agent.can_dry_run? %>
|
||||||
<%= button_tag class: 'btn btn-default agent-dry-run-button', type: 'button', 'data-action-url' => agent.persisted? ? dry_run_agent_path(agent) : dry_run_agents_path do %><%= icon_tag('glyphicon-refresh') %> Dry Run<% end %>
|
<%= button_tag class: 'btn btn-default agent-dry-run-button', type: 'button', 'data-action-url' => agent.persisted? ? dry_run_agent_path(agent) : dry_run_agents_path, 'data-with-event-mode' => agent_dry_run_with_event_mode(agent) do %><%= icon_tag('glyphicon-refresh') %> Dry Run<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@ Huginn::Application.routes.draw do
|
||||||
resources :agents do
|
resources :agents do
|
||||||
member do
|
member do
|
||||||
post :run
|
post :run
|
||||||
put :dry_run
|
post :dry_run
|
||||||
post :handle_details_post
|
post :handle_details_post
|
||||||
put :leave_scenario
|
put :leave_scenario
|
||||||
delete :remove_events
|
delete :remove_events
|
||||||
|
|
|
@ -7,10 +7,22 @@ describe DryRunnable do
|
||||||
can_dry_run!
|
can_dry_run!
|
||||||
|
|
||||||
def check
|
def check
|
||||||
|
perform
|
||||||
|
end
|
||||||
|
|
||||||
|
def receive(events)
|
||||||
|
events.each do |event|
|
||||||
|
perform(event.payload['prefix'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def perform(prefix = nil)
|
||||||
log "Logging"
|
log "Logging"
|
||||||
create_event payload: { 'test' => 'foo' }
|
create_event payload: { 'test' => "#{prefix}foo" }
|
||||||
error "Recording error"
|
error "Recording error"
|
||||||
create_event payload: { 'test' => 'bar' }
|
create_event payload: { 'test' => "#{prefix}bar" }
|
||||||
self.memory = { 'last_status' => 'ok', 'dry_run' => dry_run? }
|
self.memory = { 'last_status' => 'ok', 'dry_run' => dry_run? }
|
||||||
save!
|
save!
|
||||||
end
|
end
|
||||||
|
@ -46,21 +58,6 @@ describe DryRunnable do
|
||||||
expect(messages).to eq(['Logging', 'Recording error'])
|
expect(messages).to eq(['Logging', 'Recording error'])
|
||||||
end
|
end
|
||||||
|
|
||||||
it "traps logging, event emission and memory updating, with dry_run? returning true" do
|
|
||||||
results = nil
|
|
||||||
|
|
||||||
expect {
|
|
||||||
results = @agent.dry_run!
|
|
||||||
@agent.reload
|
|
||||||
}.not_to change {
|
|
||||||
[@agent.memory, counts]
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(results[:log]).to match(/\AI, .+ INFO -- : Logging\nE, .+ ERROR -- : Recording error\n/)
|
|
||||||
expect(results[:events]).to eq([{ 'test' => 'foo' }, { 'test' => 'bar' }])
|
|
||||||
expect(results[:memory]).to eq({ 'last_status' => 'ok', 'dry_run' => true })
|
|
||||||
end
|
|
||||||
|
|
||||||
it "does not perform dry-run if Agent does not support dry-run" do
|
it "does not perform dry-run if Agent does not support dry-run" do
|
||||||
stub(@agent).can_dry_run? { false }
|
stub(@agent).can_dry_run? { false }
|
||||||
|
|
||||||
|
@ -77,4 +74,36 @@ describe DryRunnable do
|
||||||
expect(results[:events]).to eq([])
|
expect(results[:events]).to eq([])
|
||||||
expect(results[:memory]).to eq({})
|
expect(results[:memory]).to eq({})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "dry_run!" do
|
||||||
|
it "traps any destructive operations during a run" do
|
||||||
|
results = nil
|
||||||
|
|
||||||
|
expect {
|
||||||
|
results = @agent.dry_run!
|
||||||
|
@agent.reload
|
||||||
|
}.not_to change {
|
||||||
|
[@agent.memory, counts]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(results[:log]).to match(/\AI, .+ INFO -- : Logging\nE, .+ ERROR -- : Recording error\n/)
|
||||||
|
expect(results[:events]).to eq([{ 'test' => 'foo' }, { 'test' => 'bar' }])
|
||||||
|
expect(results[:memory]).to eq({ 'last_status' => 'ok', 'dry_run' => true })
|
||||||
|
end
|
||||||
|
|
||||||
|
it "traps any destructive operations during a run when an event is given" do
|
||||||
|
results = nil
|
||||||
|
|
||||||
|
expect {
|
||||||
|
results = @agent.dry_run!(Event.new(payload: { 'prefix' => 'super' }))
|
||||||
|
@agent.reload
|
||||||
|
}.not_to change {
|
||||||
|
[@agent.memory, counts]
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(results[:log]).to match(/\AI, .+ INFO -- : Logging\nE, .+ ERROR -- : Recording error\n/)
|
||||||
|
expect(results[:events]).to eq([{ 'test' => 'superfoo' }, { 'test' => 'superbar' }])
|
||||||
|
expect(results[:memory]).to eq({ 'last_status' => 'ok', 'dry_run' => true })
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -377,6 +377,19 @@ describe AgentsController do
|
||||||
[users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count, agent.name, agent.updated_at]
|
[users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count, agent.name, agent.updated_at]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "accepts an event" do
|
||||||
|
sign_in users(:bob)
|
||||||
|
agent = agents(:bob_website_agent)
|
||||||
|
url_from_event = "http://xkcd.com/?from_event=1".freeze
|
||||||
|
expect {
|
||||||
|
post :dry_run, id: agent, event: { url: url_from_event }
|
||||||
|
}.not_to change {
|
||||||
|
[users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count, agent.name, agent.updated_at]
|
||||||
|
}
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json['log']).to match(/^I, .* : Fetching #{Regexp.quote(url_from_event)}$/)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "DELETE memory" do
|
describe "DELETE memory" do
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue