Merge pull request #429 from knu/post_agent_faraday

Make PostAgent use WebRequestConcern, adding options for Basic Auth and User-Agent.

Akinori MUSHA 10 years ago
parent
commit
03ec5eb4e9
2 changed files with 114 additions and 101 deletions
  1. 28 40
      app/models/agents/post_agent.rb
  2. 86 61
      spec/models/agents/post_agent_spec.rb

+ 28 - 40
app/models/agents/post_agent.rb

@@ -1,5 +1,7 @@
1 1
 module Agents
2 2
   class PostAgent < Agent
3
+    include WebRequestConcern
4
+
3 5
     cannot_create_events!
4 6
 
5 7
     default_schedule "never"
@@ -13,7 +15,11 @@ module Agents
13 15
 
14 16
       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 17
 
16
-      The `headers` field is optional.  When present, it should be a hash of headers to send with the request.
18
+      Other Options:
19
+
20
+        * `headers` - When present, it should be a hash of headers to send with the request.
21
+        * `basic_auth` - Specify HTTP basic auth parameters: `"username:password"`, or `["username", "password"]`.
22
+        * `user_agent` - A custom User-Agent name (default: "Faraday v#{Faraday::VERSION}").
17 23
     MD
18 24
 
19 25
     event_description "Does not produce events."
@@ -40,10 +46,6 @@ module Agents
40 46
       (interpolated['method'].presence || 'post').to_s.downcase
41 47
     end
42 48
 
43
-    def headers
44
-      interpolated['headers'].presence || {}
45
-    end
46
-
47 49
     def validate_options
48 50
       unless options['post_url'].present? && options['expected_receive_period_in_days'].present?
49 51
         errors.add(:base, "post_url and expected_receive_period_in_days are required fields")
@@ -64,6 +66,8 @@ module Agents
64 66
       unless headers.is_a?(Hash)
65 67
         errors.add(:base, "if provided, headers must be a hash")
66 68
       end
69
+
70
+      validate_web_request_options!
67 71
     end
68 72
 
69 73
     def receive(incoming_events)
@@ -81,48 +85,32 @@ module Agents
81 85
       handle interpolated['payload'].presence || {}
82 86
     end
83 87
 
84
-    def generate_uri(params = nil, payload = {})
85
-      uri = URI interpolated(payload)[:post_url]
86
-      uri.query = URI.encode_www_form(Hash[URI.decode_www_form(uri.query || '')].merge(params)) if params
87
-      uri
88
-    end
89
-
90 88
     private
91 89
 
92 90
     def handle(data, payload = {})
93
-      if method == 'post'
94
-        post_data(data, payload, Net::HTTP::Post)
95
-      elsif method == 'put'
96
-        post_data(data, payload, Net::HTTP::Put)
97
-      elsif method == 'delete'
98
-        post_data(data, payload, Net::HTTP::Delete)
99
-      elsif method == 'patch'
100
-        post_data(data, payload, Net::HTTP::Patch)
101
-      elsif method == 'get'
102
-        get_data(data, payload)
91
+      url = interpolated(payload)[:post_url]
92
+      headers = headers()
93
+
94
+      case method
95
+      when 'get', 'delete'
96
+        params, body = data, nil
97
+      when 'post', 'put', 'patch'
98
+        params = nil
99
+
100
+        case interpolated(payload)['content_type']
101
+        when 'json'
102
+          headers['Content-Type'] = 'application/json; charset=utf-8'
103
+          body = data.to_json
104
+        else
105
+          body = data
106
+        end
103 107
       else
104 108
         error "Invalid method '#{method}'"
105 109
       end
106
-    end
107
-
108
-    def post_data(data, payload, request_type = Net::HTTP::Post)
109
-      uri = generate_uri(nil, payload)
110
-      req = request_type.new(uri.request_uri, headers)
111
-
112
-      if interpolated(payload)['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
-
119
-      Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
120
-    end
121 110
 
122
-    def get_data(data, payload)
123
-      uri = generate_uri(data, payload)
124
-      req = Net::HTTP::Get.new(uri.request_uri, headers)
125
-      Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) }
111
+      faraday.run_request(method.to_sym, url, body, headers) { |request|
112
+        request.params.update(params) if params
113
+      }
126 114
     end
127 115
   end
128 116
 end

+ 86 - 61
spec/models/agents/post_agent_spec.rb

@@ -1,17 +1,19 @@
1 1
 require 'spec_helper'
2
+require 'ostruct'
2 3
 
3 4
 describe Agents::PostAgent do
4 5
   before do
5
-    @valid_params = {
6
-      :name => "somename",
7
-      :options => {
8
-        'post_url' => "http://www.example.com",
9
-        'expected_receive_period_in_days' => 1,
10
-        'payload' => {
11
-          'default' => 'value'
12
-        }
6
+    @valid_options = {
7
+      'post_url' => "http://www.example.com",
8
+      'expected_receive_period_in_days' => 1,
9
+      'payload' => {
10
+        'default' => 'value'
13 11
       }
14 12
     }
13
+    @valid_params = {
14
+      name: "somename",
15
+      options: @valid_options
16
+    }
15 17
 
16 18
     @checker = Agents::PostAgent.new(@valid_params)
17 19
     @checker.user = users(:jane)
@@ -26,22 +28,39 @@ describe Agents::PostAgent do
26 28
       }
27 29
     }
28 30
     @requests = 0
29
-    @sent_requests = { Net::HTTP::Get => [], Net::HTTP::Post => [], Net::HTTP::Put => [], Net::HTTP::Delete => [], Net::HTTP::Patch => [] }
30
-
31
-    stub.any_instance_of(Agents::PostAgent).post_data { |data, payload, type| @requests += 1; @sent_requests[type] << data }
32
-    stub.any_instance_of(Agents::PostAgent).get_data { |data, payload| @requests += 1; @sent_requests[Net::HTTP::Get] << data }
31
+    @sent_requests = Hash.new { |hash, method| hash[method] = [] }
32
+
33
+    stub_request(:any, /:/).to_return { |request|
34
+      method = request.method
35
+      @requests += 1
36
+      @sent_requests[method] << req = OpenStruct.new(uri: request.uri)
37
+      case method
38
+      when :get, :delete
39
+        req.data = request.uri.query
40
+      else
41
+        case request.headers['Content-Type'][/\A[^;\s]+/]
42
+        when 'application/x-www-form-urlencoded'
43
+          req.data = request.body
44
+        when 'application/json'
45
+          req.data = ActiveSupport::JSON.decode(request.body)
46
+        else
47
+          raise "unexpected Content-Type: #{content_type}"
48
+        end
49
+      end
50
+      { status: 200, body: "ok" }
51
+    }
33 52
   end
34 53
 
54
+  it_behaves_like WebRequestConcern
55
+
35 56
   describe "making requests" do
36 57
     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|
58
+      %w[get put post patch delete].each.with_index(1) do |verb, index|
40 59
         @checker.options['method'] = verb
41 60
         @checker.should be_valid
42 61
         @checker.check
43
-        @requests.should == index + 1
44
-        @sent_requests[type].length.should == 1
62
+        @requests.should == index
63
+        @sent_requests[verb.to_sym].length.should == 1
45 64
       end
46 65
     end
47 66
   end
@@ -59,11 +78,11 @@ describe Agents::PostAgent do
59 78
       lambda {
60 79
         lambda {
61 80
           @checker.receive([@event, event1])
62
-        }.should change { @sent_requests[Net::HTTP::Post].length }.by(2)
63
-      }.should_not change { @sent_requests[Net::HTTP::Get].length }
81
+        }.should change { @sent_requests[:post].length }.by(2)
82
+      }.should_not change { @sent_requests[:get].length }
64 83
 
65
-      @sent_requests[Net::HTTP::Post][0].should == @event.payload.merge('default' => 'value')
66
-      @sent_requests[Net::HTTP::Post][1].should == event1.payload
84
+      @sent_requests[:post][0].data.should == @event.payload.merge('default' => 'value').to_query
85
+      @sent_requests[:post][1].data.should == event1.payload.to_query
67 86
     end
68 87
 
69 88
     it "can make GET requests" do
@@ -72,10 +91,23 @@ describe Agents::PostAgent do
72 91
       lambda {
73 92
         lambda {
74 93
           @checker.receive([@event])
75
-        }.should change { @sent_requests[Net::HTTP::Get].length }.by(1)
76
-      }.should_not change { @sent_requests[Net::HTTP::Post].length }
94
+        }.should change { @sent_requests[:get].length }.by(1)
95
+      }.should_not change { @sent_requests[:post].length }
77 96
 
78
-      @sent_requests[Net::HTTP::Get][0].should == @event.payload.merge('default' => 'value')
97
+      @sent_requests[:get][0].data.should == @event.payload.merge('default' => 'value').to_query
98
+    end
99
+
100
+    it "can make a GET request merging params in post_url, payload and event" do
101
+      @checker.options['method'] = 'get'
102
+      @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value"
103
+      @event.payload = {
104
+        "some_param" => "some_value",
105
+        "another_param" => "another_value"
106
+      }
107
+      @checker.receive([@event])
108
+      uri = @sent_requests[:get].first.uri
109
+      # parameters are alphabetically sorted by Faraday
110
+      uri.request_uri.should == "/a/path?another_param=another_value&default=value&existing_param=existing_value&some_param=some_value"
79 111
     end
80 112
 
81 113
     it "can skip merging the incoming event when no_merge is set, but it still interpolates" do
@@ -84,7 +116,21 @@ describe Agents::PostAgent do
84 116
         'key' => 'it said: {{ someotherkey.somekey }}'
85 117
       }
86 118
       @checker.receive([@event])
87
-      @sent_requests[Net::HTTP::Post].first.should == { 'key' => 'it said: value' }
119
+      @sent_requests[:post].first.data.should == { 'key' => 'it said: value' }.to_query
120
+    end
121
+
122
+    it "interpolates when receiving a payload" do
123
+      @checker.options['post_url'] = "https://{{ domain }}/{{ variable }}?existing_param=existing_value"
124
+      @event.payload = {
125
+        'domain' => 'google.com',
126
+        'variable' => 'a_variable'
127
+      }
128
+      @checker.receive([@event])
129
+      uri = @sent_requests[:post].first.uri
130
+      uri.scheme.should == 'https'
131
+      uri.host.should == 'google.com'
132
+      uri.path.should == '/a_variable'
133
+      uri.query.should == "existing_param=existing_value"
88 134
     end
89 135
   end
90 136
 
@@ -92,9 +138,18 @@ describe Agents::PostAgent do
92 138
     it "sends options['payload'] as a POST request" do
93 139
       lambda {
94 140
         @checker.check
95
-      }.should change { @sent_requests[Net::HTTP::Post].length }.by(1)
141
+      }.should change { @sent_requests[:post].length }.by(1)
96 142
 
97
-      @sent_requests[Net::HTTP::Post][0].should == @checker.options['payload']
143
+      @sent_requests[:post][0].data.should == @checker.options['payload'].to_query
144
+    end
145
+
146
+    it "sends options['payload'] as JSON as a POST request" do
147
+      @checker.options['content_type'] = 'json'
148
+      lambda {
149
+        @checker.check
150
+      }.should change { @sent_requests[:post].length }.by(1)
151
+
152
+      @sent_requests[:post][0].data.should == @checker.options['payload']
98 153
     end
99 154
 
100 155
     it "sends options['payload'] as a GET request" do
@@ -102,10 +157,10 @@ describe Agents::PostAgent do
102 157
       lambda {
103 158
         lambda {
104 159
           @checker.check
105
-        }.should change { @sent_requests[Net::HTTP::Get].length }.by(1)
106
-      }.should_not change { @sent_requests[Net::HTTP::Post].length }
160
+        }.should change { @sent_requests[:get].length }.by(1)
161
+      }.should_not change { @sent_requests[:post].length }
107 162
 
108
-      @sent_requests[Net::HTTP::Get][0].should == @checker.options['payload']
163
+      @sent_requests[:get][0].data.should == @checker.options['payload'].to_query
109 164
     end
110 165
   end
111 166
 
@@ -208,34 +263,4 @@ describe Agents::PostAgent do
208 263
       @checker.should be_valid
209 264
     end
210 265
   end
211
-
212
-  describe "#generate_uri" do
213
-    it "merges params with any in the post_url" do
214
-      @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value"
215
-      uri = @checker.generate_uri("some_param" => "some_value", "another_param" => "another_value")
216
-      uri.request_uri.should == "/a/path?existing_param=existing_value&some_param=some_value&another_param=another_value"
217
-    end
218
-
219
-    it "works fine with urls that do not have a query" do
220
-      @checker.options['post_url'] = "http://example.com/a/path"
221
-      uri = @checker.generate_uri("some_param" => "some_value", "another_param" => "another_value")
222
-      uri.request_uri.should == "/a/path?some_param=some_value&another_param=another_value"
223
-    end
224
-
225
-    it "just returns the post_uri when no params are given" do
226
-      @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value"
227
-      uri = @checker.generate_uri
228
-      uri.host.should == 'example.com'
229
-      uri.scheme.should == 'http'
230
-      uri.request_uri.should == "/a/path?existing_param=existing_value"
231
-    end
232
-
233
-    it "interpolates when receiving a payload" do
234
-      @checker.options['post_url'] = "https://{{ domain }}/{{ variable }}?existing_param=existing_value"
235
-      uri = @checker.generate_uri({ "some_param" => "some_value", "another_param" => "another_value" }, { 'domain' => 'google.com', 'variable' => 'a_variable' })
236
-      uri.request_uri.should == "/a_variable?existing_param=existing_value&some_param=some_value&another_param=another_value"
237
-      uri.host.should == 'google.com'
238
-      uri.scheme.should == 'https'
239
-    end
240
-  end
241
-end
266
+end