From 7518098a2653ada02ce4999d7bb55e2947a847a4 Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Wed, 20 Jun 2018 17:02:57 +1000 Subject: [PATCH] feat: include test webhook request in execution response body --- .../webhook_execution_result_decorator.rb | 27 ++++++++++++++ lib/pact_broker/domain/webhook.rb | 2 +- .../domain/webhook_execution_result.rb | 7 +++- lib/pact_broker/domain/webhook_request.rb | 25 +++++++++---- script/seed.rb | 2 +- ...webhook_execution_result_decorator_spec.rb | 35 ++++++++++++++++++- spec/support/test_data_builder.rb | 2 +- 7 files changed, 88 insertions(+), 12 deletions(-) diff --git a/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb b/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb index e6ae1c916..4e82b5674 100644 --- a/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +++ b/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb @@ -11,6 +11,32 @@ class ErrorDecorator < BaseDecorator property :backtrace end + class HTTPRequestDecorator < BaseDecorator + property :headers, exec_context: :decorator + property :body, exec_context: :decorator + property :url, exec_context: :decorator + + def headers + headers_hash = represented.to_hash + headers_hash.keys.each_with_object({}) do | name, new_headers_hash| + new_headers_hash[name] = headers_hash[name].join(", ") + end + end + + def body + begin + ::JSON.parse(represented.body) + rescue StandardError => e + represented.body + end + end + + def url + (represented.uri || represented.path).to_s + end + end + + class HTTPResponseDecorator < BaseDecorator property :status, :getter => lambda { |_| code.to_i } property :headers, exec_context: :decorator @@ -33,6 +59,7 @@ def body end property :error, :extend => ErrorDecorator, if: lambda { |context| context[:options][:user_options][:show_response] } + property :request, :extend => HTTPRequestDecorator property :response, :extend => HTTPResponseDecorator, if: lambda { |context| context[:options][:user_options][:show_response] } property :response_hidden_message, as: :message, exec_context: :decorator, if: lambda { |context| !context[:options][:user_options][:show_response] } diff --git a/lib/pact_broker/domain/webhook.rb b/lib/pact_broker/domain/webhook.rb index 6ea046356..2afb898ae 100644 --- a/lib/pact_broker/domain/webhook.rb +++ b/lib/pact_broker/domain/webhook.rb @@ -46,7 +46,7 @@ def execute pact, verification, options end def to_s - "webhook for consumer=#{consumer_name} provider=#{provider_name} uuid=#{uuid} request=#{request}" + "webhook for consumer=#{consumer_name} provider=#{provider_name} uuid=#{uuid}" end def consumer_name diff --git a/lib/pact_broker/domain/webhook_execution_result.rb b/lib/pact_broker/domain/webhook_execution_result.rb index 78280ddb4..ad6cabd39 100644 --- a/lib/pact_broker/domain/webhook_execution_result.rb +++ b/lib/pact_broker/domain/webhook_execution_result.rb @@ -4,7 +4,8 @@ module Domain class WebhookExecutionResult - def initialize response, logs, error = nil + def initialize request, response, logs, error = nil + @request = request @response = response @logs = logs @error = error @@ -14,6 +15,10 @@ def success? !@response.nil? && @response.code.to_i < 300 end + def request + @request + end + def response @response end diff --git a/lib/pact_broker/domain/webhook_request.rb b/lib/pact_broker/domain/webhook_request.rb index 9ac139475..30dad50a2 100644 --- a/lib/pact_broker/domain/webhook_request.rb +++ b/lib/pact_broker/domain/webhook_request.rb @@ -15,12 +15,10 @@ module PactBroker module Domain class WebhookRequestError < StandardError - def initialize message, response super message @response = response end - end class WebhookResponseWithUtf8SafeBody < SimpleDelegator @@ -41,6 +39,18 @@ def unsafe_body? end end + class WebhookRequestWithRedactedHeaders < SimpleDelegator + def to_hash + __getobj__().to_hash.each_with_object({}) do | (key, values), new_hash | + new_hash[key] = redact?(key) ? ["**********"] : values + end + end + + def redact? name + WebhookRequest::HEADERS_TO_REDACT.any?{ | pattern | name =~ pattern } + end + end + class WebhookRequest include PactBroker::Logging @@ -95,7 +105,7 @@ def execute_and_build_result pact, verification, options, logs, execution_logger req = build_request(uri, pact, verification, execution_logger) response = do_request(uri, req) log_response(response, execution_logger, options) - result = WebhookExecutionResult.new(response, logs.string) + result = WebhookExecutionResult.new(WebhookRequestWithRedactedHeaders.new(req), response, logs.string) log_completion_message(options, execution_logger, result.success?) result end @@ -103,7 +113,7 @@ def execute_and_build_result pact, verification, options, logs, execution_logger def handle_error_and_build_result e, options, logs, execution_logger log_error(e, execution_logger, options) log_completion_message(options, execution_logger, false) - WebhookExecutionResult.new(nil, logs.string, e) + WebhookExecutionResult.new(nil, nil, logs.string, e) end def build_request uri, pact, verification, execution_logger @@ -123,11 +133,12 @@ def build_request uri, pact, verification, execution_logger end execution_logger.info(req.body) if req.body + logger.info "Making webhook #{uuid} request #{method.upcase} URI=#{uri} headers=#{headers_to_log} (body in debug logs)" + logger.debug "body=#{req.body}" req end def do_request uri, req - logger.info "Making webhook #{uuid} request #{to_s}" options = PactBroker::BuildHttpOptions.call(uri) response = Net::HTTP.start(uri.hostname, uri.port, :ENV, options) do |http| http.request req @@ -149,8 +160,8 @@ def response_body_hidden_message end def log_response_to_application_logger response - logger.info "Received response for webhook #{uuid} status=#{response.code}" - logger.debug "headers=#{response.to_hash}" + logger.info "Received response for webhook #{uuid} status=#{response.code} (headers and body in debug logs)" + logger.debug "headers=#{response.to_hash} " logger.debug "body=#{response.unsafe_body}" end diff --git a/script/seed.rb b/script/seed.rb index 07a785430..95b0c4921 100755 --- a/script/seed.rb +++ b/script/seed.rb @@ -47,7 +47,7 @@ .create_global_webhook(method: 'GET', url: "http://example.org?consumer=${pactbroker.consumerName}&provider=${pactbroker.providerName}") .create_certificate(path: 'spec/fixtures/certificates/self-signed.badssl.com.pem') .create_consumer("Bethtest") - .create_verification_webhook(method: 'POST', url: "http://localhost:9292", body: webhook_body) + .create_verification_webhook(method: 'POST', url: "http://localhost:9292", body: webhook_body, username: "foo", password: "bar") .create_consumer("Foo") .create_label("microservice") .create_provider("Bar") diff --git a/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb b/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb index 205efcda0..5bfb7fda3 100644 --- a/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +++ b/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb @@ -8,9 +8,15 @@ module Decorators describe "to_json" do - let(:webhook_execution_result) { PactBroker::Domain::WebhookExecutionResult.new(response, logs, error)} + let(:webhook_execution_result) { PactBroker::Domain::WebhookExecutionResult.new(request, response, logs, error)} let(:logs) { "logs" } let(:headers) { { "Something" => ["blah", "thing"]} } + let(:request) do + req = Net::HTTP::Get.new("http://example.org?foo=bar") + req['Foo'] = ['bar', 'wiffle'] + req.body = { foo: 'bar' }.to_json + req + end let(:response) { double('http_response', code: '200', body: response_body, to_hash: headers) } let(:response_body) { 'body' } let(:error) { nil } @@ -43,6 +49,32 @@ module Decorators end end + context "when there is a request" do + it "includes the request URL" do + expect(subject[:request][:url]).to eq "http://example.org?foo=bar" + end + + it "includes the request headers" do + expect(subject[:request][:headers][:'foo']).to eq "bar, wiffle" + end + + context "when the request body is JSON" do + it "includes the request body as JSON" do + expect(subject[:request][:body]).to include( foo: 'bar' ) + end + end + + context "when the request body is not json" do + before do + request.body = "" + end + + it "includes the request body as a String" do + expect(subject[:request][:body]).to eq "" + end + end + end + context "when there is a response" do it "includes the response code" do expect(subject[:response][:status]).to eq 200 @@ -51,6 +83,7 @@ module Decorators it "includes the response headers" do expect(subject[:response][:headers]).to eq :'Something' => "blah, thing" end + it "includes the response body" do expect(subject[:response][:body]).to eq response_body end diff --git a/spec/support/test_data_builder.rb b/spec/support/test_data_builder.rb index 6f363b144..d21a6ac8b 100644 --- a/spec/support/test_data_builder.rb +++ b/spec/support/test_data_builder.rb @@ -294,7 +294,7 @@ def create_triggered_webhook params = {} def create_webhook_execution params = {} params.delete(:comment) logs = params[:logs] || "logs" - webhook_execution_result = PactBroker::Domain::WebhookExecutionResult.new(OpenStruct.new(code: "200"), logs, nil) + webhook_execution_result = PactBroker::Domain::WebhookExecutionResult.new(nil, OpenStruct.new(code: "200"), logs, nil) @webhook_execution = PactBroker::Webhooks::Repository.new.create_execution @triggered_webhook, webhook_execution_result created_at = params[:created_at] || @pact.created_at + Rational(1, 86400) set_created_at_if_set created_at, :webhook_executions, {id: @webhook_execution.id}