Skip to content

Commit

Permalink
feat: add support for contract_requiring_verification_published webho…
Browse files Browse the repository at this point in the history
…ok (#476)
  • Loading branch information
bethesque authored Aug 5, 2021
1 parent bec6ae9 commit b4699df
Show file tree
Hide file tree
Showing 19 changed files with 492 additions and 30 deletions.
8 changes: 2 additions & 6 deletions lib/pact_broker/deployments/deployed_version_service.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
require "pact_broker/deployments/deployed_version"
require "pact_broker/repositories/scopes"

module PactBroker
module Deployments
class DeployedVersionService
extend PactBroker::Repositories::Scopes

def self.next_uuid
SecureRandom.uuid
Expand Down Expand Up @@ -78,12 +80,6 @@ def self.record_previous_version_undeployed(pacticipant, environment, target)
end

private_class_method :record_previous_version_undeployed

def self.scope_for(scope)
PactBroker.policy_scope!(scope)
end

private_class_method :scope_for
end
end
end
4 changes: 4 additions & 0 deletions lib/pact_broker/deployments/released_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def record_support_ended
def currently_supported
support_ended_at == nil
end

def version_number
version.number
end
end
end
end
Expand Down
12 changes: 12 additions & 0 deletions lib/pact_broker/deployments/released_version_service.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
require "pact_broker/deployments/released_version"
require "pact_broker/repositories/scopes"

module PactBroker
module Deployments
class ReleasedVersionService
extend PactBroker::Repositories::Scopes

def self.next_uuid
SecureRandom.uuid
end
Expand Down Expand Up @@ -42,6 +45,15 @@ def self.find_released_version_for_version_and_environment(version, environment)
def self.record_version_support_ended(released_version)
released_version.record_support_ended
end

def self.find_currently_supported_versions_for_pacticipant(pacticipant)
scope_for(ReleasedVersion)
.currently_supported
.where(pacticipant_id: pacticipant.id)
.eager(:version)
.eager(:environment)
.all
end
end
end
end
4 changes: 4 additions & 0 deletions lib/pact_broker/domain/webhook.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def trigger_on_provider_verification_failed?
events.any?(&:provider_verification_failed?)
end

def trigger_on_contract_requiring_verification_published?
events.any?(&:contract_requiring_verification_published?)
end

def expand_currently_deployed_provider_versions?
request.uses_parameter?(PactBroker::Webhooks::PactAndVerificationParameters::CURRENTLY_DEPLOYED_PROVIDER_VERSION_NUMBER)
end
Expand Down
8 changes: 8 additions & 0 deletions lib/pact_broker/pacts/pact_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ class PactVersion < Sequel::Model(:pact_versions)
dataset_module do
include PactBroker::Repositories::Helpers

def for_pact_domain(pact_domain)
where(
sha: pact_domain.pact_version_sha,
consumer_id: pact_domain.consumer.id,
provider_id: pact_domain.provider.id
).single_record
end

def join_successful_verifications
verifications_join = {
Sequel[:verifications][:pact_version_id] => Sequel[:pact_versions][:id],
Expand Down
13 changes: 12 additions & 1 deletion lib/pact_broker/repositories/scopes.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
module PactBroker
module Repositories
module Scopes
def with_no_scope
@unscoped = true
yield self
ensure
@unscoped = false
end

def scope_for(scope)
PactBroker.policy_scope!(scope)
if @unscoped == true
scope
else
PactBroker.policy_scope!(scope)
end
end

# For the times when it doesn't make sense to use the scoped class, this is a way to
Expand Down
12 changes: 9 additions & 3 deletions lib/pact_broker/test/http_test_data_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,17 +180,19 @@ def verify_pact(index: 0, success:, provider: last_provider_name, provider_versi
self
end

def create_global_webhook_for_contract_changed(uuid: nil, url: "https://postman-echo.com/post", body: nil)
def create_global_webhook_for_event(uuid: nil, url: "https://postman-echo.com/post", body: nil, event_name: )
puts "Creating global webhook for contract changed event with uuid #{uuid}"
uuid ||= SecureRandom.uuid
default_body = {
"deployedProviderVersion" => "${pactbroker.currentlyDeployedProviderVersionNumber}",
"providerVersionNumber" => "${pactbroker.providerVersionNumber}",
"providerVersionBranch" => "${pactbroker.providerVersionBranch}",
"consumerVersionNumber" => "${pactbroker.consumerVersionNumber}",
"consumerVersionBranch" => "${pactbroker.consumerVersionBranch}"
}
request_body = {
"description" => "A webhook for all consumers and providers",
"events" => [{
"name" => "contract_content_changed"
"name" => event_name
}],
"request" => {
"method" => "POST",
Expand All @@ -204,6 +206,10 @@ def create_global_webhook_for_contract_changed(uuid: nil, url: "https://postman-
self
end

def create_global_webhook_for_contract_changed(uuid: nil, url: "https://postman-echo.com/post", body: nil)
create_global_webhook_for_event(uuid: uuid, url: url, body: body, event_name: "contract_content_changed")
end

def create_global_webhook_for_anything_published(uuid: nil, url: "https://postman-echo.com/post")
puts "Creating global webhook for contract changed event with uuid #{uuid}"
uuid ||= SecureRandom.uuid
Expand Down
28 changes: 28 additions & 0 deletions lib/pact_broker/verifications/required_verification.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module PactBroker
module Verifications
class RequiredVerification
attr_reader :provider_version, :provider_version_descriptions

def initialize(attributes = {})
attributes.each do | (name, value) |
instance_variable_set("@#{name}", value) if respond_to?(name)
end
end

def == other
provider_version == other.provider_version && provider_version_descriptions == other.provider_version_descriptions
end

def + other
if provider_version != other.provider_version
raise PactBroker::Error.new("Can't + RequiredVerifications with different provider versions (#{provider_version.number}/#{other.provider_version.number})")
end

RequiredVerification.new(
provider_version: provider_version,
provider_version_descriptions: (provider_version_descriptions + other.provider_version_descriptions).uniq
)
end
end
end
end
50 changes: 49 additions & 1 deletion lib/pact_broker/verifications/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "pact_broker/logging"
require "pact_broker/hash_refinements"
require "pact_broker/events/publisher"
require "pact_broker/verifications/required_verification"

module PactBroker
module Verifications
Expand Down Expand Up @@ -89,7 +90,16 @@ def delete_all_verifications_between(consumer_name, options)
verification_repository.delete_all_verifications_between(consumer_name, options)
end

private
def calculate_required_verifications_for_pact(pact)
pact_version = PactBroker::Pacts::PactVersion.for_pact_domain(pact)
required_verifications = required_verifications_for_main_branch(pact_version) +
required_verifications_for_deployed_versions(pact_version) +
required_verifications_for_released_versions(pact_version)
required_verifications
.group_by(&:provider_version)
.values
.flat_map { | required_verifications_for_provider_version | required_verifications_for_provider_version.reduce(&:+) }
end

def broadcast_events(verification, pact, event_context)
event_params = {
Expand All @@ -105,6 +115,44 @@ def broadcast_events(verification, pact, event_context)
broadcast(:provider_verification_failed, event_params)
end
end
private :broadcast_events

def identify_required_verification(pact_version, provider_version, description)
any_verifications = PactBroker::Domain::Verification.where(pact_version_id: pact_version.id, provider_version_id: provider_version.id).any?
if !any_verifications
RequiredVerification.new(provider_version: provider_version, provider_version_descriptions: [description])
end
end
private :identify_required_verification

def required_verifications_for_main_branch(pact_version)
latest_version_from_main_branch = [version_service.find_latest_version_from_main_branch(pact_version.provider)].compact

latest_version_from_main_branch.collect do | main_branch_version |
identify_required_verification(pact_version, main_branch_version, "latest version from main branch")
end.compact
end
private :required_verifications_for_main_branch

def required_verifications_for_deployed_versions(pact_version)
deployed_versions = deployed_version_service.with_no_scope do | unscoped_service |
unscoped_service.find_currently_deployed_versions_for_pacticipant(pact_version.provider)
end
deployed_versions.collect do | deployed_version |
identify_required_verification(pact_version, deployed_version.version, "currently deployed version")
end.compact
end
private :required_verifications_for_deployed_versions

def required_verifications_for_released_versions(pact_version)
released_versions = released_version_service.with_no_scope do | unscoped_service |
unscoped_service.find_currently_supported_versions_for_pacticipant(pact_version.provider)
end
released_versions.collect do | released_version |
identify_required_verification(pact_version, released_version.version, "currently released version")
end.compact
end
private :required_verifications_for_released_versions
end
end
end
10 changes: 10 additions & 0 deletions lib/pact_broker/versions/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ def delete_orphan_versions consumer, provider
def find_versions_for_selector(selector)
PactBroker::Domain::Version.select_all_qualified.for_selector(selector).all
end

def find_latest_version_from_main_branch(pacticipant)
if pacticipant.main_branch
latest_from_main_branch = PactBroker::Domain::Version
.latest_versions_for_pacticipant_branches(pacticipant.id, pacticipant.main_branch)
.single_record

latest_from_main_branch || find_by_pacticipant_name_and_latest_tag(pacticipant.name, pacticipant.main_branch)
end
end
end
end
end
4 changes: 4 additions & 0 deletions lib/pact_broker/versions/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def self.create_or_update(pacticipant_name, version_number, version)
version_repository.create_or_update(pacticipant, version_number, version)
end

def self.find_latest_version_from_main_branch(pacticipant)
version_repository.find_latest_version_from_main_branch(pacticipant)
end

def self.delete version
tag_repository.delete_by_version_id version.id
webhook_repository.delete_triggered_webhooks_by_version_id version.id
Expand Down
3 changes: 3 additions & 0 deletions lib/pact_broker/webhooks/event_listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def initialize(webhook_options)

def contract_published(params)
handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED, params)
if verification_service.calculate_required_verifications_for_pact(params.fetch(:pact)).any?
handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_REQUIRING_VERIFICATION_PUBLISHED, params)
end
end

def contract_content_changed(params)
Expand Down
44 changes: 30 additions & 14 deletions lib/pact_broker/webhooks/trigger_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,6 @@ def create_triggered_webhooks_for_event pact, verification, event_name, event_co
end
end

# private
def create_triggered_webhooks_for_webhooks webhooks, pact, verification, event_name, event_context
webhooks.flat_map do | webhook |
expanded_event_contexts = expand_events_for_currently_deployed_environments(webhook, pact, event_context)
expanded_event_contexts = expanded_event_contexts.flat_map { | ec | expand_events_for_verification_of_multiple_selected_pacts(ec) }

expanded_event_contexts.collect do | expanded_event_context |
pact_for_triggered_webhook = verification ? find_pact_for_verification_triggered_webhook(pact, expanded_event_context) : pact
webhook_repository.create_triggered_webhook(next_uuid, webhook, pact_for_triggered_webhook, verification, RESOURCE_CREATION, event_name, expanded_event_context)
end
end
end

def schedule_webhooks(triggered_webhooks, options)
triggered_webhooks.each_with_index do | triggered_webhook, i |
logger.info "Scheduling job for webhook with uuid #{triggered_webhook.webhook.uuid}, context: #{triggered_webhook.event_context}"
Expand All @@ -76,14 +63,27 @@ def schedule_webhooks(triggered_webhooks, options)
end
end

private
def create_triggered_webhooks_for_webhooks webhooks, pact, verification, event_name, event_context
webhooks.flat_map do | webhook |
expanded_event_contexts = expand_events_for_currently_deployed_environments(webhook, pact, event_context)
expanded_event_contexts = expand_events_for_required_verifications(event_name, pact, expanded_event_contexts)
expanded_event_contexts = expanded_event_contexts.flat_map { | ec | expand_events_for_verification_of_multiple_selected_pacts(ec) }

expanded_event_contexts.collect do | expanded_event_context |
pact_for_triggered_webhook = verification ? find_pact_for_verification_triggered_webhook(pact, expanded_event_context) : pact
webhook_repository.create_triggered_webhook(next_uuid, webhook, pact_for_triggered_webhook, verification, RESOURCE_CREATION, event_name, expanded_event_context)
end
end
end
private :create_triggered_webhooks_for_webhooks

def merge_consumer_version_selectors(consumer_version_number, selectors, event_context)
event_context.merge(
consumer_version_number: consumer_version_number,
consumer_version_tags: selectors.collect{ | selector | selector[:tag] }.compact.uniq
)
end
private :merge_consumer_version_selectors

# Now that we de-duplicate the pact contents when verifying though the 'pacts for verification' API,
# we no longer get a webhook triggered for the verification results publication of each indiviual
Expand All @@ -103,6 +103,7 @@ def expand_events_for_verification_of_multiple_selected_pacts(event_context)
[event_context]
end
end
private :expand_events_for_verification_of_multiple_selected_pacts

def expand_events_for_currently_deployed_environments(webhook, pact, event_context)
if PactBroker.feature_enabled?(:expand_currently_deployed_provider_versions) && webhook.expand_currently_deployed_provider_versions?
Expand All @@ -113,6 +114,7 @@ def expand_events_for_currently_deployed_environments(webhook, pact, event_conte
[event_context]
end
end
private :expand_events_for_currently_deployed_environments

def find_pact_for_verification_triggered_webhook(pact, reconstituted_event_context)
if reconstituted_event_context[:consumer_version_number]
Expand All @@ -126,6 +128,20 @@ def find_pact_for_verification_triggered_webhook(pact, reconstituted_event_conte
pact
end
end
private :find_pact_for_verification_triggered_webhook

def expand_events_for_required_verifications(event_name, pact, event_contexts)
if event_name == PactBroker::Webhooks::WebhookEvent::CONTRACT_REQUIRING_VERIFICATION_PUBLISHED
required_verifications = verification_service.calculate_required_verifications_for_pact(pact)
event_contexts.flat_map do | event_context |
required_verifications.collect do | required_verification |
event_context.merge(provider_version_number: required_verification.provider_version.number, provider_version_descriptions: required_verification.provider_version_descriptions)
end
end
else
event_contexts
end
end
end
end
end
7 changes: 6 additions & 1 deletion lib/pact_broker/webhooks/webhook_event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ class WebhookEvent < Sequel::Model
VERIFICATION_SUCCEEDED = "provider_verification_succeeded"
VERIFICATION_FAILED = "provider_verification_failed"
DEFAULT_EVENT_NAME = CONTRACT_CONTENT_CHANGED
CONTRACT_REQUIRING_VERIFICATION_PUBLISHED = "contract_requiring_verification_published"

EVENT_NAMES = [CONTRACT_PUBLISHED, CONTRACT_CONTENT_CHANGED, VERIFICATION_PUBLISHED, VERIFICATION_SUCCEEDED, VERIFICATION_FAILED]
EVENT_NAMES = [CONTRACT_PUBLISHED, CONTRACT_CONTENT_CHANGED, VERIFICATION_PUBLISHED, VERIFICATION_SUCCEEDED, VERIFICATION_FAILED, CONTRACT_REQUIRING_VERIFICATION_PUBLISHED]

dataset_module do
include PactBroker::Repositories::Helpers
Expand All @@ -38,6 +39,10 @@ def provider_verification_succeeded?
def provider_verification_failed?
name == VERIFICATION_FAILED
end

def contract_requiring_verification_published?
name == CONTRACT_REQUIRING_VERIFICATION_PUBLISHED
end
end
end
end
Expand Down
Loading

0 comments on commit b4699df

Please sign in to comment.