pact_broker-client 1.32.0 → 1.37.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release_gem.yml +1 -0
- data/.github/workflows/test.yml +23 -0
- data/CHANGELOG.md +48 -0
- data/README.md +15 -3
- data/Rakefile +2 -0
- data/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +331 -8
- data/lib/pact_broker/client.rb +1 -1
- data/lib/pact_broker/client/backports.rb +13 -0
- data/lib/pact_broker/client/cli/broker.rb +73 -28
- data/lib/pact_broker/client/cli/can_i_deploy_long_desc.txt +18 -9
- data/lib/pact_broker/client/cli/custom_thor.rb +9 -12
- data/lib/pact_broker/client/git.rb +43 -22
- data/lib/pact_broker/client/hal/entity.rb +27 -3
- data/lib/pact_broker/client/hal/http_client.rb +4 -0
- data/lib/pact_broker/client/hal/links.rb +39 -0
- data/lib/pact_broker/client/hal_client_methods.rb +11 -0
- data/lib/pact_broker/client/hash_refinements.rb +19 -0
- data/lib/pact_broker/client/matrix.rb +2 -1
- data/lib/pact_broker/client/matrix/text_formatter.rb +46 -11
- data/lib/pact_broker/client/publish_pacts.rb +93 -14
- data/lib/pact_broker/client/tasks/publication_task.rb +37 -6
- data/lib/pact_broker/client/version.rb +1 -1
- data/lib/pact_broker/client/versions/record_deployment.rb +109 -0
- data/pact-broker-client.gemspec +2 -0
- data/script/publish-pact.sh +7 -1
- data/script/trigger-release.sh +1 -1
- data/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb +13 -2
- data/spec/lib/pact_broker/client/cli/broker_publish_spec.rb +108 -12
- data/spec/lib/pact_broker/client/git_spec.rb +39 -2
- data/spec/lib/pact_broker/client/hal/entity_spec.rb +4 -3
- data/spec/lib/pact_broker/client/matrix/text_formatter_spec.rb +29 -4
- data/spec/lib/pact_broker/client/publish_pacts_spec.rb +119 -7
- data/spec/lib/pact_broker/client/tasks/publication_task_spec.rb +88 -10
- data/spec/lib/pact_broker/client/versions/describe_spec.rb +0 -1
- data/spec/lib/pact_broker/client/versions/record_deployment_spec.rb +82 -0
- data/spec/pacts/pact_broker_client-pact_broker.json +335 -8
- data/spec/service_providers/pact_broker_client_create_version_spec.rb +89 -0
- data/spec/service_providers/pact_broker_client_matrix_spec.rb +4 -0
- data/spec/service_providers/pact_broker_client_versions_spec.rb +1 -2
- data/spec/service_providers/record_deployment_spec.rb +219 -0
- data/spec/support/matrix.json +6 -1
- data/spec/support/matrix.txt +3 -3
- data/spec/support/matrix_error.txt +3 -3
- data/spec/support/matrix_with_results.txt +10 -0
- data/tasks/pact.rake +2 -0
- metadata +44 -4
- data/.travis.yml +0 -11
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'delegate'
|
3
|
+
|
4
|
+
module PactBroker
|
5
|
+
module Client
|
6
|
+
module Hal
|
7
|
+
class Links
|
8
|
+
def initialize(href, key, links)
|
9
|
+
@href = href
|
10
|
+
@key = key
|
11
|
+
@links = links
|
12
|
+
end
|
13
|
+
|
14
|
+
def names
|
15
|
+
@names ||= links.collect(&:name).compact.uniq
|
16
|
+
end
|
17
|
+
|
18
|
+
def find!(name, not_found_message = nil)
|
19
|
+
link = find(name)
|
20
|
+
if link
|
21
|
+
link
|
22
|
+
else
|
23
|
+
message = not_found_message || "Could not find relation '#{key}' with name '#{name}' in resource at #{href}."
|
24
|
+
available_options = names.any? ? names.join(", ") : "<none found>"
|
25
|
+
raise RelationNotFoundError.new(message.chomp(".") + ". Available options: #{available_options}")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def find(name)
|
30
|
+
links.find{ | link | link.name == name }
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
attr_reader :links, :key, :href
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pact_broker/client/hal'
|
2
|
+
require 'pact_broker/client/retry'
|
2
3
|
|
3
4
|
module PactBroker
|
4
5
|
module Client
|
@@ -10,6 +11,16 @@ module PactBroker
|
|
10
11
|
def create_http_client(pact_broker_client_options)
|
11
12
|
PactBroker::Client::Hal::HttpClient.new(pact_broker_client_options.merge(pact_broker_client_options[:basic_auth] || {}))
|
12
13
|
end
|
14
|
+
|
15
|
+
def index_entry_point
|
16
|
+
@index_entry_point ||= create_index_entry_point(pact_broker_base_url, pact_broker_client_options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def index_resource
|
20
|
+
@index_resource ||= Retry.while_error do
|
21
|
+
index_entry_point.get!
|
22
|
+
end
|
23
|
+
end
|
13
24
|
end
|
14
25
|
end
|
15
26
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PactBroker
|
2
|
+
module Client
|
3
|
+
module HashRefinements
|
4
|
+
refine Hash do
|
5
|
+
def compact
|
6
|
+
h = {}
|
7
|
+
each do |key, value|
|
8
|
+
h[key] = value unless value == nil
|
9
|
+
end
|
10
|
+
h
|
11
|
+
end unless Hash.method_defined? :compact
|
12
|
+
|
13
|
+
def compact!
|
14
|
+
reject! {|_key, value| value == nil}
|
15
|
+
end unless Hash.method_defined? :compact!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -53,10 +53,11 @@ module PactBroker
|
|
53
53
|
opts[:success] = [*options[:success]]
|
54
54
|
end
|
55
55
|
opts[:limit] = options[:limit] if options[:limit]
|
56
|
+
opts[:environment] = options[:to_environment] if options[:to_environment]
|
56
57
|
if options[:to_tag]
|
57
58
|
opts[:latest] = 'true'
|
58
59
|
opts[:tag] = options[:to_tag]
|
59
|
-
elsif selectors.size == 1
|
60
|
+
elsif selectors.size == 1 && !options[:to_environment]
|
60
61
|
opts[:latest] = 'true'
|
61
62
|
end
|
62
63
|
opts
|
@@ -1,41 +1,76 @@
|
|
1
1
|
require 'table_print'
|
2
|
+
require 'dig_rb'
|
3
|
+
require 'pact_broker/client/hash_refinements'
|
2
4
|
|
3
5
|
module PactBroker
|
4
6
|
module Client
|
5
7
|
class Matrix
|
6
8
|
class TextFormatter
|
9
|
+
using PactBroker::Client::HashRefinements
|
7
10
|
|
8
|
-
Line = Struct.new(:consumer, :consumer_version, :provider, :provider_version, :success)
|
11
|
+
Line = Struct.new(:consumer, :consumer_version, :provider, :provider_version, :success, :ref)
|
9
12
|
|
10
13
|
OPTIONS = [
|
11
14
|
{ consumer: {} },
|
12
15
|
{ consumer_version: {display_name: 'C.VERSION'} },
|
13
16
|
{ provider: {} },
|
14
17
|
{ provider_version: {display_name: 'P.VERSION'} },
|
15
|
-
{ success: {display_name: 'SUCCESS?'} }
|
18
|
+
{ success: {display_name: 'SUCCESS?'} },
|
19
|
+
{ ref: { display_name: 'RESULT#' }}
|
16
20
|
]
|
17
21
|
|
18
22
|
def self.call(matrix)
|
19
23
|
matrix_rows = matrix[:matrix]
|
20
24
|
return "" if matrix_rows.size == 0
|
21
|
-
|
25
|
+
verification_result_number = 0
|
26
|
+
data = matrix_rows.each_with_index.collect do | line |
|
27
|
+
has_verification_result_url = lookup(line, nil, :verificationResult, :_links, :self, :href)
|
28
|
+
if has_verification_result_url
|
29
|
+
verification_result_number += 1
|
30
|
+
end
|
22
31
|
Line.new(
|
23
|
-
lookup(line, :consumer, :name),
|
24
|
-
lookup(line, :consumer, :version, :number),
|
25
|
-
lookup(line, :provider, :name),
|
26
|
-
lookup(line, :provider, :version, :number),
|
27
|
-
lookup(line, :verificationResult, :success).to_s
|
32
|
+
lookup(line, "???", :consumer, :name),
|
33
|
+
lookup(line, "???", :consumer, :version, :number),
|
34
|
+
lookup(line, "???", :provider, :name) ,
|
35
|
+
lookup(line, "???", :provider, :version, :number),
|
36
|
+
(lookup(line, "???", :verificationResult, :success)).to_s,
|
37
|
+
has_verification_result_url ? verification_result_number : ""
|
28
38
|
)
|
29
39
|
end
|
30
40
|
|
31
41
|
printer = TablePrint::Printer.new(data, OPTIONS)
|
32
|
-
printer.table_print
|
42
|
+
printer.table_print + verification_result_urls_text(matrix)
|
33
43
|
end
|
34
44
|
|
35
|
-
def self.lookup line, *keys
|
45
|
+
def self.lookup line, default, *keys
|
36
46
|
keys.reduce(line) { | line, key | line[key] }
|
37
47
|
rescue NoMethodError
|
38
|
-
|
48
|
+
default
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.verification_results_urls_and_successes(matrix)
|
52
|
+
(matrix[:matrix] || []).collect do | row |
|
53
|
+
url = row.dig(:verificationResult, :_links, :self, :href)
|
54
|
+
if url
|
55
|
+
success = row.dig(:verificationResult, :success)
|
56
|
+
[url, success]
|
57
|
+
else
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
end.compact
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.verification_result_urls_text(matrix)
|
64
|
+
text = self.verification_results_urls_and_successes(matrix).each_with_index.collect do |(url, success), i|
|
65
|
+
status = success ? 'success' : 'failure'
|
66
|
+
"#{i+1}. #{url} (#{status})"
|
67
|
+
end.join("\n")
|
68
|
+
|
69
|
+
if text.size > 0
|
70
|
+
"\n\nVERIFICATION RESULTS\n--------------------\n#{text}"
|
71
|
+
else
|
72
|
+
text
|
73
|
+
end
|
39
74
|
end
|
40
75
|
end
|
41
76
|
end
|
@@ -4,39 +4,64 @@ require 'pact_broker/client/retry'
|
|
4
4
|
require 'pact_broker/client/pact_file'
|
5
5
|
require 'pact_broker/client/pact_hash'
|
6
6
|
require 'pact_broker/client/merge_pacts'
|
7
|
+
require 'pact_broker/client/hal_client_methods'
|
8
|
+
require 'pact_broker/client/hash_refinements'
|
7
9
|
|
8
10
|
module PactBroker
|
9
11
|
module Client
|
10
12
|
class PublishPacts
|
13
|
+
using PactBroker::Client::HashRefinements
|
14
|
+
include HalClientMethods
|
11
15
|
|
12
|
-
def self.call(pact_broker_base_url, pact_file_paths,
|
13
|
-
new(pact_broker_base_url, pact_file_paths,
|
16
|
+
def self.call(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={})
|
17
|
+
new(pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options).call
|
14
18
|
end
|
15
19
|
|
16
|
-
def initialize pact_broker_base_url, pact_file_paths,
|
20
|
+
def initialize pact_broker_base_url, pact_file_paths, consumer_version_params, pact_broker_client_options={}
|
17
21
|
@pact_broker_base_url = pact_broker_base_url
|
18
22
|
@pact_file_paths = pact_file_paths
|
19
|
-
@
|
20
|
-
@
|
23
|
+
@consumer_version_number = consumer_version_params[:number].respond_to?(:strip) ? consumer_version_params[:number].strip : consumer_version_params[:number]
|
24
|
+
@branch = consumer_version_params[:branch]
|
25
|
+
@build_url = consumer_version_params[:build_url]
|
26
|
+
@tags = consumer_version_params[:tags] ? consumer_version_params[:tags].collect{ |tag| tag.respond_to?(:strip) ? tag.strip : tag } : []
|
27
|
+
@version_required = consumer_version_params[:version_required]
|
21
28
|
@pact_broker_client_options = pact_broker_client_options
|
22
29
|
end
|
23
30
|
|
24
31
|
def call
|
25
32
|
validate
|
26
33
|
$stdout.puts("")
|
27
|
-
result = apply_tags && publish_pacts
|
34
|
+
result = create_consumer_versions && apply_tags && publish_pacts
|
28
35
|
$stdout.puts("")
|
29
36
|
result
|
30
37
|
end
|
31
38
|
|
32
39
|
private
|
33
40
|
|
34
|
-
attr_reader :pact_broker_base_url, :pact_file_paths, :
|
41
|
+
attr_reader :pact_broker_base_url, :pact_file_paths, :consumer_version_number, :branch, :tags, :build_url, :pact_broker_client_options, :version_required
|
35
42
|
|
36
43
|
def pact_broker_client
|
37
44
|
@pact_broker_client ||= PactBroker::Client::PactBrokerClient.new(base_url: pact_broker_base_url, client_options: pact_broker_client_options)
|
38
45
|
end
|
39
46
|
|
47
|
+
def index_entry_point
|
48
|
+
@index_entry_point ||= create_index_entry_point(pact_broker_base_url, pact_broker_client_options)
|
49
|
+
end
|
50
|
+
|
51
|
+
def index_resource
|
52
|
+
@index_resource ||= Retry.while_error do
|
53
|
+
index_entry_point.get!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def can_create_version_with_branch?
|
58
|
+
@can_create_version_with_branch ||= index_resource.can?('pb:pacticipant-version')
|
59
|
+
end
|
60
|
+
|
61
|
+
def merge_on_server?
|
62
|
+
pact_broker_client_options[:write] == :merge
|
63
|
+
end
|
64
|
+
|
40
65
|
def publish_pacts
|
41
66
|
pact_files.group_by(&:pact_name).collect do | pact_name, pact_files |
|
42
67
|
$stdout.puts "Merging #{pact_files.collect(&:path).join(", ")}" if pact_files.size > 1
|
@@ -66,8 +91,58 @@ module PactBroker
|
|
66
91
|
end
|
67
92
|
end
|
68
93
|
|
94
|
+
def create_consumer_versions
|
95
|
+
if create_versions?
|
96
|
+
consumer_names.collect do | consumer_name |
|
97
|
+
create_version(index_resource, consumer_name)
|
98
|
+
end
|
99
|
+
true
|
100
|
+
else
|
101
|
+
true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_versions?
|
106
|
+
if version_required
|
107
|
+
if can_create_version_with_branch?
|
108
|
+
true
|
109
|
+
else
|
110
|
+
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.")
|
111
|
+
end
|
112
|
+
elsif (branch || build_url) && can_create_version_with_branch?
|
113
|
+
true
|
114
|
+
else
|
115
|
+
false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def create_version(index_resource, consumer_name)
|
120
|
+
Retry.while_error do
|
121
|
+
version_resource = index_resource._link('pb:pacticipant-version').expand(version: consumer_version_number, pacticipant: consumer_name).put(version_body).assert_success!
|
122
|
+
message = if version_resource.response.status == 200
|
123
|
+
"Replaced version #{consumer_version_number} of #{consumer_name}"
|
124
|
+
else
|
125
|
+
"Created version #{consumer_version_number} of #{consumer_name}"
|
126
|
+
end
|
127
|
+
|
128
|
+
message = message + " (branch #{branch})" if branch
|
129
|
+
$stdout.puts message
|
130
|
+
if version_resource.response.status == 200
|
131
|
+
$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")
|
132
|
+
end
|
133
|
+
true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def version_body
|
138
|
+
{
|
139
|
+
branch: branch,
|
140
|
+
buildUrl: build_url
|
141
|
+
}.compact
|
142
|
+
end
|
143
|
+
|
69
144
|
def apply_tags
|
70
|
-
return true if tags.
|
145
|
+
return true if tags.empty?
|
71
146
|
tags.all? do | tag |
|
72
147
|
tag_consumer_version tag
|
73
148
|
end
|
@@ -77,8 +152,8 @@ module PactBroker
|
|
77
152
|
versions = pact_broker_client.pacticipants.versions
|
78
153
|
Retry.while_error do
|
79
154
|
consumer_names.collect do | consumer_name |
|
80
|
-
versions.tag(pacticipant: consumer_name, version:
|
81
|
-
$stdout.puts "Tagged version #{
|
155
|
+
versions.tag(pacticipant: consumer_name, version: consumer_version_number, tag: tag)
|
156
|
+
$stdout.puts "Tagged version #{consumer_version_number} of #{consumer_name} as #{tag.inspect}"
|
82
157
|
true
|
83
158
|
end
|
84
159
|
end
|
@@ -90,18 +165,22 @@ module PactBroker
|
|
90
165
|
def publish_pact_contents(pact)
|
91
166
|
Retry.while_error do
|
92
167
|
pacts = pact_broker_client.pacticipants.versions.pacts
|
93
|
-
if pacts.version_published?(consumer: pact.consumer_name, provider: pact.provider_name, consumer_version:
|
94
|
-
|
168
|
+
if pacts.version_published?(consumer: pact.consumer_name, provider: pact.provider_name, consumer_version: consumer_version_number)
|
169
|
+
if merge_on_server?
|
170
|
+
$stdout.puts "A pact for this consumer version is already published. Merging contents."
|
171
|
+
else
|
172
|
+
$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)")
|
173
|
+
end
|
95
174
|
end
|
96
175
|
|
97
|
-
latest_pact_url = pacts.publish(pact_hash: pact, consumer_version:
|
176
|
+
latest_pact_url = pacts.publish(pact_hash: pact, consumer_version: consumer_version_number)
|
98
177
|
$stdout.puts "The latest version of this pact can be accessed at the following URL:\n#{latest_pact_url}"
|
99
178
|
true
|
100
179
|
end
|
101
180
|
end
|
102
181
|
|
103
182
|
def validate
|
104
|
-
raise PactBroker::Client::Error.new("Please specify the
|
183
|
+
raise PactBroker::Client::Error.new("Please specify the consumer_version_number") unless (consumer_version_number && consumer_version_number.to_s.strip.size > 0)
|
105
184
|
raise PactBroker::Client::Error.new("Please specify the pact_broker_base_url") unless (pact_broker_base_url && pact_broker_base_url.to_s.strip.size > 0)
|
106
185
|
raise PactBroker::Client::Error.new("No pact files found") unless (pact_file_paths && pact_file_paths.any?)
|
107
186
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rake/tasklib'
|
2
2
|
require 'pact_broker/client/git'
|
3
|
+
require 'pact_broker/client/hash_refinements'
|
3
4
|
|
4
5
|
=begin
|
5
6
|
require pact_broker/client/tasks
|
@@ -17,31 +18,52 @@ end
|
|
17
18
|
module PactBroker
|
18
19
|
module Client
|
19
20
|
class PublicationTask < ::Rake::TaskLib
|
21
|
+
using PactBroker::Client::HashRefinements
|
20
22
|
|
21
23
|
attr_accessor :pattern, :pact_broker_base_url, :consumer_version, :tag, :write_method, :tag_with_git_branch, :pact_broker_basic_auth, :pact_broker_token
|
24
|
+
attr_reader :auto_detect_version_properties, :branch, :build_url
|
22
25
|
alias_method :tags=, :tag=
|
23
26
|
alias_method :tags, :tag
|
24
27
|
|
25
28
|
def initialize name = nil, &block
|
26
29
|
@name = name
|
30
|
+
@auto_detect_version_properties = nil
|
31
|
+
@version_required = false
|
27
32
|
@pattern = 'spec/pacts/*.json'
|
28
33
|
@pact_broker_base_url = 'http://pact-broker'
|
29
34
|
rake_task &block
|
30
35
|
end
|
31
36
|
|
37
|
+
def auto_detect_version_properties= auto_detect_version_properties
|
38
|
+
@version_required = version_required || auto_detect_version_properties
|
39
|
+
@auto_detect_version_properties = auto_detect_version_properties
|
40
|
+
end
|
41
|
+
|
42
|
+
def branch= branch
|
43
|
+
@version_required = version_required || !!branch
|
44
|
+
@branch = branch
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_url= build_url
|
48
|
+
@version_required = version_required || !!build_url
|
49
|
+
@build_url = build_url
|
50
|
+
end
|
51
|
+
|
32
52
|
private
|
33
53
|
|
54
|
+
attr_reader :version_required
|
55
|
+
|
34
56
|
def rake_task &block
|
35
57
|
namespace :pact do
|
36
58
|
desc "Publish pacts to pact broker"
|
37
59
|
task task_name do
|
38
60
|
block.call(self)
|
39
61
|
require 'pact_broker/client/publish_pacts'
|
40
|
-
pact_broker_client_options =
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
success = PactBroker::Client::PublishPacts.new(pact_broker_base_url, FileList[pattern],
|
62
|
+
pact_broker_client_options = { write: write_method, token: pact_broker_token }
|
63
|
+
pact_broker_client_options[:basic_auth] = pact_broker_basic_auth if pact_broker_basic_auth && pact_broker_basic_auth.any?
|
64
|
+
pact_broker_client_options.compact!
|
65
|
+
consumer_version_params = { number: consumer_version, branch: the_branch, build_url: build_url, tags: all_tags, version_required: version_required }.compact
|
66
|
+
success = PactBroker::Client::PublishPacts.new(pact_broker_base_url, FileList[pattern], consumer_version_params, pact_broker_client_options).call
|
45
67
|
raise "One or more pacts failed to be published" unless success
|
46
68
|
end
|
47
69
|
end
|
@@ -53,9 +75,18 @@ module PactBroker
|
|
53
75
|
|
54
76
|
def all_tags
|
55
77
|
t = [*tags]
|
56
|
-
t << PactBroker::Client::Git.branch if tag_with_git_branch
|
78
|
+
t << PactBroker::Client::Git.branch(raise_error: true) if tag_with_git_branch
|
57
79
|
t.compact.uniq
|
58
80
|
end
|
81
|
+
|
82
|
+
def the_branch
|
83
|
+
if branch.nil? && auto_detect_version_properties != false
|
84
|
+
PactBroker::Client::Git.branch(raise_error: auto_detect_version_properties == true)
|
85
|
+
else
|
86
|
+
branch
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
59
90
|
end
|
60
91
|
end
|
61
92
|
end
|