Skip to content

Commit

Permalink
feat: update publish pacts command to use new 'all in one' contract p…
Browse files Browse the repository at this point in the history
…ublishing endpoint
  • Loading branch information
bethesque committed Apr 17, 2021
1 parent b1a8933 commit 50dfb11
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 185 deletions.
2 changes: 1 addition & 1 deletion example/scripts/publish-pact.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# assumes you've set PACT_BROKER_BASE_URL, PACT_BROKER_USERNAME and PACT_BROKER_PASSWORD already

bundle exec bin/pact-broker publish $(dirname "$0")/pact.json --consumer-app-version=1.0.0 --tag master
bundle exec bin/pact-broker publish $(dirname "$0")/pact.json --consumer-app-version=1.0.0 --tag master --verbose
5 changes: 3 additions & 2 deletions lib/pact_broker/client/cli/broker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ def publish(*pact_files)
require 'pact_broker/client/error'
validate_credentials
validate_pact_files(pact_files)
success = publish_pacts(pact_files)
raise PactPublicationError, "One or more pacts failed to be published" unless success
result = publish_pacts(pact_files)
$stdout.puts result.message
exit(1) unless result.success
rescue PactBroker::Client::Error => e
raise PactPublicationError, "#{e.class} - #{e.message}"
end
Expand Down
187 changes: 65 additions & 122 deletions lib/pact_broker/client/publish_pacts.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
require 'term/ansicolor'
require 'pact_broker/client'
require 'pact_broker/client/retry'
require 'pact_broker/client/pact_file'
require 'pact_broker/client/pact_hash'
require 'pact_broker/client/merge_pacts'
require 'pact_broker/client/hal_client_methods'
require 'pact_broker/client/hash_refinements'
require 'base64'
require 'pact_broker/client/publish_pacts_the_old_way'

module PactBroker
module Client
Expand All @@ -30,153 +26,100 @@ def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, p

def call
validate
$stdout.puts("")
result = create_consumer_versions && apply_tags && publish_pacts
$stdout.puts("")
result
if index_resource.can?("pb:publish-contracts")
publish_pacts
PactBroker::Client::CommandResult.new(success?, message)
else
PublishPactsTheOldWay.call(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options)
end
end

private

attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version_number, :branch, :tags, :build_url, :pact_broker_client_options, :version_required

def pact_broker_client
@pact_broker_client ||= PactBroker::Client::PactBrokerClient.new(base_url: pact_broker_base_url, client_options: pact_broker_client_options)
end

def index_entry_point
@index_entry_point ||= create_index_entry_point(pact_broker_base_url, pact_broker_client_options)
end

def index_resource
@index_resource ||= Retry.while_error do
index_entry_point.get!
end
end

def can_create_version_with_branch?
@can_create_version_with_branch ||= index_resource.can?('pb:pacticipant-version')
end
attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version_number, :branch, :tags, :build_url, :pact_broker_client_options, :response_entities

def merge_on_server?
pact_broker_client_options[:write] == :merge
def request_body_for(consumer_name)
{
pacticipantName: consumer_name,
versionNumber: consumer_version_number,
tags: tags,
branch: branch,
buildUrl: build_url,
contracts: contracts_for(consumer_name)
}.compact
end

def publish_pacts
pact_files.group_by(&:pact_name).collect do | pact_name, pact_files |
$stdout.puts "Merging #{pact_files.collect(&:path).join(", ")}" if pact_files.size > 1
publish_pact(PactHash[merge_contents(pact_files)])
end.all?
end

def merge_contents(pact_files)
MergePacts.call(pact_files.collect(&:pact_hash))
end

def pact_files
@pact_files ||= pact_file_paths.collect{ |pact_file_path| PactFile.new(pact_file_path) }
@response_entities = consumer_names.collect do | consumer_name |
index_resource._link("pb:publish-contracts").post(request_body_for(consumer_name))
end
end

def consumer_names
pact_files.collect(&:consumer_name).uniq
def success?
response_entities.all?(&:success?)
end

def publish_pact pact
begin
$stdout.puts "Publishing #{pact.pact_name} to pact broker at #{pact_broker_base_url}"
publish_pact_contents pact
rescue => e
$stderr.puts "Failed to publish #{pact.pact_name} due to error: #{e.class} - #{e}"
false
end
def message
response_entities.flat_map do | response_entity |
if response_entity.success?
response_entity.logs.collect do | log |
colorized_message(log)
end
else
::Term::ANSIColor.red(response_entity.response.body.to_s)
end
end.join("\n")
end

def create_consumer_versions
if create_versions?
consumer_names.collect do | consumer_name |
create_version(index_resource, consumer_name)
end
true
def colorized_message(log)
color = color_for_level(log["level"])
if color
::Term::ANSIColor.send(color, log["message"])
else
true
log["message"]
end
end

def create_versions?
if version_required
if can_create_version_with_branch?
true
else
raise PactBroker::Client::Error.new("This version of the Pact Broker does not support versions with branches or build URLs. Please upgrade your broker to 2.76.2 or later.")
end
elsif (branch || build_url) && can_create_version_with_branch?
true
else
false
def color_for_level(level)
case level
when "warn" then :yellow
when "error" then :red
when "info" then :green
else nil
end
end

def create_version(index_resource, consumer_name)
Retry.while_error do
version_resource = index_resource._link('pb:pacticipant-version').expand(version: consumer_version_number, pacticipant: consumer_name).put(version_body).assert_success!
message = if version_resource.response.status == 200
"Replaced version #{consumer_version_number} of #{consumer_name}"
else
"Created version #{consumer_version_number} of #{consumer_name}"
end
def contracts_for(consumer_name)
pact_files_for(consumer_name).collect do | pact_file |
end

message = message + " (branch #{branch})" if branch
$stdout.puts message
if version_resource.response.status == 200
$stdout.puts ::Term::ANSIColor.yellow("Replacing the version resource is not recommended under normal circumstances and may indicate that you have not configured your Pact pipeline correctly (unless you are just re-running a build for a particular commit). For more information see https://docs.pact.io/versioning")
end
true
pact_files_for(consumer_name).group_by(&:pact_name).values.collect do | pact_files |
$stderr.puts "Merging #{pact_files.collect(&:path).join(", ")}" if pact_files.size > 1
pact_hash = PactHash[merge_contents(pact_files)]
{
role: "consumer",
providerName: pact_hash.provider_name,
specification: "pact",
contentType: "application/json",
content: Base64.strict_encode64(pact_hash.to_json)
}
end
end

def version_body
{
branch: branch,
buildUrl: build_url
}.compact
def merge_contents(pact_files)
MergePacts.call(pact_files.collect(&:pact_hash))
end

def apply_tags
return true if tags.empty?
tags.all? do | tag |
tag_consumer_version tag
end
def pact_files
@pact_files ||= pact_file_paths.collect{ |pact_file_path| PactFile.new(pact_file_path) }
end

def tag_consumer_version tag
versions = pact_broker_client.pacticipants.versions
Retry.while_error do
consumer_names.collect do | consumer_name |
versions.tag(pacticipant: consumer_name, version: consumer_version_number, tag: tag)
$stdout.puts "Tagged version #{consumer_version_number} of #{consumer_name} as #{tag.inspect}"
true
end
end
rescue => e
$stderr.puts "Failed to tag version due to error: #{e.class} - #{e}"
false
def pact_files_for(consumer_name)
pact_files.select{ | pact_file | pact_file.consumer_name == consumer_name }
end

def publish_pact_contents(pact)
Retry.while_error do
pacts = pact_broker_client.pacticipants.versions.pacts
if pacts.version_published?(consumer: pact.consumer_name, provider: pact.provider_name, consumer_version: consumer_version_number)
if merge_on_server?
$stdout.puts "A pact for this consumer version is already published. Merging contents."
else
$stdout.puts ::Term::ANSIColor.yellow("A pact for this consumer version is already published. Overwriting. (Note: Overwriting pacts is not recommended as it can lead to race conditions. Best practice is to provide a unique consumer version number for each publication. For more information, see https://docs.pact.io/versioning)")
end
end

latest_pact_url = pacts.publish(pact_hash: pact, consumer_version: consumer_version_number)
$stdout.puts "The latest version of this pact can be accessed at the following URL:\n#{latest_pact_url}"
true
end
def consumer_names
pact_files.collect(&:consumer_name).uniq
end

def validate
Expand Down
Loading

0 comments on commit 50dfb11

Please sign in to comment.