diff --git a/app/assets/javascripts/components/utils.js.coffee b/app/assets/javascripts/components/utils.js.coffee
index 605d47df..445d239e 100644
--- a/app/assets/javascripts/components/utils.js.coffee
+++ b/app/assets/javascripts/components/utils.js.coffee
@@ -43,90 +43,65 @@ class @Utils
if with_event_mode is 'no'
return @invokeDryRun(url, data, cleanup)
+ $.ajax url,
+ method: 'GET',
+ data:
+ with_event_mode: with_event_mode
+ source_ids: $.map($(".link-region select option:selected"), (el) -> $(el).val() )
+ success: (modal_data) =>
+ Utils.showDynamicModal modal_data,
+ body: (body) =>
+ form = $(body).find('.dry-run-form')
+ payload_editor = form.find('.payload-editor')
- Utils.showDynamicModal """
-
Event to send#{if with_event_mode is 'maybe' then ' (Optional)' else ''}
-
- """,
- 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')
- $(body).closest('[role=dialog]').on 'shown.bs.modal', ->
- $(this).find('.btn-primary').focus()
- title: 'Dry Run'
- onHide: cleanup
+ if previous = $(button).data('payload')
+ payload_editor.text(previous)
+
+ editor = window.setupJsonEditor(payload_editor)[0]
+
+ $(body).find('.dry-run-event-sample').click (e) =>
+ e.preventDefault()
+ editor.json = $(e.currentTarget).data('payload')
+ editor.rebuild()
+
+ 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')
+ $(body).closest('[role=dialog]').on 'shown.bs.modal', ->
+ $(this).find('.btn-primary').focus()
+ title: 'Dry Run'
+ onHide: cleanup
@invokeDryRun: (url, data, callback) ->
$('body').css(cursor: 'progress')
- $.ajax type: 'POST', url: url, dataType: 'json', data: data
+ $.ajax type: 'POST', url: url, dataType: 'html', data: data
.always =>
$('body').css(cursor: 'auto')
- .done (json) =>
- Utils.showDynamicModal """
-
-
-
-
- """,
- body: (body) ->
- $(body).
- find('.agent-dry-run-log').text(json.log).end().
- find('.agent-dry-run-events').text(json.events).end().
- find('.agent-dry-run-memory').text(json.memory)
- active = if json.events.match(/^\[?\s*\]?$/) then 'tabLog' else 'tabEvents'
- $('#resultTabs a[href="#' + active + '"]').tab('show')
+ .done (modal_data) =>
+ Utils.showDynamicModal modal_data,
title: 'Dry Run Results',
onHide: callback
.fail (xhr, status, error) ->
diff --git a/app/controllers/agents/dry_runs_controller.rb b/app/controllers/agents/dry_runs_controller.rb
new file mode 100644
index 00000000..190bc99c
--- /dev/null
+++ b/app/controllers/agents/dry_runs_controller.rb
@@ -0,0 +1,50 @@
+module Agents
+ class DryRunsController < ApplicationController
+ include ActionView::Helpers::TextHelper
+
+ def index
+ @events = if params[:agent_id]
+ current_user.agents.find_by(id: params[:agent_id]).received_events.limit(5)
+ elsif params[:source_ids]
+ Event.where(agent_id: current_user.agents.where(id: params[:source_ids]).pluck(:id))
+ .order("id DESC").limit(5)
+ end
+
+ render layout: false
+ end
+
+ def create
+ attrs = params[:agent] || {}
+ if agent = current_user.agents.find_by(id: params[:agent_id])
+ # POST /agents/:id/dry_run
+ if attrs.present?
+ type = agent.type
+ agent = Agent.build_for_type(type, current_user, attrs)
+ end
+ else
+ # POST /agents/dry_run
+ type = attrs.delete(:type)
+ agent = Agent.build_for_type(type, current_user, attrs)
+ end
+ agent.name ||= '(Untitled)'
+
+ if agent.valid?
+ 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)
+ else
+ @results = { events: [], memory: [],
+ log: [
+ "#{pluralize(agent.errors.count, "error")} prohibited this Agent from being saved:",
+ *agent.errors.full_messages
+ ].join("\n- ") }
+ end
+
+ render layout: false
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/agents_controller.rb b/app/controllers/agents_controller.rb
index 3f935869..7adda59f 100644
--- a/app/controllers/agents_controller.rb
+++ b/app/controllers/agents_controller.rb
@@ -48,47 +48,6 @@ class AgentsController < ApplicationController
end
end
- def dry_run
- attrs = params[:agent] || {}
- if agent = current_user.agents.find_by(id: params[:id])
- # POST /agents/:id/dry_run
- if attrs.present?
- type = agent.type
- agent = Agent.build_for_type(type, current_user, attrs)
- end
- else
- # POST /agents/dry_run
- type = attrs.delete(:type)
- agent = Agent.build_for_type(type, current_user, attrs)
- end
- agent.name ||= '(Untitled)'
-
- if agent.valid?
- 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: {
- log: results[:log],
- events: Utils.pretty_print(results[:events], false),
- memory: Utils.pretty_print(results[:memory] || {}, false),
- }
- else
- render json: {
- log: [
- "#{pluralize(agent.errors.count, "error")} prohibited this Agent from being saved:",
- *agent.errors.full_messages
- ].join("\n- "),
- events: '',
- memory: '',
- }
- end
- end
-
def type_details
@agent = Agent.build_for_type(params[:type], current_user, {})
initialize_presenter
diff --git a/app/views/agents/_action_menu.html.erb b/app/views/agents/_action_menu.html.erb
index e5fd31c7..80d5274a 100644
--- a/app/views/agents/_action_menu.html.erb
+++ b/app/views/agents/_action_menu.html.erb
@@ -7,7 +7,7 @@
<% if agent.can_dry_run? %>
- <%= 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)" %>
+ <%= link_to icon_tag('glyphicon-refresh') + ' Dry Run', '#', 'data-action-url' => agent_dry_runs_path(agent), 'data-with-event-mode' => agent_dry_run_with_event_mode(agent), tabindex: "-1", onclick: "Utils.handleDryRunButton(this)" %>
<% end %>
diff --git a/app/views/agents/_options.erb b/app/views/agents/_options.erb
index 4d474787..3bff9071 100644
--- a/app/views/agents/_options.erb
+++ b/app/views/agents/_options.erb
@@ -25,6 +25,6 @@
<%= submit_tag "Save", :class => "btn btn-primary" %>
<% 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, 'data-with-event-mode' => agent_dry_run_with_event_mode(agent) 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? ? agent_dry_runs_path(agent) : dry_runs_path(type: agent.type), 'data-with-event-mode' => agent_dry_run_with_event_mode(agent) do %><%= icon_tag('glyphicon-refresh') %> Dry Run<% end %>
<% end %>
diff --git a/app/views/agents/dry_runs/create.html.erb b/app/views/agents/dry_runs/create.html.erb
new file mode 100644
index 00000000..22494a4e
--- /dev/null
+++ b/app/views/agents/dry_runs/create.html.erb
@@ -0,0 +1,18 @@
+
+
+
+
+
+
<%= Utils.pretty_print(@results[:events], false) %>
+
+
+
<%= @results[:log] %>
+
+
+
<%= Utils.pretty_print(@results[:memory], false) %>
+
+
diff --git a/app/views/agents/dry_runs/index.html.erb b/app/views/agents/dry_runs/index.html.erb
new file mode 100644
index 00000000..d9e5bf11
--- /dev/null
+++ b/app/views/agents/dry_runs/index.html.erb
@@ -0,0 +1,20 @@
+<% if @events && @events.length > 0 %>
+ Recently received events:
+ <% @events.each do |event| %>
+ <%= link_to '#', class: 'dry-run-event-sample', 'data-payload' => event.payload.to_json do %>
+ <%= truncate event.payload.to_json, :length => 90, :omission => "" %>
+ <% end %>
+ <% end %>
+<% end %>
+
+Event to send<%= params[:with_event_mode] == 'maybe' ? ' (Optional)' : '' %>
+
diff --git a/config/routes.rb b/config/routes.rb
index 5cb2e3e2..3cb30969 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,7 +2,6 @@ Huginn::Application.routes.draw do
resources :agents do
member do
post :run
- post :dry_run
post :handle_details_post
put :leave_scenario
delete :remove_events
@@ -13,7 +12,6 @@ Huginn::Application.routes.draw do
put :toggle_visibility
post :propagate
get :type_details
- post :dry_run
get :event_descriptions
post :validate
post :complete
@@ -26,6 +24,14 @@ Huginn::Application.routes.draw do
end
resources :events, :only => [:index]
+
+ scope module: :agents do
+ resources :dry_runs, only: [:index, :create]
+ end
+ end
+
+ scope module: :agents do
+ resources :dry_runs, only: [:index, :create]
end
resource :diagram, :only => [:show]
diff --git a/spec/controllers/agents/dry_runs_controller_spec.rb b/spec/controllers/agents/dry_runs_controller_spec.rb
new file mode 100644
index 00000000..ca5ce023
--- /dev/null
+++ b/spec/controllers/agents/dry_runs_controller_spec.rb
@@ -0,0 +1,104 @@
+require 'rails_helper'
+
+describe Agents::DryRunsController do
+ def valid_attributes(options = {})
+ {
+ type: "Agents::WebsiteAgent",
+ name: "Something",
+ options: agents(:bob_website_agent).options,
+ source_ids: [agents(:bob_weather_agent).id, ""]
+ }.merge(options)
+ end
+
+ before do
+ sign_in users(:bob)
+ end
+
+ describe "GET index" do
+ it "does not load any events without specifing sources" do
+ get :index, type: 'Agents::WebsiteAgent', source_ids: []
+ expect(assigns(:events)).to eq([])
+ end
+
+ context "does not load events when the agent is owned by a different user" do
+ before do
+ @agent = agents(:jane_website_agent)
+ @agent.sources << @agent
+ @agent.save!
+ expect(@agent.events.count).not_to be(0)
+ end
+
+ it "for new agents" do
+ get :index, type: 'Agents::WebsiteAgent', source_ids: [@agent.id]
+ expect(assigns(:events)).to eq([])
+ end
+
+ it "for existing agents" do
+ expect(@agent.events.count).not_to be(0)
+ expect { get :index, agent_id: @agent }.to raise_error(NoMethodError)
+ end
+ end
+
+ context "loads the most recent events" do
+ before do
+ @agent = agents(:bob_website_agent)
+ @agent.sources << @agent
+ @agent.save!
+ end
+
+ it "load the most recent events when providing source ids" do
+ get :index, type: 'Agents::WebsiteAgent', source_ids: [@agent.id]
+ expect(assigns(:events)).to eq([@agent.events.first])
+ end
+
+ it "loads the most recent events for a saved agent" do
+ get :index, agent_id: @agent
+ expect(assigns(:events)).to eq([@agent.events.first])
+ end
+ end
+ end
+
+ describe "POST create" do
+ before do
+ stub_request(:any, /xkcd/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/xkcd.html")), status: 200)
+ end
+
+ it "does not actually create any agent, event or log" do
+ expect {
+ post :create, agent: valid_attributes
+ }.not_to change {
+ [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count]
+ }
+ results = assigns(:results)
+ expect(results[:log]).to be_a(String)
+ expect(results[:log]).to include('Extracting html at')
+ expect(results[:events]).to be_a(Array)
+ expect(results[:events].length).to eq(1)
+ expect(results[:events].map(&:class)).to eq([ActiveSupport::HashWithIndifferentAccess])
+ expect(results[:memory]).to be_a(Hash)
+ end
+
+ it "does not actually update an agent" do
+ agent = agents(:bob_weather_agent)
+ expect {
+ post :create, agent_id: agent, agent: valid_attributes(name: 'New Name')
+ }.not_to change {
+ [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count, agent.name, agent.updated_at]
+ }
+ end
+
+ it "accepts an event" do
+ agent = agents(:bob_website_agent)
+ agent.options['url_from_event'] = '{{ url }}'
+ agent.save!
+ url_from_event = "http://xkcd.com/?from_event=1".freeze
+ expect {
+ post :create, agent_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]
+ }
+ results = assigns(:results)
+ expect(results[:log]).to match(/^\[\d\d:\d\d:\d\d\] INFO -- : Fetching #{Regexp.quote(url_from_event)}$/)
+ end
+ end
+end
diff --git a/spec/controllers/agents_controller_spec.rb b/spec/controllers/agents_controller_spec.rb
index de7432b7..e8f758bf 100644
--- a/spec/controllers/agents_controller_spec.rb
+++ b/spec/controllers/agents_controller_spec.rb
@@ -408,52 +408,6 @@ describe AgentsController do
end
end
- describe "POST dry_run" do
- before do
- stub_request(:any, /xkcd/).to_return(body: File.read(Rails.root.join("spec/data_fixtures/xkcd.html")), status: 200)
- end
-
- it "does not actually create any agent, event or log" do
- sign_in users(:bob)
- expect {
- post :dry_run, agent: valid_attributes()
- }.not_to change {
- [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count]
- }
- json = JSON.parse(response.body)
- expect(json['log']).to be_a(String)
- expect(json['events']).to be_a(String)
- expect(JSON.parse(json['events']).map(&:class)).to eq([Hash])
- expect(json['memory']).to be_a(String)
- expect(JSON.parse(json['memory'])).to be_a(Hash)
- end
-
- it "does not actually update an agent" do
- sign_in users(:bob)
- agent = agents(:bob_weather_agent)
- expect {
- post :dry_run, id: agent, agent: valid_attributes(name: 'New Name')
- }.not_to change {
- [users(:bob).agents.count, users(:bob).events.count, users(:bob).logs.count, agent.name, agent.updated_at]
- }
- end
-
- it "accepts an event" do
- sign_in users(:bob)
- agent = agents(:bob_website_agent)
- agent.options['url_from_event'] = '{{ url }}'
- agent.save!
- 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(/^\[\d\d:\d\d:\d\d\] INFO -- : Fetching #{Regexp.quote(url_from_event)}$/)
- end
- end
-
describe "DELETE memory" do
it "clears memory of the agent" do
agent = agents(:bob_website_agent)