From 5f92b38f6efa664774b1a01ab932937942e4bf8a Mon Sep 17 00:00:00 2001 From: Dominik Sander Date: Mon, 31 Jul 2017 22:11:19 +0200 Subject: [PATCH] Add more options to the GrowlAgent It now additionally allows to use `sticky`, `priority` and `callback_url`. The Agent is now `FormConfigurable` and supports Liquid in all options. `register_growl` had to be moved inside of the `events` loop to allow Liquid interpolation for the previously static `growl_*` options. #2018 --- app/models/agents/growl_agent.rb | 65 ++++++++++++++----- ...731191002_migrate_growl_agent_to_liquid.rb | 18 +++++ spec/models/agents/growl_agent_spec.rb | 45 +++++++------ 3 files changed, 89 insertions(+), 39 deletions(-) create mode 100644 db/migrate/20170731191002_migrate_growl_agent_to_liquid.rb diff --git a/app/models/agents/growl_agent.rb b/app/models/agents/growl_agent.rb index b2274802..110561ba 100644 --- a/app/models/agents/growl_agent.rb +++ b/app/models/agents/growl_agent.rb @@ -1,9 +1,11 @@ module Agents class GrowlAgent < Agent + include FormConfigurable attr_reader :growler cannot_be_scheduled! cannot_create_events! + can_dry_run! gem_dependency_check { defined?(Growl) } @@ -11,10 +13,16 @@ module Agents The Growl Agent sends any events it receives to a Growl GNTP server immediately. #{'## Include `ruby-growl` in your Gemfile to use this Agent!' if dependencies_missing?} - - It is assumed that events have a `message` or `text` key, which will hold the body of the growl notification, and a `subject` key, which will have the headline of the Growl notification. You can use Event Formatting Agent if your event does not provide these keys. - Set `expected_receive_period_in_days` to the maximum amount of time that you'd expect to pass between Events being received by this Agent. + The option `message`, which will hold the body of the growl notification, and the `subject` option, + which will have the headline of the Growl notification are required. All other options are optional. + When `callback_url` is set to a URL clicking on the notification will open the link in your default browser. + + Set `expected_receive_period_in_days` to the maximum amount of time that you'd expect to pass between + Events being received by this Agent. + + Have a look at the [Wiki](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) to learn + more about liquid templating. MD def default_options @@ -23,12 +31,27 @@ module Agents 'growl_password' => '', 'growl_app_name' => 'HuginnGrowl', 'growl_notification_name' => 'Notification', - 'expected_receive_period_in_days' => "2" + 'expected_receive_period_in_days' => "2", + 'subject' => '{{subject}}', + 'message' => '{{message}}', + 'sticky' => 'false', + 'priority' => '0' } end - + + form_configurable :growl_server + form_configurable :growl_password + form_configurable :growl_app_name + form_configurable :growl_notification_name + form_configurable :expected_receive_period_in_days + form_configurable :subject + form_configurable :message, type: :text + form_configurable :sticky, type: :boolean + form_configurable :priority + form_configurable :callback_url + def working? - last_receive_at && last_receive_at > interpolated['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? + last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs? end def validate_options @@ -38,25 +61,31 @@ module Agents end def register_growl - @growler = Growl.new interpolated['growl_server'], interpolated['growl_app_name'], "GNTP" + @growler = Growl::GNTP.new(interpolated['growl_server'], interpolated['growl_app_name']) @growler.password = interpolated['growl_password'] - @growler.add_notification interpolated['growl_notification_name'] + @growler.add_notification(interpolated['growl_notification_name']) end - def notify_growl(subject, message) - @growler.notify(interpolated['growl_notification_name'], subject, message) + def notify_growl(subject:, message:, priority:, sticky:, callback_url:) + @growler.notify(interpolated['growl_notification_name'], subject, message, priority, sticky, nil, callback_url) end def receive(incoming_events) - register_growl incoming_events.each do |event| - message = (event.payload['message'] || event.payload['text']).to_s - subject = event.payload['subject'].to_s - if message.present? && subject.present? - log "Sending Growl notification '#{subject}': '#{message}' to #{interpolated(event)['growl_server']} with event #{event.id}" - notify_growl(subject,message) - else - log "Event #{event.id} not sent, message and subject expected" + interpolate_with(event) do + register_growl + message = interpolated[:message] + subject = interpolated[:subject] + if message.present? && subject.present? + log "Sending Growl notification '#{subject}': '#{message}' to #{interpolated(event)['growl_server']} with event #{event.id}" + notify_growl(subject: subject, + message: message, + priority: interpolated[:priority].to_i, + sticky: boolify(interpolated[:sticky]) || false, + callback_url: interpolated[:callback_url].presence) + else + log "Event #{event.id} not sent, message and subject expected" + end end end end diff --git a/db/migrate/20170731191002_migrate_growl_agent_to_liquid.rb b/db/migrate/20170731191002_migrate_growl_agent_to_liquid.rb new file mode 100644 index 00000000..2a75e0ba --- /dev/null +++ b/db/migrate/20170731191002_migrate_growl_agent_to_liquid.rb @@ -0,0 +1,18 @@ +class MigrateGrowlAgentToLiquid < ActiveRecord::Migration[5.1] + def up + Agents::GrowlAgent.find_each do |agent| + agent.options['subject'] = '{{subject}}' if agent.options['subject'].blank? + agent.options['message'] = '{{ message | default: text }}' if agent.options['message'].blank? + agent.save(validate: false) + end + end + + def down + Agents::GrowlAgent.find_each do |agent| + %w(subject message sticky priority).each do |key| + agent.options.delete(key) + end + agent.save(validate: false) + end + end +end diff --git a/spec/models/agents/growl_agent_spec.rb b/spec/models/agents/growl_agent_spec.rb index 2ae222fc..84a65f15 100644 --- a/spec/models/agents/growl_agent_spec.rb +++ b/spec/models/agents/growl_agent_spec.rb @@ -7,11 +7,13 @@ describe Agents::GrowlAgent do :growl_app_name => 'HuginnGrowlApp', :growl_password => 'mypassword', :growl_notification_name => 'Notification', - :expected_receive_period_in_days => '1' }) + expected_receive_period_in_days: '1' , + message: '{{message}}', + subject: '{{subject}}'}) @checker.user = users(:bob) @checker.save! - - stub.any_instance_of(Growl).notify + + stub.any_instance_of(Growl::GNTP).notify @event = Event.new @event.agent = agents(:bob_weather_agent) @@ -45,7 +47,7 @@ describe Agents::GrowlAgent do expect(@checker).not_to be_valid end end - + describe "register_growl" do it "should set the password for the Growl connection from the agent options" do @checker.register_growl @@ -54,34 +56,34 @@ describe Agents::GrowlAgent do it "should add a notification to the Growl connection" do called = false - any_instance_of(Growl) do |obj| + any_instance_of(Growl::GNTP) do |obj| called = true mock(obj).add_notification(@checker.options[:growl_notification_name]) end - + @checker.register_growl expect(called).to be_truthy end end - + describe "notify_growl" do before do @checker.register_growl end - + it "should call Growl.notify with the correct notification name, subject, and message" do message = "message" subject = "subject" called = false - any_instance_of(Growl) do |obj| + any_instance_of(Growl::GNTP) do |obj| called = true - mock(obj).notify(@checker.options[:growl_notification_name],subject,message) + mock(obj).notify(@checker.options[:growl_notification_name], subject, message, 0, false, nil, '') end - @checker.notify_growl(subject,message) + @checker.notify_growl(subject: subject, message: message, sticky: false, priority: 0, callback_url: '') expect(called).to be_truthy end end - + describe "receive" do def generate_events_array events = [] @@ -90,31 +92,32 @@ describe Agents::GrowlAgent do end return events end - - it "should call register_growl once regardless of number of events received" do - mock.proxy(@checker).register_growl.once - @checker.receive(generate_events_array) + + it "should call register_growl once per received event" do + events = generate_events_array + mock.proxy(@checker).register_growl.times(events.length) + @checker.receive(events) end - + it "should call notify_growl one time for each event received" do events = generate_events_array events.each do |event| - mock.proxy(@checker).notify_growl(event.payload['subject'], event.payload['message']) + mock.proxy(@checker).notify_growl(subject: event.payload['subject'], message: event.payload['message'], priority: 0, sticky: false, callback_url: nil) end @checker.receive(events) end - + it "should not call notify_growl if message or subject are missing" do event_without_a_subject = Event.new event_without_a_subject.agent = agents(:bob_weather_agent) event_without_a_subject.payload = { :message => 'Looks like its going to rain' } event_without_a_subject.save! - + event_without_a_message = Event.new event_without_a_message.agent = agents(:bob_weather_agent) event_without_a_message.payload = { :subject => 'Weather Alert YO!' } event_without_a_message.save! - + mock.proxy(@checker).notify_growl.never @checker.receive([event_without_a_subject,event_without_a_message]) end