Merge pull request #114 from albertsun/event-email-agent

Event email agent

Andrew Cantino 11 年 前
コミット
744ec91b37
共有5 個のファイルを変更した136 個の追加27 個の削除を含む
  1. 37 0
      app/concerns/email_concern.rb
  2. 7 27
      app/models/agents/digest_email_agent.rb
  3. 32 0
      app/models/agents/email_agent.rb
  4. 1 0
      spec/models/agents/digest_email_agent_spec.rb
  5. 59 0
      spec/models/agents/email_agent_spec.rb

+ 37 - 0
app/concerns/email_concern.rb

@@ -0,0 +1,37 @@
1
+module EmailConcern
2
+  extend ActiveSupport::Concern
3
+
4
+  MAIN_KEYS = %w[title message text main value].map(&:to_sym)
5
+
6
+  included do
7
+    self.validate :validate_email_options
8
+  end
9
+
10
+  def validate_email_options
11
+    errors.add(:base, "subject and expected_receive_period_in_days are required") unless options[:subject].present? && options[:expected_receive_period_in_days].present?
12
+  end
13
+
14
+  def working?
15
+    last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs?
16
+  end
17
+
18
+  def present(payload)
19
+    if payload.is_a?(Hash)
20
+      payload = ActiveSupport::HashWithIndifferentAccess.new(payload)
21
+      MAIN_KEYS.each do |key|
22
+        return { :title => payload[key].to_s, :entries => present_hash(payload, key) } if payload.has_key?(key)
23
+      end
24
+
25
+      { :title => "Event", :entries => present_hash(payload) }
26
+    else
27
+      { :title => payload.to_s, :entries => [] }
28
+    end
29
+  end
30
+
31
+  def present_hash(hash, skip_key = nil)
32
+    hash.to_a.sort_by {|a| a.first.to_s }.map { |k, v| "#{k}: #{v}" unless k.to_s == skip_key.to_s }.compact
33
+  end
34
+
35
+  module ClassMethods
36
+  end
37
+end

+ 7 - 27
app/models/agents/digest_email_agent.rb

@@ -1,6 +1,7 @@
1 1
 module Agents
2 2
   class DigestEmailAgent < Agent
3
-    MAIN_KEYS = %w[title message text main value].map(&:to_sym)
3
+    include EmailConcern
4
+
4 5
     default_schedule "5am"
5 6
 
6 7
     cannot_create_events!
@@ -22,45 +23,24 @@ module Agents
22 23
       }
23 24
     end
24 25
 
25
-    def working?
26
-      last_receive_at && last_receive_at > options[:expected_receive_period_in_days].to_i.days.ago && !recent_error_logs?
27
-    end
28
-
29
-    def validate_options
30
-      errors.add(:base, "subject and expected_receive_period_in_days are required") unless options[:subject].present? && options[:expected_receive_period_in_days].present?
31
-    end
32
-
33 26
     def receive(incoming_events)
34 27
       incoming_events.each do |event|
35 28
         self.memory[:queue] ||= []
36 29
         self.memory[:queue] << event.payload
30
+        self.memory[:events] ||= []
31
+        self.memory[:events] << event.id
37 32
       end
38 33
     end
39 34
 
40 35
     def check
41 36
       if self.memory[:queue] && self.memory[:queue].length > 0
37
+        ids = self.memory[:events].join(",")
42 38
         groups = self.memory[:queue].map { |payload| present(payload) }
43
-        log "Sending digest mail to #{user.email}"
39
+        log "Sending digest mail to #{user.email} with events [#{ids}]"
44 40
         SystemMailer.delay.send_message(:to => user.email, :subject => options[:subject], :headline => options[:headline], :groups => groups)
45 41
         self.memory[:queue] = []
42
+        self.memory[:events] = []
46 43
       end
47 44
     end
48
-
49
-    def present(payload)
50
-      if payload.is_a?(Hash)
51
-        payload = ActiveSupport::HashWithIndifferentAccess.new(payload)
52
-        MAIN_KEYS.each do |key|
53
-          return { :title => payload[key].to_s, :entries => present_hash(payload, key) } if payload.has_key?(key)
54
-        end
55
-
56
-        { :title => "Event", :entries => present_hash(payload) }
57
-      else
58
-        { :title => payload.to_s, :entries => [] }
59
-      end
60
-    end
61
-
62
-    def present_hash(hash, skip_key = nil)
63
-      hash.to_a.sort_by {|a| a.first.to_s }.map { |k, v| "#{k}: #{v}" unless k.to_s == skip_key.to_s }.compact
64
-    end
65 45
   end
66 46
 end

+ 32 - 0
app/models/agents/email_agent.rb

@@ -0,0 +1,32 @@
1
+module Agents
2
+  class EmailAgent < Agent
3
+    include EmailConcern
4
+
5
+    cannot_be_scheduled!
6
+    cannot_create_events!
7
+
8
+    description <<-MD
9
+      The EmailAgent sends any events it receives via email immediately.
10
+      The email will be sent to your account's address and will have a `subject` and an optional `headline` before
11
+      listing the Events.  If the Events' payloads contain a `:message`, that will be highlighted, otherwise everything in
12
+      their payloads will be shown.
13
+
14
+      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.
15
+    MD
16
+
17
+    def default_options
18
+      {
19
+          :subject => "You have a notification!",
20
+          :headline => "Your notification:",
21
+          :expected_receive_period_in_days => "2"
22
+      }
23
+    end
24
+
25
+    def receive(incoming_events)
26
+      incoming_events.each do |event|
27
+        log "Sending digest mail to #{user.email} with event #{event.id}"
28
+        SystemMailer.delay.send_message(:to => user.email, :subject => options[:subject], :headline => options[:headline], :groups => [present(event.payload)])
29
+      end
30
+    end
31
+  end
32
+end

+ 1 - 0
spec/models/agents/digest_email_agent_spec.rb

@@ -41,6 +41,7 @@ describe Agents::DigestEmailAgent do
41 41
                                  { :title => "Foo", :url => "http://google.com", :bar => 2 },
42 42
                                  { "message" => "hi", :woah => "there" },
43 43
                                  { "test" => 2 }]
44
+      @checker.memory[:events] = [1,2,3,4]
44 45
       @checker.save!
45 46
 
46 47
       Agents::DigestEmailAgent.async_check(@checker.id)

+ 59 - 0
spec/models/agents/email_agent_spec.rb

@@ -0,0 +1,59 @@
1
+require 'spec_helper'
2
+
3
+describe Agents::EmailAgent do
4
+  def get_message_part(mail, content_type)
5
+    mail.body.parts.find { |p| p.content_type.match content_type }.body.raw_source
6
+  end
7
+
8
+  before do
9
+    @checker = Agents::EmailAgent.new(:name => "something", :options => { :expected_receive_period_in_days => 2, :subject => "something interesting" })
10
+    @checker.user = users(:bob)
11
+    @checker.save!
12
+  end
13
+
14
+  after do
15
+    ActionMailer::Base.deliveries = []
16
+  end
17
+
18
+  describe "#receive" do
19
+    it "immediately sends any payloads it receives" do
20
+      ActionMailer::Base.deliveries.should == []
21
+
22
+      event1 = Event.new
23
+      event1.agent = agents(:bob_rain_notifier_agent)
24
+      event1.payload = "Something you should know about"
25
+      event1.save!
26
+
27
+      event2 = Event.new
28
+      event2.agent = agents(:bob_weather_agent)
29
+      event2.payload = "Something else you should know about"
30
+      event2.save!
31
+
32
+      Agents::EmailAgent.async_receive(@checker.id, [event1.id])
33
+      Agents::EmailAgent.async_receive(@checker.id, [event2.id])
34
+
35
+      ActionMailer::Base.deliveries.count.should == 2
36
+      ActionMailer::Base.deliveries.last.to.should == ["bob@example.com"]
37
+      ActionMailer::Base.deliveries.last.subject.should == "something interesting"
38
+      get_message_part(ActionMailer::Base.deliveries.last, /plain/).strip.should == "Something else you should know about"
39
+      get_message_part(ActionMailer::Base.deliveries.first, /plain/).strip.should == "Something you should know about"
40
+    end
41
+
42
+    it "can receive complex events and send them on" do
43
+      stub_request(:any, /wunderground/).to_return(:body => File.read(Rails.root.join("spec/data_fixtures/weather.json")), :status => 200)
44
+      stub.any_instance_of(Agents::WeatherAgent).is_tomorrow?(anything) { true }
45
+      @checker.sources << agents(:bob_weather_agent)
46
+
47
+      Agent.async_check(agents(:bob_weather_agent).id)
48
+
49
+      Agent.receive!
50
+
51
+      plain_email_text = get_message_part(ActionMailer::Base.deliveries.last, /plain/).strip
52
+      html_email_text = get_message_part(ActionMailer::Base.deliveries.last, /html/).strip
53
+
54
+      plain_email_text.should =~ /avehumidity/
55
+      html_email_text.should =~ /avehumidity/
56
+    end
57
+
58
+  end
59
+end