Merge pull request #394 from cantino/post_agent_additions

PostAgent Additions

Andrew Cantino 10 anos atrás
pai
commit
fa036646dd

+ 5 - 2
app/assets/javascripts/application.js.coffee.erb

@@ -25,7 +25,10 @@ hideSchedule = ->
25 25
   $(".schedule-region select").hide()
26 26
   $(".schedule-region .cannot-be-scheduled").show()
27 27
 
28
-showSchedule = ->
28
+showSchedule = (defaultSchedule = null) ->
29
+  $(".schedule-region select").show()
30
+  if defaultSchedule?
31
+    $(".schedule-region select").val(defaultSchedule).change()
29 32
   $(".schedule-region select").show()
30 33
   $(".schedule-region .cannot-be-scheduled").hide()
31 34
 
@@ -145,7 +148,7 @@ $(document).ready ->
145 148
       $(".event-descriptions").html("").hide()
146 149
       $.getJSON "/agents/type_details", { type: $(@).val() }, (json) =>
147 150
         if json.can_be_scheduled
148
-          showSchedule()
151
+          showSchedule(json.default_schedule)
149 152
         else
150 153
           hideSchedule()
151 154
 

+ 1 - 0
app/controllers/agents_controller.rb

@@ -34,6 +34,7 @@ class AgentsController < ApplicationController
34 34
     agent = Agent.build_for_type(params[:type], current_user, {})
35 35
     render :json => {
36 36
         :can_be_scheduled => agent.can_be_scheduled?,
37
+        :default_schedule => agent.default_schedule,
37 38
         :can_receive_events => agent.can_receive_events?,
38 39
         :can_create_events => agent.can_create_events?,
39 40
         :options => agent.default_options,

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

@@ -5,10 +5,14 @@ module Agents
5 5
     default_schedule "never"
6 6
 
7 7
     description <<-MD
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.
8
+      A PostAgent receives events from other agents (or runs periodically), merges those events with the [Liquid-interpolated](https://github.com/cantino/huginn/wiki/Formatting-Events-using-Liquid) contents of `payload`, and sends the results as POST (or GET) requests to a specified url.  To skip merging in the incoming event, but still send the interpolated payload, set `no_merge` to `true`.
9 9
 
10 10
       The `post_url` field must specify where you would like to send requests. Please include the URI scheme (`http` or `https`).
11 11
 
12
+      The `method` used can be any of `get`, `post`, `put`, `patch`, and `delete`.
13
+
14
+      By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`).  Change `content_type` to `json` to send JSON instead.
15
+
12 16
       The `headers` field is optional.  When present, it should be a hash of headers to send with the request.
13 17
     MD
14 18
 
@@ -17,10 +21,12 @@ module Agents
17 21
     def default_options
18 22
       {
19 23
         'post_url' => "http://www.example.com",
20
-        'expected_receive_period_in_days' => 1,
24
+        'expected_receive_period_in_days' => '1',
25
+        'content_type' => 'form',
21 26
         'method' => 'post',
22 27
         'payload' => {
23
-          'key' => 'value'
28
+          'key' => 'value',
29
+          'something' => 'the event contained {{ somekey }}'
24 30
         },
25 31
         'headers' => {}
26 32
       }
@@ -47,8 +53,12 @@ module Agents
47 53
         errors.add(:base, "if provided, payload must be a hash")
48 54
       end
49 55
 
50
-      unless %w[post get].include?(method)
51
-        errors.add(:base, "method must be 'post' or 'get'")
56
+      unless %w[post get put delete patch].include?(method)
57
+        errors.add(:base, "method must be 'post', 'get', 'put', 'delete', or 'patch'")
58
+      end
59
+
60
+      if options['no_merge'].present? && !%[true false].include?(options['no_merge'].to_s)
61
+        errors.add(:base, "if provided, no_merge must be 'true' or 'false'")
52 62
       end
53 63
 
54 64
       unless headers.is_a?(Hash)
@@ -58,7 +68,12 @@ module Agents
58 68
 
59 69
     def receive(incoming_events)
60 70
       incoming_events.each do |event|
61
-        handle (interpolated(event.payload)['payload'].presence || {}).merge(event.payload)
71
+        outgoing = interpolated(event.payload)['payload'].presence || {}
72
+        if interpolated['no_merge'].to_s == 'true'
73
+          handle outgoing
74
+        else
75
+          handle outgoing.merge(event.payload)
76
+        end
62 77
       end
63 78
     end
64 79
 
@@ -76,7 +91,13 @@ module Agents
76 91
 
77 92
     def handle(data)
78 93
       if method == 'post'
79
-        post_data(data)
94
+        post_data(data, Net::HTTP::Post)
95
+      elsif method == 'put'
96
+        post_data(data, Net::HTTP::Put)
97
+      elsif method == 'delete'
98
+        post_data(data, Net::HTTP::Delete)
99
+      elsif method == 'patch'
100
+        post_data(data, Net::HTTP::Patch)
80 101
       elsif method == 'get'
81 102
         get_data(data)
82 103
       else
@@ -84,10 +105,17 @@ module Agents
84 105
       end
85 106
     end
86 107
 
87
-    def post_data(data)
108
+    def post_data(data, request_type = Net::HTTP::Post)
88 109
       uri = generate_uri
89
-      req = Net::HTTP::Post.new(uri.request_uri, headers)
90
-      req.form_data = data
110
+      req = request_type.new(uri.request_uri, headers)
111
+
112
+      if interpolated['content_type'] == 'json'
113
+        req.set_content_type('application/json', 'charset' => 'utf-8')
114
+        req.body = data.to_json
115
+      else
116
+        req.form_data = data
117
+      end
118
+
91 119
       Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
92 120
     end
93 121
 

+ 64 - 17
spec/models/agents/post_agent_spec.rb

@@ -25,11 +25,25 @@ describe Agents::PostAgent do
25 25
         'somekey' => 'value'
26 26
       }
27 27
     }
28
+    @requests = 0
29
+    @sent_requests = { Net::HTTP::Get => [], Net::HTTP::Post => [], Net::HTTP::Put => [], Net::HTTP::Delete => [], Net::HTTP::Patch => [] }
28 30
 
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 }
31
+    stub.any_instance_of(Agents::PostAgent).post_data { |data, type| @requests += 1; @sent_requests[type] << data }
32
+    stub.any_instance_of(Agents::PostAgent).get_data { |data| @requests += 1; @sent_requests[Net::HTTP::Get] << data }
33
+  end
34
+
35
+  describe "making requests" do
36
+    it "can make requests of each type" do
37
+      { 'get' => Net::HTTP::Get, 'put' => Net::HTTP::Put,
38
+        'post' => Net::HTTP::Post, 'patch' => Net::HTTP::Patch,
39
+        'delete' => Net::HTTP::Delete }.each.with_index do |(verb, type), index|
40
+        @checker.options['method'] = verb
41
+        @checker.should be_valid
42
+        @checker.check
43
+        @requests.should == index + 1
44
+        @sent_requests[type].length.should == 1
45
+      end
46
+    end
33 47
   end
34 48
 
35 49
   describe "#receive" do
@@ -45,11 +59,11 @@ describe Agents::PostAgent do
45 59
       lambda {
46 60
         lambda {
47 61
           @checker.receive([@event, event1])
48
-        }.should change { @sent_posts.length }.by(2)
49
-      }.should_not change { @sent_gets.length }
62
+        }.should change { @sent_requests[Net::HTTP::Post].length }.by(2)
63
+      }.should_not change { @sent_requests[Net::HTTP::Get].length }
50 64
 
51
-      @sent_posts[0].should == @event.payload.merge('default' => 'value')
52
-      @sent_posts[1].should == event1.payload
65
+      @sent_requests[Net::HTTP::Post][0].should == @event.payload.merge('default' => 'value')
66
+      @sent_requests[Net::HTTP::Post][1].should == event1.payload
53 67
     end
54 68
 
55 69
     it "can make GET requests" do
@@ -58,10 +72,19 @@ describe Agents::PostAgent do
58 72
       lambda {
59 73
         lambda {
60 74
           @checker.receive([@event])
61
-        }.should change { @sent_gets.length }.by(1)
62
-      }.should_not change { @sent_posts.length }
75
+        }.should change { @sent_requests[Net::HTTP::Get].length }.by(1)
76
+      }.should_not change { @sent_requests[Net::HTTP::Post].length }
63 77
 
64
-      @sent_gets[0].should == @event.payload.merge('default' => 'value')
78
+      @sent_requests[Net::HTTP::Get][0].should == @event.payload.merge('default' => 'value')
79
+    end
80
+
81
+    it "can skip merging the incoming event when no_merge is set, but it still interpolates" do
82
+      @checker.options['no_merge'] = 'true'
83
+      @checker.options['payload'] = {
84
+        'key' => 'it said: {{ someotherkey.somekey }}'
85
+      }
86
+      @checker.receive([@event])
87
+      @sent_requests[Net::HTTP::Post].first.should == { 'key' => 'it said: value' }
65 88
     end
66 89
   end
67 90
 
@@ -69,9 +92,9 @@ describe Agents::PostAgent do
69 92
     it "sends options['payload'] as a POST request" do
70 93
       lambda {
71 94
         @checker.check
72
-      }.should change { @sent_posts.length }.by(1)
95
+      }.should change { @sent_requests[Net::HTTP::Post].length }.by(1)
73 96
 
74
-      @sent_posts[0].should == @checker.options['payload']
97
+      @sent_requests[Net::HTTP::Post][0].should == @checker.options['payload']
75 98
     end
76 99
 
77 100
     it "sends options['payload'] as a GET request" do
@@ -79,10 +102,10 @@ describe Agents::PostAgent do
79 102
       lambda {
80 103
         lambda {
81 104
           @checker.check
82
-        }.should change { @sent_gets.length }.by(1)
83
-      }.should_not change { @sent_posts.length }
105
+        }.should change { @sent_requests[Net::HTTP::Get].length }.by(1)
106
+      }.should_not change { @sent_requests[Net::HTTP::Post].length }
84 107
 
85
-      @sent_gets[0].should == @checker.options['payload']
108
+      @sent_requests[Net::HTTP::Get][0].should == @checker.options['payload']
86 109
     end
87 110
   end
88 111
 
@@ -112,7 +135,7 @@ describe Agents::PostAgent do
112 135
       @checker.should_not be_valid
113 136
     end
114 137
 
115
-    it "should validate method as post or get, defaulting to post" do
138
+    it "should validate method as post, get, put, patch, or delete, defaulting to post" do
116 139
       @checker.options['method'] = ""
117 140
       @checker.method.should == "post"
118 141
       @checker.should be_valid
@@ -125,11 +148,35 @@ describe Agents::PostAgent do
125 148
       @checker.method.should == "get"
126 149
       @checker.should be_valid
127 150
 
151
+      @checker.options['method'] = "patch"
152
+      @checker.method.should == "patch"
153
+      @checker.should be_valid
154
+
128 155
       @checker.options['method'] = "wut"
129 156
       @checker.method.should == "wut"
130 157
       @checker.should_not be_valid
131 158
     end
132 159
 
160
+    it "should validate that no_merge is 'true' or 'false', if present" do
161
+      @checker.options['no_merge'] = ""
162
+      @checker.should be_valid
163
+
164
+      @checker.options['no_merge'] = "true"
165
+      @checker.should be_valid
166
+
167
+      @checker.options['no_merge'] = "false"
168
+      @checker.should be_valid
169
+
170
+      @checker.options['no_merge'] = false
171
+      @checker.should be_valid
172
+
173
+      @checker.options['no_merge'] = true
174
+      @checker.should be_valid
175
+
176
+      @checker.options['no_merge'] = 'blarg'
177
+      @checker.should_not be_valid
178
+    end
179
+
133 180
     it "should validate payload as a hash, if present" do
134 181
       @checker.options['payload'] = ""
135 182
       @checker.should be_valid