diff --git a/db/migrations/20180113_make_webhook_pacticipant_ids_optional.rb b/db/migrations/20180113_make_webhook_pacticipant_ids_optional.rb new file mode 100644 index 000000000..911043a47 --- /dev/null +++ b/db/migrations/20180113_make_webhook_pacticipant_ids_optional.rb @@ -0,0 +1,48 @@ +Sequel.migration do + up do + alter_table(:webhooks) do + set_column_allow_null(:consumer_id) + set_column_allow_null(:provider_id) + end + + alter_table(:triggered_webhooks) do + set_column_allow_null(:consumer_id) + set_column_allow_null(:provider_id) + end + + create_or_replace_view(:latest_triggered_webhooks, + "select tw.* + from triggered_webhooks tw + inner join latest_triggered_webhook_ids ltwi + on tw.consumer_id = ltwi.consumer_id + and tw.provider_id = ltwi.provider_id + and tw.webhook_uuid = ltwi.webhook_uuid + and tw.created_at = ltwi.latest_triggered_webhook_created_at + + union + + select tw.* + from triggered_webhooks tw + inner join latest_triggered_webhook_ids ltwi + on tw.consumer_id = ltwi.consumer_id + and tw.webhook_uuid = ltwi.webhook_uuid + and tw.created_at = ltwi.latest_triggered_webhook_created_at + where tw.provider_id is null + and ltwi.provider_id is null + + union + + select tw.* + from triggered_webhooks tw + inner join latest_triggered_webhook_ids ltwi + on tw.provider_id = ltwi.provider_id + and tw.webhook_uuid = ltwi.webhook_uuid + and tw.created_at = ltwi.latest_triggered_webhook_created_at + where tw.consumer_id is null + and ltwi.consumer_id is null" + ) + end + + down do + end +end diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index a420b80f5..ab246ad23 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -56,6 +56,8 @@ module PactBroker # Webhooks add ['webhooks', 'provider', :provider_name, 'consumer', :consumer_name ], Api::Resources::PactWebhooks, {resource_name: "pact_webhooks"} + add ['webhooks', 'provider', :provider_name], Api::Resources::PactWebhooks, {resource_name: "provider_webhooks"} + add ['webhooks', 'consumer', :consumer_name], Api::Resources::PactWebhooks, {resource_name: "consumer_webhooks"} add ['webhooks', 'provider', :provider_name, 'consumer', :consumer_name, 'status' ], Api::Resources::PactWebhooksStatus, {resource_name: "pact_webhooks_status"} add ['webhooks', :uuid ], Api::Resources::Webhook, {resource_name: "webhook"} diff --git a/lib/pact_broker/api/decorators/webhook_decorator.rb b/lib/pact_broker/api/decorators/webhook_decorator.rb index 09833826c..a6582e602 100644 --- a/lib/pact_broker/api/decorators/webhook_decorator.rb +++ b/lib/pact_broker/api/decorators/webhook_decorator.rb @@ -38,26 +38,32 @@ class WebhookEventDecorator < BaseDecorator link :'pb:consumer' do | options | - { - title: "Consumer", - name: represented.consumer.name, - href: pacticipant_url(options.fetch(:base_url), represented.consumer) - } + if represented.consumer + { + title: "Consumer", + name: represented.consumer.name, + href: pacticipant_url(options.fetch(:base_url), represented.consumer) + } + end end link :'pb:provider' do | options | - { - title: "Provider", - name: represented.provider.name, - href: pacticipant_url(options.fetch(:base_url), represented.provider) - } + if represented.provider + { + title: "Provider", + name: represented.provider.name, + href: pacticipant_url(options.fetch(:base_url), represented.provider) + } + end end link :'pb:pact-webhooks' do | options | - { - title: "All webhooks for consumer #{represented.consumer.name} and provider #{represented.provider.name}", - href: webhooks_for_pact_url(represented.consumer, represented.provider, options[:base_url]) - } + if represented.consumer && represented.provider + { + title: "All webhooks for consumer #{represented.consumer.name} and provider #{represented.provider.name}", + href: webhooks_for_pact_url(represented.consumer, represented.provider, options[:base_url]) + } + end end link :'pb:webhooks' do | options | diff --git a/lib/pact_broker/api/resources/pact_webhooks.rb b/lib/pact_broker/api/resources/pact_webhooks.rb index d31ee272e..bd68ae2f8 100644 --- a/lib/pact_broker/api/resources/pact_webhooks.rb +++ b/lib/pact_broker/api/resources/pact_webhooks.rb @@ -24,7 +24,8 @@ def content_types_accepted end def resource_exists? - consumer && provider + (identifier_from_path[:consumer_name].nil? || consumer) && + (identifier_from_path[:provider_name].nil? || provider) end def malformed_request? @@ -77,11 +78,11 @@ def next_uuid end def consumer - @consumer ||= find_pacticipant(identifier_from_path[:consumer_name], "consumer") + @consumer ||= identifier_from_path[:consumer_name] && find_pacticipant(identifier_from_path[:consumer_name], "consumer") end def provider - @provider ||= find_pacticipant(identifier_from_path[:provider_name], "provider") + @provider ||= identifier_from_path[:provider_name] && find_pacticipant(identifier_from_path[:provider_name], "provider") end def find_pacticipant name, role diff --git a/lib/pact_broker/domain/webhook.rb b/lib/pact_broker/domain/webhook.rb index b9fab9102..349ad01f8 100644 --- a/lib/pact_broker/domain/webhook.rb +++ b/lib/pact_broker/domain/webhook.rb @@ -25,7 +25,13 @@ def initialize attributes = {} end def description - "A webhook for the pact between #{consumer.name} and #{provider.name}" + if consumer && provider + "A webhook for the pact between #{consumer.name} and #{provider.name}" + elsif provider + "A webhook for all pacts with provider #{provider.name}" + else + "A webhook for all pacts with consumer #{consumer.name}" + end end def request_description diff --git a/lib/pact_broker/webhooks/repository.rb b/lib/pact_broker/webhooks/repository.rb index 4285a403b..6270bd30f 100644 --- a/lib/pact_broker/webhooks/repository.rb +++ b/lib/pact_broker/webhooks/repository.rb @@ -59,14 +59,38 @@ def find_all Webhook.all.collect(&:to_domain) end + def find_for_pact pact + find_by_consumer_and_provider(pact.consumer, pact.provider) + + find_by_consumer_and_provider(nil, pact.provider) + + find_by_consumer_and_provider(pact.consumer, nil) + end + def find_by_consumer_and_provider consumer, provider - Webhook.where(consumer_id: consumer.id, provider_id: provider.id).collect(&:to_domain) + criteria = { + consumer_id: (consumer ? consumer.id : nil), + provider_id: (provider ? provider.id : nil) + } + Webhook.where(criteria).collect(&:to_domain) + end + + def find_for_pact_and_event_name pact, event_name + find_by_consumer_and_or_provider_and_event_name(pact.consumer, pact.provider, event_name) + end + + def find_by_consumer_and_or_provider_and_event_name consumer, provider, event_name + find_by_consumer_and_provider_and_event_name(consumer, provider, event_name) + + find_by_consumer_and_provider_and_event_name(nil, provider, event_name) + + find_by_consumer_and_provider_and_event_name(consumer, nil, event_name) end def find_by_consumer_and_provider_and_event_name consumer, provider, event_name + criteria = { + consumer_id: (consumer ? consumer.id : nil), + provider_id: (provider ? provider.id : nil) + } Webhook .select_all_qualified - .where(consumer_id: consumer.id, provider_id: provider.id) + .where(criteria) .join(:webhook_events, { webhook_id: :id }) .where(Sequel[:webhook_events][:name] => event_name) .collect(&:to_domain) @@ -136,9 +160,20 @@ def delete_triggered_webhooks_by_pact_publication_ids pact_publication_ids DeprecatedExecution.where(pact_publication_id: pact_publication_ids).delete end + def find_latest_triggered_webhooks_for_pact pact + find_latest_triggered_webhooks(pact.consumer, pact.provider) + + find_latest_triggered_webhooks(nil, pact.provider) + + find_latest_triggered_webhooks(pact.consumer, nil) + end + def find_latest_triggered_webhooks consumer, provider + # The manual grouping is just to get rid of any webhooks that triggered at the same time + criteria = { + consumer_id: consumer ? consumer.id : nil, + provider_id: provider ? provider.id : nil + } LatestTriggeredWebhook - .where(consumer: consumer, provider: provider) + .where(criteria) .order(:id) .all end diff --git a/lib/pact_broker/webhooks/service.rb b/lib/pact_broker/webhooks/service.rb index c80aa86a4..7f11f66b4 100644 --- a/lib/pact_broker/webhooks/service.rb +++ b/lib/pact_broker/webhooks/service.rb @@ -81,6 +81,14 @@ def self.update_triggered_webhook_status triggered_webhook, status webhook_repository.update_triggered_webhook_status triggered_webhook, status end + def self.find_for_pact pact + webhook_repository.find_for_pact(pact) + end + + def self.find_by_consumer_and_or_provider consumer, provider + webhook_repository.find_by_consumer_and_or_provider(consumer, provider) + end + def self.find_by_consumer_and_provider consumer, provider webhook_repository.find_by_consumer_and_provider consumer, provider end @@ -109,6 +117,10 @@ def self.run_later webhooks, pact, verification, event_name end end + def self.find_latest_triggered_webhooks_for_pact pact + webhook_repository.find_latest_triggered_webhooks_for_pact pact + end + def self.find_latest_triggered_webhooks consumer, provider webhook_repository.find_latest_triggered_webhooks consumer, provider end diff --git a/lib/pact_broker/webhooks/webhook.rb b/lib/pact_broker/webhooks/webhook.rb index 14ba932e6..0480d6ed8 100644 --- a/lib/pact_broker/webhooks/webhook.rb +++ b/lib/pact_broker/webhooks/webhook.rb @@ -28,8 +28,8 @@ def self.from_domain webhook, consumer, provider new( properties_hash_from_domain(webhook).merge(uuid: webhook.uuid) ).tap do | db_webhook | - db_webhook.consumer_id = consumer.id - db_webhook.provider_id = provider.id + db_webhook.consumer_id = consumer.id if consumer + db_webhook.provider_id = provider.id if provider end end diff --git a/spec/features/create_webhook_spec.rb b/spec/features/create_webhook_spec.rb index 0aea0bb4e..081436b07 100644 --- a/spec/features/create_webhook_spec.rb +++ b/spec/features/create_webhook_spec.rb @@ -6,74 +6,101 @@ TestDataBuilder.new.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider") end - let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" } let(:headers) { {'CONTENT_TYPE' => 'application/json'} } let(:response_body) { JSON.parse(last_response.body, symbolize_names: true)} let(:webhook_json) { webhook_hash.to_json } + let(:webhook_hash) do + { + events: [{ + name: 'something_happened' + }], + request: { + method: 'POST', + url: 'http://example.org', + headers: { + :"Content-Type" => "application/json" + }, + body: { + a: 'body' + } + } + } + end subject { post path, webhook_json, headers } - context "with invalid attributes" do + context "for a consumer and provider" do + let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" } - let(:webhook_hash) { {} } + context "with invalid attributes" do + let(:webhook_hash) { {} } - it "returns a 400" do - subject - expect(last_response.status).to be 400 - end + it "returns a 400" do + subject + expect(last_response.status).to be 400 + end it "returns a JSON content type" do subject expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8' end - it "returns the validation errors" do - subject - expect(response_body[:errors]).to_not be_empty + it "returns the validation errors" do + subject + expect(response_body[:errors]).to_not be_empty + end end - end - - context "with valid attributes" do - - let(:webhook_hash) do - { - events: [{ - name: 'something_happened' - }], - request: { - method: 'POST', - url: 'https://example.org', - headers: { - :"Content-Type" => "application/json" - }, - body: { - a: 'body' - } - } - } + context "with valid attributes" do + it "returns a 201 response" do + subject + expect(last_response.status).to be 201 + end + + it "returns the Location header" do + subject + expect(last_response.headers['Location']).to match(%r{http://example.org/webhooks/.+}) + end + + it "returns a JSON Content Type" do + subject + expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8' + end + + it "returns the newly created webhook" do + subject + expect(response_body).to include webhook_hash + end end + end - let(:webhook_json) { webhook_hash.to_json } + context "for a provider" do + let(:path) { "/webhooks/provider/Some%20Provider" } it "returns a 201 response" do subject expect(last_response.status).to be 201 end - it "returns the Location header" do + it "creates a webhook without a consumer" do subject - expect(last_response.headers['Location']).to match(%r{http://example.org/webhooks/.+}) + expect(PactBroker::Webhooks::Webhook.first.provider).to_not be nil + expect(PactBroker::Webhooks::Webhook.first.consumer).to be nil end + end - it "returns a JSON Content Type" do + context "for a consumer" do + let(:path) { "/webhooks/consumer/Some%20Consumer" } + + it "returns a 201 response" do subject - expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8' + expect(last_response.status).to be 201 end - it "returns the newly created webhook" do + it "creates a webhook without a provider" do subject - expect(response_body).to include webhook_hash + expect(PactBroker::Webhooks::Webhook.first.consumer).to_not be nil + expect(PactBroker::Webhooks::Webhook.first.provider).to be nil end end end diff --git a/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb b/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb index d1f20700f..c347c3268 100644 --- a/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +++ b/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb @@ -89,6 +89,34 @@ module Decorators end end + context 'when there is no consumer' do + before do + webhook.consumer = nil + end + + it 'does not include the consumer relation' do + expect(parsed_json[:_links][:'pb:consumer']).to be nil + end + + it 'does not include the pact webhooks relation' do + expect(parsed_json[:_links][:'pb:pact-webhooks']).to be nil + end + end + + context 'when there is no provider' do + before do + webhook.provider = nil + end + + it 'does not include the provider relation' do + expect(parsed_json[:_links][:'pb:provider']).to be nil + end + + it 'does not include the pact webhooks relation' do + expect(parsed_json[:_links][:'pb:pact-webhooks']).to be nil + end + end + context 'when the headers contain sensitve information' do let(:headers) { { 'Authorization' => 'foo' } } it 'redacts them' do diff --git a/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb b/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb index 9c2953986..59ada0698 100644 --- a/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb +++ b/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb @@ -1,12 +1,9 @@ require 'pact_broker/api/resources/pact_webhooks' module PactBroker::Api - module Resources - describe PactWebhooks do - let(:webhook_service) { PactBroker::Webhooks::Service } let(:uuid) { '1483234k24DKFGJ45K' } let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" } @@ -35,23 +32,77 @@ module Resources subject { get path } - it "returns a 200 HAL JSON response" do - subject - expect(last_response).to be_a_hal_json_success_response - end + describe "for webhooks with a consumer and provider" do + it "returns a 200 HAL JSON response" do + subject + expect(last_response).to be_a_hal_json_success_response + end + + it "generates a JSON body" do + expect(Decorators::WebhooksDecorator).to receive(:new).with(webhooks) + expect(decorator).to receive(:to_json).with(user_options: instance_of(Decorators::DecoratorContext)) + subject + end - it "generates a JSON body" do - expect(Decorators::WebhooksDecorator).to receive(:new).with(webhooks) - expect(decorator).to receive(:to_json).with(user_options: instance_of(Decorators::DecoratorContext)) - subject + it "includes the generated JSON in the response body" do + subject + expect(last_response.body).to eq json + end + + context "when the provider does not exist" do + context "when the provider does not exist" do + let(:provider) { nil } + + it "returns a 404" do + expect(subject.status).to eq 404 + end + end + end + + context "when the consumer does not exist" do + let(:consumer) { nil } + + it "returns a 404" do + expect(subject.status).to eq 404 + end + end end - it "includes the generated JSON in the response body" do - subject - expect(last_response.body).to eq json + context "for provider webhooks" do + let(:path) { "/webhooks/provider/Some%20Provider" } + + context "when the provider does exist" do + it "returns a 200" do + expect(subject.status).to eq 200 + end + end + + context "when the provider does not exist" do + let(:provider) { nil } + + it "returns a 404" do + expect(subject.status).to eq 404 + end + end end + context "for consumer webhooks" do + let(:path) { "/webhooks/consumer/Some%20Consumer" } + + context "when the consumer does exist" do + it "returns a 200" do + expect(subject.status).to eq 200 + end + end + + context "when the consumer does not exist" do + let(:consumer) { nil } + it "returns a 404" do + expect(subject.status).to eq 404 + end + end + end end describe "POST" do diff --git a/spec/lib/pact_broker/domain/webhook_spec.rb b/spec/lib/pact_broker/domain/webhook_spec.rb index e415b4c4a..eadafdf4c 100644 --- a/spec/lib/pact_broker/domain/webhook_spec.rb +++ b/spec/lib/pact_broker/domain/webhook_spec.rb @@ -1,12 +1,8 @@ -require 'spec_helper' require 'pact_broker/domain/webhook' module PactBroker - module Domain - describe Webhook do - let(:consumer) { Pacticipant.new(name: 'Consumer')} let(:provider) { Pacticipant.new(name: 'Provider')} let(:request) { instance_double(PactBroker::Domain::WebhookRequest, execute: nil)} @@ -14,16 +10,29 @@ module Domain let(:pact) { double('pact') } let(:verification) { double('verification') } - subject { Webhook.new(request: request, consumer: consumer, provider: provider,) } + subject(:webhook) { Webhook.new(request: request, consumer: consumer, provider: provider) } describe "description" do - it "returns a description of the webhook" do - expect(subject.description).to eq "A webhook for the pact between Consumer and Provider" + subject { webhook.description } + + context "with a consumer and provider" do + it { is_expected.to eq "A webhook for the pact between Consumer and Provider" } + end + + context "with a consumer only" do + let(:provider) { nil } + + it { is_expected.to eq "A webhook for all pacts with consumer Consumer" } + end + + context "with a provider only" do + let(:consumer) { nil } + + it { is_expected.to eq "A webhook for all pacts with provider Provider" } end end describe "execute" do - it "executes the request" do expect(request).to receive(:execute).with(pact, verification, options) subject.execute pact, verification, options diff --git a/spec/lib/pact_broker/webhooks/repository_spec.rb b/spec/lib/pact_broker/webhooks/repository_spec.rb index 5c9aa545b..e56a2949a 100644 --- a/spec/lib/pact_broker/webhooks/repository_spec.rb +++ b/spec/lib/pact_broker/webhooks/repository_spec.rb @@ -23,9 +23,8 @@ module Webhooks end let(:events) { [event]} let(:webhook) { Domain::Webhook.new(request: request, events: events)} - let(:test_data_builder) { TestDataBuilder.new } - let(:consumer) { test_data_builder.create_pacticipant 'Consumer'; test_data_builder.pacticipant} - let(:provider) { test_data_builder.create_pacticipant 'Provider'; test_data_builder.pacticipant} + let(:consumer) { td.create_pacticipant 'Consumer'; td.pacticipant} + let(:provider) { td.create_pacticipant 'Provider'; td.pacticipant} let(:uuid) { 'the-uuid' } let(:created_webhook_record) { ::DB::PACT_BROKER_DB[:webhooks].order(:id).last } let(:created_headers) { ::DB::PACT_BROKER_DB[:webhook_headers].where(webhook_id: created_webhook_record[:id]).order(:name).all } @@ -44,8 +43,7 @@ module Webhooks end describe "#create" do - - subject { Repository.new.create uuid, webhook, consumer, provider } + subject { Repository.new.create(uuid, webhook, consumer, provider) } it "saves webhook" do subject @@ -74,7 +72,7 @@ module Webhooks Repository.new.create 'another-uuid', webhook, consumer, provider end - subject { Repository.new.delete_by_uuid uuid } + subject { Repository.new.delete_by_uuid(uuid) } it "deletes the webhook headers" do expect { subject }.to change { @@ -93,7 +91,7 @@ module Webhooks before do allow(SecureRandom).to receive(:urlsafe_base64).and_return(uuid, 'another-uuid') - Repository.new.create uuid, webhook, consumer, provider + Repository.new.create(uuid, webhook, consumer, provider) end context "when the pacticipant is the consumer" do @@ -121,9 +119,7 @@ module Webhooks end describe "find_by_uuid" do - - - subject { Repository.new.find_by_uuid uuid } + subject { Repository.new.find_by_uuid(uuid) } context "when a webhook is found" do before do @@ -152,7 +148,6 @@ module Webhooks expect(subject.request.headers).to eq headers end - it "returns a webhook with the username set" do expect(subject.request.username).to eq 'username' end @@ -202,7 +197,6 @@ module Webhooks describe "update_by_uuid" do let(:uuid) { '1234' } - let(:td) { TestDataBuilder.new } let(:old_webhook_params) do { events: [{ name: 'something' }], @@ -235,7 +229,7 @@ module Webhooks PactBroker::Domain::Webhook.new(events: [new_event], request: PactBroker::Domain::WebhookRequest.new(new_webhook_params)) end - subject { Repository.new.update_by_uuid uuid, new_webhook } + subject { Repository.new.update_by_uuid(uuid, new_webhook) } it "updates the webhook" do updated_webhook = subject @@ -265,32 +259,29 @@ module Webhooks end describe "find_by_consumer_and_provider" do - let(:test_data_builder) { TestDataBuilder.new } - subject { Repository.new.find_by_consumer_and_provider test_data_builder.consumer, test_data_builder.provider} + let(:consumer) { td.consumer } + let(:provider) { td.provider } - context "when a webhook exists with a matching consumer and provider" do + subject { Repository.new.find_by_consumer_and_provider(consumer, provider) } + context "when a webhook exists with a matching consumer and provider" do before do - allow(SecureRandom).to receive(:urlsafe_base64).and_call_original - test_data_builder - .create_consumer("Consumer") + td.create_consumer("Consumer") .create_provider("Another Provider") .create_webhook .create_provider("Provider") .create_webhook end - it "returns an array of webhooks" do expect(subject).to be_instance_of Array - expect(subject.first.uuid).to eq test_data_builder.webhook.uuid + expect(subject.first.uuid).to eq td.webhook.uuid end end context "when a webhook does not exist with a matching consumer and provider" do - before do - test_data_builder + td .create_consumer("Consumer") .create_provider("Provider") .create_webhook @@ -301,16 +292,49 @@ module Webhooks expect(subject).to eq [] end end + + context "when the consumer argument is nil" do + let(:consumer) { nil } + + before do + td.create_provider("Provider") + .create_consumer("Consumer") + .create_provider_webhook + .create_webhook + end + + it "returns all the webhooks where the provider matches and the consumer id is nil" do + expect(subject.size).to be 1 + expect(subject.first.consumer).to be nil + expect(subject.first.provider).to_not be nil + end + end + + context "when the provider argument is nil" do + let(:provider) { nil } + + before do + td.create_consumer("Consumer") + .create_provider("Provider") + .create_consumer_webhook + .create_webhook + end + + it "returns all the webhooks where the consumer matches and the provider id is nil" do + expect(subject.size).to be 1 + expect(subject.first.provider).to be nil + expect(subject.first.consumer).to_not be nil + end + end end describe "find_by_consumer_and_provider_and_event_name" do - let(:test_data_builder) { TestDataBuilder.new } - subject { Repository.new.find_by_consumer_and_provider_and_event_name test_data_builder.consumer, test_data_builder.provider, 'something_happened' } + subject { Repository.new.find_by_consumer_and_provider_and_event_name td.consumer, td.provider, 'something_happened' } context "when a webhook exists with a matching consumer and provider and event name" do before do - test_data_builder + td .create_consumer("Consumer") .create_provider("Another Provider") .create_webhook @@ -326,6 +350,31 @@ module Webhooks end end + describe "find_for_pact_and_event_name" do + context "when a webhook exists with a matching consumer and provider and event name" do + before do + td + .create_consumer("Consumer") + .create_consumer_version("1") + .create_provider("Another Provider") + .create_webhook + .create_provider("Provider") + .create_pact + .create_webhook(uuid: '1', events: [{ name: 'something_happened' }]) + .create_webhook(uuid: '2', events: [{ name: 'something_happened' }]) + .create_webhook(uuid: '3', events: [{ name: 'something_else_happened' }]) + .create_consumer_webhook(uuid: '4', events: [{ name: 'something_happened' }]) + .create_provider_webhook(uuid: '5', events: [{ name: 'something_happened' }]) + end + + subject { Repository.new.find_for_pact_and_event_name(td.pact, 'something_happened') } + + it "returns an array of webhooks" do + expect(subject.collect(&:uuid).sort).to eq ['1', '2', '4', '5'] + end + end + end + describe "create_triggered_webhook" do before do td.create_consumer @@ -405,8 +454,6 @@ module Webhooks end describe "delete_triggered_webhooks_by_webhook_uuid" do - let(:td) { TestDataBuilder.new } - before do td.create_consumer .create_provider @@ -421,8 +468,8 @@ module Webhooks .create_deprecated_webhook_execution .create_webhook_execution end - let(:webhook_id) { Webhook.find(uuid: td.webhook.uuid).id } + subject { Repository.new.delete_triggered_webhooks_by_webhook_uuid td.webhook.uuid } it "deletes the related triggered webhooks" do @@ -553,6 +600,39 @@ module Webhooks end end + describe "find_latest_triggered_webhooks_for_pact" do + before do + td + .create_pact_with_hierarchy("Foo", "1.0.0", "Bar") + .create_webhook + .create_triggered_webhook + .create_webhook_execution + .create_pact_with_hierarchy + .create_webhook + .create_triggered_webhook(trigger_uuid: '256', created_at: DateTime.new(2016)) + .create_webhook_execution + .create_triggered_webhook(trigger_uuid: '332', created_at: DateTime.new(2017)) + .create_webhook_execution + .create_provider_webhook(uuid: '987') + .create_triggered_webhook(trigger_uuid: '876', created_at: DateTime.new(2017)) + .create_webhook_execution + .create_triggered_webhook(trigger_uuid: '638', created_at: DateTime.new(2018)) + .create_webhook_execution + .create_consumer_webhook + .create_triggered_webhook(trigger_uuid: '555', created_at: DateTime.new(2017)) + .create_webhook_execution + .create_triggered_webhook(trigger_uuid: '777', created_at: DateTime.new(2018)) + .create_webhook_execution + + end + + subject { Repository.new.find_latest_triggered_webhooks_for_pact(td.pact) } + + it "finds the latest triggered webhooks" do + expect(subject.collect(&:trigger_uuid).sort).to eq ['332', '638', '777'] + end + end + describe "fail_retrying_triggered_webhooks" do before do td.create_pact_with_hierarchy diff --git a/spec/lib/pact_broker/webhooks/service_spec.rb b/spec/lib/pact_broker/webhooks/service_spec.rb index c276dab7f..fd55b186c 100644 --- a/spec/lib/pact_broker/webhooks/service_spec.rb +++ b/spec/lib/pact_broker/webhooks/service_spec.rb @@ -39,7 +39,7 @@ module Webhooks let(:triggered_webhook) { instance_double(PactBroker::Webhooks::TriggeredWebhook) } before do - allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_by_consumer_and_provider_and_event_name).and_return(webhooks) + allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_for_pact_and_event_name).and_return(webhooks) allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:create_triggered_webhook).and_return(triggered_webhook) allow(Job).to receive(:perform_in) end @@ -47,7 +47,7 @@ module Webhooks subject { Service.execute_webhooks pact, verification, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED } it "finds the webhooks" do - expect_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_by_consumer_and_provider_and_event_name).with(consumer, provider, PactBroker::Webhooks::WebhookEvent::DEFAULT_EVENT_NAME) + expect_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_for_pact_and_event_name).with(pact, PactBroker::Webhooks::WebhookEvent::DEFAULT_EVENT_NAME) subject end diff --git a/spec/support/test_data_builder.rb b/spec/support/test_data_builder.rb index bbebbe547..0620c36e5 100644 --- a/spec/support/test_data_builder.rb +++ b/spec/support/test_data_builder.rb @@ -244,14 +244,16 @@ def revise_pact json_content = nil self end - def create_webhook params = {} - params.delete(:comment) + def create_webhook parameters = {} + params = parameters.dup + consumer = params.key?(:consumer) ? params.delete(:consumer) : @consumer + provider = params.key?(:provider) ? params.delete(:provider) : @provider uuid = params[:uuid] || PactBroker::Webhooks::Service.next_uuid event_params = params[:events] || [{ name: PactBroker::Webhooks::WebhookEvent::DEFAULT_EVENT_NAME }] events = event_params.collect{ |e| PactBroker::Webhooks::WebhookEvent.new(e) } default_params = { method: 'POST', url: 'http://example.org', headers: {'Content-Type' => 'application/json'}} request = PactBroker::Domain::WebhookRequest.new(default_params.merge(params)) - @webhook = PactBroker::Webhooks::Repository.new.create uuid, PactBroker::Domain::Webhook.new(request: request, events: events), @consumer, @provider + @webhook = PactBroker::Webhooks::Repository.new.create uuid, PactBroker::Domain::Webhook.new(request: request, events: events), consumer, provider self end