Merge pull request #238 from cantino/upgraded_post_agent

Allow PostAgents to specify a default hash of data to be POSTed, as well as to be run periodically.

Andrew Cantino 11 anos atrás
pai
commit
1754ac38f2
2 arquivos alterados com 167 adições e 24 exclusões
  1. 57 10
      app/models/agents/post_agent.rb
  2. 110 14
      spec/models/agents/post_agent_spec.rb

+ 57 - 10
app/models/agents/post_agent.rb

@@ -1,10 +1,13 @@
1 1
 module Agents
2 2
   class PostAgent < Agent
3
-    cannot_be_scheduled!
4 3
     cannot_create_events!
5 4
 
5
+    default_schedule "never"
6
+
6 7
     description <<-MD
7
-       Post Agent receives events from other agents and send those events as the contents of a post request to a specified url. `post_url` field must specify where you would like to receive post requests and do not forget to include URI scheme (`http` or `https`)
8
+      A PostAgent receives events from other agents (or runs periodically), merges those events with the contents of `payload`, and sends the results as POST (or GET) requests to a specified url.
9
+
10
+      The `post_url` field must specify where you would like to send requests. Please include the URI scheme (`http` or `https`).
8 11
     MD
9 12
 
10 13
     event_description "Does not produce events."
@@ -12,7 +15,11 @@ module Agents
12 15
     def default_options
13 16
       {
14 17
         'post_url' => "http://www.example.com",
15
-        'expected_receive_period_in_days' => 1
18
+        'expected_receive_period_in_days' => 1,
19
+        'method' => 'post',
20
+        'payload' => {
21
+          'key' => 'value'
22
+        }
16 23
       }
17 24
     end
18 25
 
@@ -20,23 +27,63 @@ module Agents
20 27
       last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs?
21 28
     end
22 29
 
30
+    def method
31
+      (options['method'].presence || 'post').to_s.downcase
32
+    end
33
+
23 34
     def validate_options
24 35
       unless options['post_url'].present? && options['expected_receive_period_in_days'].present?
25 36
         errors.add(:base, "post_url and expected_receive_period_in_days are required fields")
26 37
       end
27
-    end
28 38
 
29
-    def post_event(uri, event)
30
-      req = Net::HTTP::Post.new(uri.request_uri)
31
-      req.form_data = event
32
-      Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
39
+      if options['payload'].present? && !options['payload'].is_a?(Hash)
40
+        errors.add(:base, "if provided, payload must be a hash")
41
+      end
42
+
43
+      unless %w[post get].include?(method)
44
+        errors.add(:base, "method must be 'post' or 'get'")
45
+      end
33 46
     end
34 47
 
35 48
     def receive(incoming_events)
36 49
       incoming_events.each do |event|
37
-        uri = URI options[:post_url]
38
-        post_event uri, event.payload
50
+        handle (options['payload'].presence || {}).merge(event.payload)
39 51
       end
40 52
     end
53
+
54
+    def check
55
+      handle options['payload'].presence || {}
56
+    end
57
+
58
+    def generate_uri(params = nil)
59
+      uri = URI options[:post_url]
60
+      uri.query = URI.encode_www_form(Hash[URI.decode_www_form(uri.query || '')].merge(params)) if params
61
+      uri
62
+    end
63
+
64
+    private
65
+
66
+    def handle(data)
67
+      if method == 'post'
68
+        post_data(data)
69
+      elsif method == 'get'
70
+        get_data(data)
71
+      else
72
+        error "Invalid method '#{method}'"
73
+      end
74
+    end
75
+
76
+    def post_data(data)
77
+      uri = generate_uri
78
+      req = Net::HTTP::Post.new(uri.request_uri)
79
+      req.form_data = data
80
+      Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
81
+    end
82
+
83
+    def get_data(data)
84
+      uri = generate_uri(data)
85
+      req = Net::HTTP::Get.new(uri.request_uri)
86
+      Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
87
+    end
41 88
   end
42 89
 end

+ 110 - 14
spec/models/agents/post_agent_spec.rb

@@ -5,8 +5,11 @@ describe Agents::PostAgent do
5 5
     @valid_params = {
6 6
       :name => "somename",
7 7
       :options => {
8
-        :post_url => "http://www.example.com",
9
-        :expected_receive_period_in_days => 1
8
+        'post_url' => "http://www.example.com",
9
+        'expected_receive_period_in_days' => 1,
10
+        'payload' => {
11
+          'default' => 'value'
12
+        }
10 13
       }
11 14
     }
12 15
 
@@ -17,28 +20,69 @@ describe Agents::PostAgent do
17 20
     @event = Event.new
18 21
     @event.agent = agents(:jane_weather_agent)
19 22
     @event.payload = {
20
-      :somekey => "somevalue",
21
-      :someotherkey => {
22
-        :somekey => "value"
23
+      'somekey' => 'somevalue',
24
+      'someotherkey' => {
25
+        'somekey' => 'value'
23 26
       }
24 27
     }
25 28
 
26
-    @sent_messages = []
27
-    stub.any_instance_of(Agents::PostAgent).post_event { |uri, event| @sent_messages << event }
29
+    @sent_posts = []
30
+    @sent_gets = []
31
+    stub.any_instance_of(Agents::PostAgent).post_data { |data| @sent_posts << data }
32
+    stub.any_instance_of(Agents::PostAgent).get_data { |data| @sent_gets << data }
28 33
   end
29 34
 
30 35
   describe "#receive" do
31
-    it "checks if it can handle multiple events" do
36
+    it "can handle multiple events and merge the payloads with options['payload']" do
32 37
       event1 = Event.new
33 38
       event1.agent = agents(:bob_weather_agent)
34 39
       event1.payload = {
35
-        :xyz => "value1",
36
-        :message => "value2"
40
+        'xyz' => 'value1',
41
+        'message' => 'value2',
42
+        'default' => 'value2'
37 43
       }
38 44
 
39 45
       lambda {
40
-        @checker.receive([@event, event1])
41
-      }.should change { @sent_messages.length }.by(2)
46
+        lambda {
47
+          @checker.receive([@event, event1])
48
+        }.should change { @sent_posts.length }.by(2)
49
+      }.should_not change { @sent_gets.length }
50
+
51
+      @sent_posts[0].should == @event.payload.merge('default' => 'value')
52
+      @sent_posts[1].should == event1.payload
53
+    end
54
+
55
+    it "can make GET requests" do
56
+      @checker.options['method'] = 'get'
57
+
58
+      lambda {
59
+        lambda {
60
+          @checker.receive([@event])
61
+        }.should change { @sent_gets.length }.by(1)
62
+      }.should_not change { @sent_posts.length }
63
+
64
+      @sent_gets[0].should == @event.payload.merge('default' => 'value')
65
+    end
66
+  end
67
+
68
+  describe "#check" do
69
+    it "sends options['payload'] as a POST request" do
70
+      lambda {
71
+        @checker.check
72
+      }.should change { @sent_posts.length }.by(1)
73
+
74
+      @sent_posts[0].should == @checker.options['payload']
75
+    end
76
+
77
+    it "sends options['payload'] as a GET request" do
78
+      @checker.options['method'] = 'get'
79
+      lambda {
80
+        lambda {
81
+          @checker.check
82
+        }.should change { @sent_gets.length }.by(1)
83
+      }.should_not change { @sent_posts.length }
84
+
85
+      @sent_gets[0].should == @checker.options['payload']
42 86
     end
43 87
   end
44 88
 
@@ -59,13 +103,65 @@ describe Agents::PostAgent do
59 103
     end
60 104
 
61 105
     it "should validate presence of post_url" do
62
-      @checker.options[:post_url] = ""
106
+      @checker.options['post_url'] = ""
63 107
       @checker.should_not be_valid
64 108
     end
65 109
 
66 110
     it "should validate presence of expected_receive_period_in_days" do
67
-      @checker.options[:expected_receive_period_in_days] = ""
111
+      @checker.options['expected_receive_period_in_days'] = ""
112
+      @checker.should_not be_valid
113
+    end
114
+
115
+    it "should validate method as post or get, defaulting to post" do
116
+      @checker.options['method'] = ""
117
+      @checker.method.should == "post"
118
+      @checker.should be_valid
119
+
120
+      @checker.options['method'] = "POST"
121
+      @checker.method.should == "post"
122
+      @checker.should be_valid
123
+
124
+      @checker.options['method'] = "get"
125
+      @checker.method.should == "get"
126
+      @checker.should be_valid
127
+
128
+      @checker.options['method'] = "wut"
129
+      @checker.method.should == "wut"
130
+      @checker.should_not be_valid
131
+    end
132
+
133
+    it "should validate payload as a hash, if present" do
134
+      @checker.options['payload'] = ""
135
+      @checker.should be_valid
136
+
137
+      @checker.options['payload'] = "hello"
138
+      @checker.should_not be_valid
139
+
140
+      @checker.options['payload'] = ["foo", "bar"]
68 141
       @checker.should_not be_valid
142
+
143
+      @checker.options['payload'] = { 'this' => 'that' }
144
+      @checker.should be_valid
145
+    end
146
+  end
147
+
148
+  describe "#generate_uri" do
149
+    it "merges params with any in the post_url" do
150
+      @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value"
151
+      uri = @checker.generate_uri("some_param" => "some_value", "another_param" => "another_value")
152
+      uri.request_uri.should == "/a/path?existing_param=existing_value&some_param=some_value&another_param=another_value"
153
+    end
154
+
155
+    it "works fine with urls that do not have a query" do
156
+      @checker.options['post_url'] = "http://example.com/a/path"
157
+      uri = @checker.generate_uri("some_param" => "some_value", "another_param" => "another_value")
158
+      uri.request_uri.should == "/a/path?some_param=some_value&another_param=another_value"
159
+    end
160
+
161
+    it "just returns the post_uri when no params are given" do
162
+      @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value"
163
+      uri = @checker.generate_uri
164
+      uri.request_uri.should == "/a/path?existing_param=existing_value"
69 165
     end
70 166
   end
71 167
 end