Skip to content

Commit

Permalink
feat: use the latest matching pact or verification to test webhook ex…
Browse files Browse the repository at this point in the history
…ecution, or a placeholder if either is not found
  • Loading branch information
bethesque committed Jun 19, 2018
1 parent 69d5cb0 commit 273078b
Show file tree
Hide file tree
Showing 17 changed files with 294 additions and 85 deletions.
2 changes: 1 addition & 1 deletion lib/pact_broker/api/decorators/webhook_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class WebhookEventDecorator < BaseDecorator

link :'pb:execute' do | options |
{
title: "Test the execution of the webhook by sending a POST request to this URL",
title: "Test the execution of the webhook with the latest matching pact or verification by sending a POST request to this URL",
href: webhook_execution_url(represented, options[:base_url])
}
end
Expand Down
6 changes: 1 addition & 5 deletions lib/pact_broker/api/resources/webhook_execution.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def allowed_methods
end

def process_post
webhook_execution_result = webhook_service.execute_webhook_now webhook, pact
webhook_execution_result = webhook_service.test_execution(webhook)
response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
response.body = post_response_body webhook_execution_result
if webhook_execution_result.success?
Expand All @@ -39,10 +39,6 @@ def webhook
@webhook ||= webhook_service.find_by_uuid uuid
end

def pact
@pact ||= pact_service.find_latest_pact consumer_name: webhook.consumer_name, provider_name: webhook.provider_name
end

def uuid
identifier_from_path[:uuid]
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pact_broker/doc/views/webhooks.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Pact Broker Github repository.

### Testing

To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "execute" relation. The response or error will be shown in the window.
To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "pb:execute" relation. The latest matching pact/verification will be used in the template, or a placeholder if none exists. The response or error will be shown in the window.

### Deleting

Expand Down
17 changes: 17 additions & 0 deletions lib/pact_broker/pacts/placeholder_pact.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
require 'pact_broker/domain/pact'

module PactBroker
module Pacts
class PlaceholderPact < PactBroker::Domain::Pact
def initialize
consumer = OpenStruct.new(name: "placeholder-consumer")
@provider = OpenStruct.new(name: "placeholder-provider")
@consumer_version = OpenStruct.new(number: "1", pacticipant: consumer, tags: [OpenStruct.new(name: "master")])
@consumer_version_number = @consumer_version.number
@created_at = DateTime.now
@revision_number = 1
@pact_version_sha = "5d445a4612743728dfd99ccd4210423c052bb9db"
end
end
end
end
14 changes: 14 additions & 0 deletions lib/pact_broker/pacts/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ def find_latest_pact(consumer_name, provider_name, tag = nil)
query.latest.all.collect(&:to_domain_with_content)[0]
end

# Allows optional consumer_name and provider_name
def search_for_latest_pact(consumer_name, provider_name, tag = nil)
query = LatestPactPublicationsByConsumerVersion.select_all_qualified
query = query.consumer(consumer_name) if consumer_name
query = query.provider(provider_name) if provider_name

if tag == :untagged
query = query.untagged
elsif tag
query = query.tag(tag)
end
query.latest.all.collect(&:to_domain_with_content)[0]
end

def find_pact consumer_name, consumer_version, provider_name, pact_version_sha = nil
query = if pact_version_sha
AllPactPublications
Expand Down
4 changes: 4 additions & 0 deletions lib/pact_broker/pacts/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ def find_latest_pact params
pact_repository.find_latest_pact(params[:consumer_name], params[:provider_name], params[:tag])
end

def search_for_latest_pact params
pact_repository.search_for_latest_pact(params[:consumer_name], params[:provider_name], params[:tag])
end

def find_latest_pacts
pact_repository.find_latest_pacts
end
Expand Down
23 changes: 23 additions & 0 deletions lib/pact_broker/verifications/placeholder_verification.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module PactBroker
module Verifications
class PlaceholderVerification
attr_accessor :id, :number, :success,
:consumer_name, :provider_name, :provider_version,
:provider_version_number, :build_url,
:execution_date, :created_at, :pact_version_sha

def initialize
@provider_name = "placeholder-provider"
@consumer_name = "placeholder-consumer"
@number = 1
@success = true
@pact_version_sha = "5d445a4612743728dfd99ccd4210423c052bb9db"
tags = [OpenStruct.new(name: "master")]
@provider_version = OpenStruct.new(number: "aaaabbbbccccddddeeeeffff1111222233334444", tags: tags)
@provider_version_number = @provider_version.number
@execution_date = DateTime.now
@created_at = DateTime.now
end
end
end
end
9 changes: 9 additions & 0 deletions lib/pact_broker/verifications/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ def find consumer_name, provider_name, pact_version_sha, verification_number
.verification_number(verification_number).single_record
end

def search_for_latest consumer_name, provider_name
query = LatestVerificationsByConsumerVersion
.select_all_qualified
.join(:all_pact_publications, pact_version_id: :pact_version_id)
query = query.consumer(consumer_name) if consumer_name
query = query.provider(provider_name) if provider_name
query.reverse(:execution_date, :id).first
end

def find_latest_verifications_for_consumer_version consumer_name, consumer_version_number
# Use LatestPactPublicationsByConsumerVersion not AllPactPublcations because we don't
# want verifications for shadowed revisions as it would be misleading.
Expand Down
4 changes: 4 additions & 0 deletions lib/pact_broker/verifications/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ def find_latest_verification_for_tags consumer_name, provider_name, consumer_ver
verification_repository.find_latest_verification_for_tags(consumer_name, provider_name, consumer_version_tag_name, provider_version_tag_name)
end

def search_for_latest consumer_name, provider_name
verification_repository.search_for_latest(consumer_name, provider_name)
end

def verification_summary_for_consumer_version params
verifications = find_latest_verifications_for_consumer_version(params)
pacts = pact_service.find_by_consumer_version(params)
Expand Down
15 changes: 15 additions & 0 deletions lib/pact_broker/webhooks/service.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
require 'pact_broker/repositories'
require 'pact_broker/services'
require 'pact_broker/logging'
require 'base64'
require 'securerandom'
require 'pact_broker/webhooks/job'
require 'pact_broker/webhooks/triggered_webhook'
require 'pact_broker/webhooks/status'
require 'pact_broker/webhooks/webhook_event'
require 'pact_broker/verifications/placeholder_verification'
require 'pact_broker/pacts/placeholder_pact'

module PactBroker

Expand All @@ -16,6 +19,7 @@ class Service
USER = PactBroker::Webhooks::TriggeredWebhook::TRIGGER_TYPE_USER

extend Repositories
extend Services
include Logging

def self.next_uuid
Expand Down Expand Up @@ -59,6 +63,17 @@ def self.find_all
webhook_repository.find_all
end

def self.test_execution webhook
options = { failure_log_message: "Webhook execution failed", show_response: PactBroker.configuration.show_webhook_response?}
verification = nil
if webhook.trigger_on_provider_verification_published?
verification = verification_service.search_for_latest(webhook.consumer_name, webhook.provider_name) || PactBroker::Verifications::PlaceholderVerification.new
end

pact = pact_service.search_for_latest_pact(consumer_name: webhook.consumer_name, provider_name: webhook.provider_name) || PactBroker::Pacts::PlaceholderPact.new
webhook.execute(pact, verification, options)
end

def self.execute_webhook_now webhook, pact, verification = nil
triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, verification, USER)
options = { failure_log_message: "Webhook execution failed"}
Expand Down
79 changes: 28 additions & 51 deletions script/seed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,46 @@
ENV['RACK_ENV'] = 'development'
require 'sequel'
require 'logger'
DATABASE_CREDENTIALS = {logger: Logger.new($stdout), adapter: "sqlite", database: ARGV[0], :encoding => 'utf8'}
DATABASE_CREDENTIALS = {logger: Logger.new($stdout), adapter: "sqlite", database: ARGV[0], :encoding => 'utf8'}.tap { |it| puts it }
#DATABASE_CREDENTIALS = {adapter: "postgres", database: "pact_broker", username: 'pact_broker', password: 'pact_broker', :encoding => 'utf8'}

connection = Sequel.connect(DATABASE_CREDENTIALS)
connection.timezone = :utc
# Uncomment these lines to open a pry session for inspecting the database
# require 'table_print'
# require 'pry'; pry(binding);
# exit

require 'pact_broker/db'
PactBroker::DB.connection = connection
require 'pact_broker'
require 'support/test_data_builder'

# Uncomment these lines to open a pry session for inspecting the database
# require 'table_print'
# require 'pry'; pry(binding);
# exit

require 'database/table_dependency_calculator'
PactBroker::Database::TableDependencyCalculator.call(connection).each do | table_name |
connection[table_name].delete
end

class TestDataBuilder
def method_missing *args
self
end

def publish_pact params = {}
create_pact params
end
end

# latest verifications
# TestDataBuilder.new
# .create_consumer("Foo")
# .create_provider("Bar")
# .create_consumer_version("1.2.3")
# .create_pact
# .create_verification(provider_version: "4.5.6", success: true)
# .create_provider("Wiffle")
# .create_pact
# .create_verification(provider_version: "5.6.7", success: false)
# .create_provider("Meep")
# .create_pact
# .create_webhook(method: 'GET', url: 'https://localhost:9393?url=${pactbroker.pactUrl}', body: '${pactbroker.pactUrl}')

webhook_body = {
'pactUrl' => '${pactbroker.pactUrl}',
'verificationResultUrl' => '${pactbroker.verificationResultUrl}',
'consumerVersionNumber' => '${pactbroker.consumerVersionNumber}',
'providerVersionNumber' => '${pactbroker.providerVersionNumber}',
'providerVersionTags' => '${pactbroker.providerVersionTags}',
'consumerVersionTags' => '${pactbroker.consumerVersionTags}',
'consumerName' => '${pactbroker.consumerName}',
'providerName' => '${pactbroker.providerName}',
'githubVerificationStatus' => '${pactbroker.githubVerificationStatus}'
}

# .create_webhook(method: 'GET', url: 'https://localhost:9393?url=${pactbroker.pactUrl}', body: '${pactbroker.pactUrl}')
TestDataBuilder.new
.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_consumer("Foo")
.create_label("microservice")
.create_provider("Bar")
Expand All @@ -65,33 +57,33 @@ def publish_pact params = {}
.create_provider_webhook(method: 'GET', url: 'https://theage.com.au')
.create_webhook(method: 'GET', url: 'https://self-signed.badssl.com')
.create_consumer_version("1.2.100")
.publish_pact
.create_pact
.create_verification(provider_version: "1.4.234", success: true, execution_date: DateTime.now - 15)
.revise_pact
.create_consumer_version("1.2.101")
.create_consumer_version_tag('prod')
.publish_pact
.create_pact
.create_verification(provider_version: "9.9.10", success: false, execution_date: DateTime.now - 15)
.create_consumer_version("1.2.102")
.publish_pact(created_at: (Date.today - 7).to_datetime)
.create_pact(created_at: (Date.today - 7).to_datetime)
.create_verification(provider_version: "9.9.9", success: true, execution_date: DateTime.now - 14)
.create_provider("Animals")
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
.publish_pact(created_at: (Time.now - 140).to_datetime)
.create_pact(created_at: (Time.now - 140).to_datetime)
.create_verification(provider_version: "2.0.366", execution_date: Date.today - 2) #changed
.create_provider("Wiffles")
.publish_pact
.create_pact
.create_verification(provider_version: "3.6.100", success: false, execution_date: Date.today - 7)
.create_provider("Hello World App")
.create_consumer_version("1.2.107")
.publish_pact(created_at: (Date.today - 1).to_datetime)
.create_pact(created_at: (Date.today - 1).to_datetime)
.create_consumer("The Android App")
.create_provider("The back end")
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
.create_consumer_version("1.2.106")
.create_consumer_version_tag("production")
.create_consumer_version_tag("feat-x")
.publish_pact
.create_pact
.create_consumer("Some other app")
.create_provider("A service")
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
Expand All @@ -101,20 +93,5 @@ def publish_pact params = {}
.create_triggered_webhook(status: 'failure')
.create_webhook_execution
.create_consumer_version("1.2.106")
.publish_pact(created_at: (Date.today - 26).to_datetime)
.create_pact(created_at: (Date.today - 26).to_datetime)
.create_verification(provider_version: "4.8.152", execution_date: DateTime.now)

# TestDataBuilder.new
# .create_pact_with_hierarchy("A", "1", "B")
# .create_consumer_version_tag("master")
# .create_consumer_version_tag("prod")
# .create_verification(provider_version: "1")
# .create_consumer_version("2")
# .create_consumer_version_tag("master")
# .create_pact
# .create_verification(provider_version: "2")

# TestDataBuilder.new
# .create_pact_with_hierarchy("Foo", "1", "Bar")
# .create_webhook(method: 'GET', url: 'http://localhost:9393', events: [{ name: 'provider_verification_published' }, {name: ''}])

26 changes: 8 additions & 18 deletions spec/features/execute_webhook_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
expect(subject.status).to be 200
end

it "saves a TriggeredWebhook" do
expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(1)
it "does not save a TriggeredWebhook" do
expect { subject }.to_not change { PactBroker::Webhooks::TriggeredWebhook.count }
end

it "saves a WebhookExecution" do
expect { subject }.to change { PactBroker::Webhooks::Execution.count }.by(1)
it "does not save a WebhookExecution" do
expect { subject }.to_not change { PactBroker::Webhooks::Execution.count }
end

context "when a webhook host whitelist is not configured" do
Expand All @@ -52,11 +52,6 @@
it "does not show any response details" do
expect(subject.body).to_not include response_body
end

it "does not log any response details" do
subject
expect(PactBroker::Webhooks::Execution.order(:id).last.logs).to_not include response_body
end
end

context "when a webhook host whitelist is configured" do
Expand All @@ -67,11 +62,6 @@
it "includes response details" do
expect(subject.body).to include response_body
end

it "logs the response details" do
subject
expect(PactBroker::Webhooks::Execution.order(:id).last.logs).to include response_body
end
end
end

Expand All @@ -98,12 +88,12 @@
expect(subject.status).to be 500
end

it "saves a TriggeredWebhook" do
expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(1)
it "does not save a TriggeredWebhook" do
expect { subject }.to_not change { PactBroker::Webhooks::TriggeredWebhook.count }
end

it "saves a WebhookExecution" do
expect { subject }.to change { PactBroker::Webhooks::Execution.count }.by(1)
it "does not save a WebhookExecution" do
expect { subject }.to_not change { PactBroker::Webhooks::Execution.count }
end
end
end
10 changes: 2 additions & 8 deletions spec/lib/pact_broker/api/resources/webhook_execution_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,12 @@ module Resources
let(:provider_name) { "bar" }

before do
allow(PactBroker::Webhooks::Service).to receive(:execute_webhook_now).and_return(execution_result)
allow(PactBroker::Webhooks::Service).to receive(:test_execution).and_return(execution_result)
allow(PactBroker::Api::Decorators::WebhookExecutionResultDecorator).to receive(:new).and_return(decorator)
allow(PactBroker::Pacts::Service).to receive(:find_latest_pact).and_return(pact)
end

it "finds the latest pact for the webhook" do
expect(PactBroker::Pacts::Service).to receive(:find_latest_pact).with(consumer_name: consumer_name, provider_name: provider_name)
subject
end

it "executes the webhook" do
expect(PactBroker::Webhooks::Service).to receive(:execute_webhook_now).with(webhook, pact)
expect(PactBroker::Webhooks::Service).to receive(:test_execution).with(webhook)
subject
end

Expand Down
Loading

0 comments on commit 273078b

Please sign in to comment.