diff --git a/app/models/agents/post_agent.rb b/app/models/agents/post_agent.rb index f30c7c76..d6104f4d 100644 --- a/app/models/agents/post_agent.rb +++ b/app/models/agents/post_agent.rb @@ -1,5 +1,7 @@ module Agents class PostAgent < Agent + include WebRequestConcern + cannot_create_events! default_schedule "never" @@ -13,7 +15,11 @@ module Agents By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`). Change `content_type` to `json` to send JSON instead. - The `headers` field is optional. When present, it should be a hash of headers to send with the request. + Other Options: + + * `headers` - When present, it should be a hash of headers to send with the request. + * `basic_auth` - Specify HTTP basic auth parameters: `"username:password"`, or `["username", "password"]`. + * `user_agent` - A custom User-Agent name (default: "Faraday v#{Faraday::VERSION}"). MD event_description "Does not produce events." @@ -40,10 +46,6 @@ module Agents (interpolated['method'].presence || 'post').to_s.downcase end - def headers - interpolated['headers'].presence || {} - end - def validate_options unless options['post_url'].present? && options['expected_receive_period_in_days'].present? errors.add(:base, "post_url and expected_receive_period_in_days are required fields") @@ -64,6 +66,8 @@ module Agents unless headers.is_a?(Hash) errors.add(:base, "if provided, headers must be a hash") end + + validate_web_request_options! end def receive(incoming_events) @@ -81,48 +85,32 @@ module Agents handle interpolated['payload'].presence || {} end - def generate_uri(params = nil, payload = {}) - uri = URI interpolated(payload)[:post_url] - uri.query = URI.encode_www_form(Hash[URI.decode_www_form(uri.query || '')].merge(params)) if params - uri - end - private def handle(data, payload = {}) - if method == 'post' - post_data(data, payload, Net::HTTP::Post) - elsif method == 'put' - post_data(data, payload, Net::HTTP::Put) - elsif method == 'delete' - post_data(data, payload, Net::HTTP::Delete) - elsif method == 'patch' - post_data(data, payload, Net::HTTP::Patch) - elsif method == 'get' - get_data(data, payload) + url = interpolated(payload)[:post_url] + headers = headers() + + case method + when 'get', 'delete' + params, body = data, nil + when 'post', 'put', 'patch' + params = nil + + case interpolated(payload)['content_type'] + when 'json' + headers['Content-Type'] = 'application/json; charset=utf-8' + body = data.to_json + else + body = data + end else error "Invalid method '#{method}'" end - end - def post_data(data, payload, request_type = Net::HTTP::Post) - uri = generate_uri(nil, payload) - req = request_type.new(uri.request_uri, headers) - - if interpolated(payload)['content_type'] == 'json' - req.set_content_type('application/json', 'charset' => 'utf-8') - req.body = data.to_json - else - req.form_data = data - end - - Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) } - end - - def get_data(data, payload) - uri = generate_uri(data, payload) - req = Net::HTTP::Get.new(uri.request_uri, headers) - Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == "https") { |http| http.request(req) } + faraday.run_request(method.to_sym, url, body, headers) { |request| + request.params.update(params) if params + } end end end diff --git a/spec/models/agents/post_agent_spec.rb b/spec/models/agents/post_agent_spec.rb index c3a4b109..100883ed 100644 --- a/spec/models/agents/post_agent_spec.rb +++ b/spec/models/agents/post_agent_spec.rb @@ -2,16 +2,17 @@ require 'spec_helper' describe Agents::PostAgent do before do - @valid_params = { - :name => "somename", - :options => { - 'post_url' => "http://www.example.com", - 'expected_receive_period_in_days' => 1, - 'payload' => { - 'default' => 'value' - } + @valid_options = { + 'post_url' => "http://www.example.com", + 'expected_receive_period_in_days' => 1, + 'payload' => { + 'default' => 'value' } } + @valid_params = { + name: "somename", + options: @valid_options + } @checker = Agents::PostAgent.new(@valid_params) @checker.user = users(:jane) @@ -26,22 +27,37 @@ describe Agents::PostAgent do } } @requests = 0 - @sent_requests = { Net::HTTP::Get => [], Net::HTTP::Post => [], Net::HTTP::Put => [], Net::HTTP::Delete => [], Net::HTTP::Patch => [] } + @sent_requests = Hash.new { |hash, method| hash[method] = [] } - stub.any_instance_of(Agents::PostAgent).post_data { |data, payload, type| @requests += 1; @sent_requests[type] << data } - stub.any_instance_of(Agents::PostAgent).get_data { |data, payload| @requests += 1; @sent_requests[Net::HTTP::Get] << data } + stub_request(:any, /:/).to_return { |request| + method = request.method + @requests += 1 + case method + when :get, :delete + data = request.uri.query + else + if data = request.body + case request.headers['Content-Type'] + when /json/ + data = ActiveSupport::JSON.decode(data) + end + end + end + @sent_requests[method] << data + { status: 200, body: "ok" } + } end + it_behaves_like WebRequestConcern + describe "making requests" do it "can make requests of each type" do - { 'get' => Net::HTTP::Get, 'put' => Net::HTTP::Put, - 'post' => Net::HTTP::Post, 'patch' => Net::HTTP::Patch, - 'delete' => Net::HTTP::Delete }.each.with_index do |(verb, type), index| + %w[get put post patch delete].each.with_index(1) do |verb, index| @checker.options['method'] = verb @checker.should be_valid @checker.check - @requests.should == index + 1 - @sent_requests[type].length.should == 1 + @requests.should == index + @sent_requests[verb.to_sym].length.should == 1 end end end @@ -59,11 +75,11 @@ describe Agents::PostAgent do lambda { lambda { @checker.receive([@event, event1]) - }.should change { @sent_requests[Net::HTTP::Post].length }.by(2) - }.should_not change { @sent_requests[Net::HTTP::Get].length } + }.should change { @sent_requests[:post].length }.by(2) + }.should_not change { @sent_requests[:get].length } - @sent_requests[Net::HTTP::Post][0].should == @event.payload.merge('default' => 'value') - @sent_requests[Net::HTTP::Post][1].should == event1.payload + @sent_requests[:post][0].should == @event.payload.merge('default' => 'value').to_query + @sent_requests[:post][1].should == event1.payload.to_query end it "can make GET requests" do @@ -72,10 +88,10 @@ describe Agents::PostAgent do lambda { lambda { @checker.receive([@event]) - }.should change { @sent_requests[Net::HTTP::Get].length }.by(1) - }.should_not change { @sent_requests[Net::HTTP::Post].length } + }.should change { @sent_requests[:get].length }.by(1) + }.should_not change { @sent_requests[:post].length } - @sent_requests[Net::HTTP::Get][0].should == @event.payload.merge('default' => 'value') + @sent_requests[:get][0].should == @event.payload.merge('default' => 'value').to_query end it "can skip merging the incoming event when no_merge is set, but it still interpolates" do @@ -84,7 +100,7 @@ describe Agents::PostAgent do 'key' => 'it said: {{ someotherkey.somekey }}' } @checker.receive([@event]) - @sent_requests[Net::HTTP::Post].first.should == { 'key' => 'it said: value' } + @sent_requests[:post].first.should == { 'key' => 'it said: value' }.to_query end end @@ -92,9 +108,9 @@ describe Agents::PostAgent do it "sends options['payload'] as a POST request" do lambda { @checker.check - }.should change { @sent_requests[Net::HTTP::Post].length }.by(1) + }.should change { @sent_requests[:post].length }.by(1) - @sent_requests[Net::HTTP::Post][0].should == @checker.options['payload'] + @sent_requests[:post][0].should == @checker.options['payload'].to_query end it "sends options['payload'] as a GET request" do @@ -102,10 +118,10 @@ describe Agents::PostAgent do lambda { lambda { @checker.check - }.should change { @sent_requests[Net::HTTP::Get].length }.by(1) - }.should_not change { @sent_requests[Net::HTTP::Post].length } + }.should change { @sent_requests[:get].length }.by(1) + }.should_not change { @sent_requests[:post].length } - @sent_requests[Net::HTTP::Get][0].should == @checker.options['payload'] + @sent_requests[:get][0].should == @checker.options['payload'].to_query end end @@ -208,34 +224,4 @@ describe Agents::PostAgent do @checker.should be_valid end end - - describe "#generate_uri" do - it "merges params with any in the post_url" do - @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value" - uri = @checker.generate_uri("some_param" => "some_value", "another_param" => "another_value") - uri.request_uri.should == "/a/path?existing_param=existing_value&some_param=some_value&another_param=another_value" - end - - it "works fine with urls that do not have a query" do - @checker.options['post_url'] = "http://example.com/a/path" - uri = @checker.generate_uri("some_param" => "some_value", "another_param" => "another_value") - uri.request_uri.should == "/a/path?some_param=some_value&another_param=another_value" - end - - it "just returns the post_uri when no params are given" do - @checker.options['post_url'] = "http://example.com/a/path?existing_param=existing_value" - uri = @checker.generate_uri - uri.host.should == 'example.com' - uri.scheme.should == 'http' - uri.request_uri.should == "/a/path?existing_param=existing_value" - end - - it "interpolates when receiving a payload" do - @checker.options['post_url'] = "https://{{ domain }}/{{ variable }}?existing_param=existing_value" - uri = @checker.generate_uri({ "some_param" => "some_value", "another_param" => "another_value" }, { 'domain' => 'google.com', 'variable' => 'a_variable' }) - uri.request_uri.should == "/a_variable?existing_param=existing_value&some_param=some_value&another_param=another_value" - uri.host.should == 'google.com' - uri.scheme.should == 'https' - end - end -end \ No newline at end of file +end