From eecd67c3785812403984a65efed07da9ccd67e71 Mon Sep 17 00:00:00 2001 From: Dominik Sander Date: Sat, 2 Apr 2016 11:35:30 +0200 Subject: [PATCH] PostAgent: allow sending arbitrary string data When `content_type` contains a MIME type and `payload` is not a Hash the string in `payload` will be send as the HTTP body and `content_type` is send as the `Content-Type` header. #1361 --- app/models/agents/post_agent.rb | 29 +++++++++++++++++++++---- spec/models/agents/post_agent_spec.rb | 31 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/app/models/agents/post_agent.rb b/app/models/agents/post_agent.rb index 49945577..d59dea5a 100644 --- a/app/models/agents/post_agent.rb +++ b/app/models/agents/post_agent.rb @@ -2,6 +2,8 @@ module Agents class PostAgent < Agent include WebRequestConcern + MIME_RE = /\A\w+\/.+\z/ + can_dry_run! no_bulk_receive! default_schedule "never" @@ -13,7 +15,13 @@ module Agents The `method` used can be any of `get`, `post`, `put`, `patch`, and `delete`. - By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`). Change `content_type` to `json` to send JSON instead. Change `content_type` to `xml` to send XML, where the name of the root element may be specified using `xml_root`, defaulting to `post`. + By default, non-GETs will be sent with form encoding (`application/x-www-form-urlencoded`). + + Change `content_type` to `json` to send JSON instead. + + Change `content_type` to `xml` to send XML, where the name of the root element may be specified using `xml_root`, defaulting to `post`. + + When `content_type` contains a [MIME](https://en.wikipedia.org/wiki/Media_type) type, and `payload` is a string, its interpolated value will be sent as a string in the HTTP request's body and the request's `Content-Type` HTTP header will be set to `content_type`. When `payload` is a string `no_merge` has to be set to `true`. If `emit_events` is set to `true`, the server response will be emitted as an Event and can be fed to a WebsiteAgent for parsing (using its `data_from_event` and `type` options). No data processing will be attempted by this Agent, so the Event's "body" value will always be raw text. @@ -49,7 +57,8 @@ module Agents 'something' => 'the event contained {{ somekey }}' }, 'headers' => {}, - 'emit_events' => 'false' + 'emit_events' => 'false', + 'no_merge' => 'false' } end @@ -66,10 +75,19 @@ module Agents errors.add(:base, "post_url and expected_receive_period_in_days are required fields") end - if options['payload'].present? && !options['payload'].is_a?(Hash) + if options['payload'].present? && %w[get delete].include?(method) && !options['payload'].is_a?(Hash) errors.add(:base, "if provided, payload must be a hash") end + if options['payload'].present? && %w[post put patch].include?(method) + if !options['payload'].is_a?(Hash) && options['content_type'] !~ MIME_RE + errors.add(:base, "if provided, payload must be a hash") + end + if options['content_type'] =~ MIME_RE && options['payload'].is_a?(String) && boolify(options['no_merge']) != true + errors.add(:base, "when the payload is a string, `no_merge` has to be set to `true`") + end + end + if options.has_key?('emit_events') && boolify(options['emit_events']).nil? errors.add(:base, "if provided, emit_events must be true or false") end @@ -116,13 +134,16 @@ module Agents when 'post', 'put', 'patch' params = nil - case interpolated(payload)['content_type'] + case (content_type = interpolated(payload)['content_type']) when 'json' headers['Content-Type'] = 'application/json; charset=utf-8' body = data.to_json when 'xml' headers['Content-Type'] = 'text/xml; charset=utf-8' body = data.to_xml(root: (interpolated(payload)[:xml_root] || 'post')) + when MIME_RE + headers['Content-Type'] = content_type + body = data.to_s else body = data end diff --git a/spec/models/agents/post_agent_spec.rb b/spec/models/agents/post_agent_spec.rb index d054692b..853ea571 100644 --- a/spec/models/agents/post_agent_spec.rb +++ b/spec/models/agents/post_agent_spec.rb @@ -46,6 +46,8 @@ describe Agents::PostAgent do req.data = ActiveSupport::JSON.decode(request.body) when 'text/xml' req.data = Hash.from_xml(request.body) + when Agents::PostAgent::MIME_RE + req.data = request.body else raise "unexpected Content-Type: #{content_type}" end @@ -187,6 +189,16 @@ describe Agents::PostAgent do expect(@sent_requests[:get][0].data).to eq(@checker.options['payload'].to_query) end + it "sends options['payload'] as a string POST request when content-type continas a MIME type" do + @checker.options['payload'] = 'hello' + @checker.options['content_type'] = 'application/xml' + expect { + @checker.check + }.to change { @sent_requests[:post].length }.by(1) + + expect(@sent_requests[:post][0].data).to eq('hello') + end + describe "emitting events" do context "when emit_events is not set to true" do it "does not emit events" do @@ -304,6 +316,25 @@ describe Agents::PostAgent do expect(@checker).to be_valid end + it "should not validate payload as a hash if content_type includes a MIME type and method is not get or delete" do + @checker.options['no_merge'] = 'true' + @checker.options['content_type'] = 'text/xml' + @checker.options['payload'] = "test" + expect(@checker).to be_valid + + @checker.options['method'] = 'get' + expect(@checker).not_to be_valid + + @checker.options['method'] = 'delete' + expect(@checker).not_to be_valid + end + + it "requires `no_merge` to be set to true when content_type contains a MIME type" do + @checker.options['content_type'] = 'text/xml' + @checker.options['payload'] = "test" + expect(@checker).not_to be_valid + end + it "requires headers to be a hash, if present" do @checker.options['headers'] = [1,2,3] expect(@checker).not_to be_valid