pact_broker-client 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +17 -0
- data/README.md +2 -1
- data/bin/pact-broker +4 -0
- data/gemfiles/default.gemfile.lock +29 -24
- data/gemfiles/ruby_under_22.gemfile.lock +25 -22
- data/lib/pact_broker/client/base_client.rb +16 -1
- data/lib/pact_broker/client/can_i_deploy.rb +80 -0
- data/lib/pact_broker/client/cli/broker.rb +52 -0
- data/lib/pact_broker/client/cli/publish.rb +9 -7
- data/lib/pact_broker/client/cli/version_selector_options_parser.rb +29 -0
- data/lib/pact_broker/client/matrix.rb +39 -0
- data/lib/pact_broker/client/matrix/formatter.rb +20 -0
- data/lib/pact_broker/client/matrix/json_formatter.rb +13 -0
- data/lib/pact_broker/client/matrix/text_formatter.rb +33 -0
- data/lib/pact_broker/client/pact_broker_client.rb +5 -5
- data/lib/pact_broker/client/retry.rb +15 -4
- data/lib/pact_broker/client/version.rb +1 -1
- data/pact-broker-client.gemspec +2 -2
- data/spec/lib/pact_broker/client/can_i_deploy_spec.rb +85 -0
- data/spec/lib/pact_broker/client/cli/broker_spec.rb +81 -0
- data/spec/lib/pact_broker/client/cli/publish_spec.rb +6 -4
- data/spec/lib/pact_broker/client/cli/version_selector_options_parser_spec.rb +43 -0
- data/spec/lib/pact_broker/client/matrix/text_formatter_spec.rb +29 -0
- data/spec/lib/pact_broker/client/matrix_spec.rb +16 -0
- data/spec/pacts/pact_broker_client-pact_broker.json +83 -0
- data/spec/service_providers/pact_broker_client_matrix_spec.rb +119 -0
- data/spec/spec_helper.rb +0 -5
- data/spec/support/matrix.json +23 -0
- data/spec/support/matrix.txt +3 -0
- data/spec/support/matrix_error.txt +3 -0
- metadata +50 -23
@@ -14,7 +14,7 @@ module PactBroker
|
|
14
14
|
desc 'PACT_DIRS_OR_FILES ...', "Publish pacts to a Pact Broker."
|
15
15
|
method_option :consumer_app_version, required: true, aliases: "-a", desc: "The consumer application version"
|
16
16
|
method_option :broker_base_url, required: true, aliases: "-b", desc: "The base URL of the Pact Broker"
|
17
|
-
method_option :broker_username, aliases: "-
|
17
|
+
method_option :broker_username, aliases: "-u", desc: "Pact Broker basic auth username"
|
18
18
|
method_option :broker_password, aliases: "-p", desc: "Pact Broker basic auth password"
|
19
19
|
method_option :tag, aliases: "-t", type: :array, banner: "TAG", desc: "Tag name for consumer version. Can be specified multiple times."
|
20
20
|
method_option :verbose, aliases: "-v", desc: "Verbose output", :required => false
|
@@ -44,9 +44,9 @@ module PactBroker
|
|
44
44
|
|
45
45
|
def publish_pacts pact_files
|
46
46
|
PactBroker::Client::PublishPacts.call(
|
47
|
-
options
|
47
|
+
options.broker_base_url,
|
48
48
|
file_list(pact_files),
|
49
|
-
options
|
49
|
+
options.consumer_app_version,
|
50
50
|
tags,
|
51
51
|
pact_broker_client_options
|
52
52
|
)
|
@@ -63,14 +63,16 @@ module PactBroker
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def tags
|
66
|
-
[*options
|
66
|
+
[*options.tag].compact
|
67
67
|
end
|
68
68
|
|
69
69
|
def pact_broker_client_options
|
70
|
-
if options
|
70
|
+
if options.broker_password
|
71
71
|
{
|
72
|
-
|
73
|
-
|
72
|
+
basic_auth: {
|
73
|
+
username: options.broker_username,
|
74
|
+
password: options.broker_password
|
75
|
+
}
|
74
76
|
}
|
75
77
|
else
|
76
78
|
{}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PactBroker
|
2
|
+
module Client
|
3
|
+
module CLI
|
4
|
+
class VersionSelectorOptionsParser
|
5
|
+
def self.call options
|
6
|
+
versions = []
|
7
|
+
last_flag = nil
|
8
|
+
options.each do | option |
|
9
|
+
case option
|
10
|
+
when "--name", "-n"
|
11
|
+
versions << {}
|
12
|
+
when /^\-/
|
13
|
+
nil
|
14
|
+
else
|
15
|
+
case last_flag
|
16
|
+
when "--name", "-n"
|
17
|
+
versions.last[:name] = option
|
18
|
+
when "--version", "-a"
|
19
|
+
versions.last[:version] = option
|
20
|
+
end
|
21
|
+
end
|
22
|
+
last_flag = option if option.start_with?("-")
|
23
|
+
end
|
24
|
+
versions
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'base_client'
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Client
|
5
|
+
class Matrix < BaseClient
|
6
|
+
def get selectors
|
7
|
+
query = {selectors: convert_selector_hashes_to_params(selectors)}
|
8
|
+
response = self.class.get("/matrix", query: query, headers: default_get_headers)
|
9
|
+
$stdout.puts("DEBUG: Response headers #{response.headers}") if verbose?
|
10
|
+
$stdout.puts("DEBUG: Response body #{response}") if verbose?
|
11
|
+
response = handle_response(response) do
|
12
|
+
JSON.parse(response.body, symbolize_names: true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def handle_response response
|
17
|
+
if response.success?
|
18
|
+
yield
|
19
|
+
elsif response.code == 401
|
20
|
+
raise Error.new("Authentication failed")
|
21
|
+
elsif response.code == 404
|
22
|
+
raise Error.new("Matrix resource not found at #{base_url}/matrix. Please upgrade your broker to the latest version.")
|
23
|
+
else
|
24
|
+
error_message = nil
|
25
|
+
begin
|
26
|
+
error_message = JSON.parse(response.body)['errors'].join("\n")
|
27
|
+
rescue
|
28
|
+
raise Error.new(response.body)
|
29
|
+
end
|
30
|
+
raise Error.new(error_message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def convert_selector_hashes_to_params(selectors)
|
35
|
+
selectors.collect{ |selector| "#{selector[:name]}/version/#{selector[:version]}" }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pact_broker/client/matrix/json_formatter'
|
2
|
+
require 'pact_broker/client/matrix/text_formatter'
|
3
|
+
|
4
|
+
module PactBroker
|
5
|
+
module Client
|
6
|
+
class Matrix
|
7
|
+
class Formatter
|
8
|
+
def self.call(matrix_lines, format)
|
9
|
+
formatter = case format
|
10
|
+
when 'json' then JsonFormatter
|
11
|
+
when 'table' then TextFormatter
|
12
|
+
else
|
13
|
+
raise PactBroker::Client::Error.new("Invalid output option '#{format}. Must be one of 'table' or 'json'.")
|
14
|
+
end
|
15
|
+
formatter.call(matrix_lines)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'table_print'
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Client
|
5
|
+
class Matrix
|
6
|
+
class TextFormatter
|
7
|
+
|
8
|
+
Line = Struct.new(:consumer, :consumer_version, :provider, :provider_version, :success)
|
9
|
+
|
10
|
+
def self.call(matrix_lines)
|
11
|
+
data = matrix_lines.collect do | line |
|
12
|
+
Line.new(
|
13
|
+
lookup(line, :consumer, :name),
|
14
|
+
lookup(line, :consumer, :version, :number),
|
15
|
+
lookup(line, :consumer, :name),
|
16
|
+
lookup(line, :provider, :version, :number),
|
17
|
+
lookup(line, :verificationResult, :success).to_s
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
printer = TablePrint::Printer.new(data)
|
22
|
+
printer.table_print
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.lookup line, *keys
|
26
|
+
keys.reduce(line) { | line, key | line[key] }
|
27
|
+
rescue NoMethodError
|
28
|
+
"???"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -1,11 +1,9 @@
|
|
1
1
|
require 'pact_broker/client/pacticipants'
|
2
2
|
require 'pact_broker/client/versions'
|
3
3
|
require 'pact_broker/client/pacts'
|
4
|
-
|
4
|
+
require 'pact_broker/client/matrix'
|
5
5
|
|
6
6
|
module PactBroker
|
7
|
-
|
8
|
-
|
9
7
|
module Client
|
10
8
|
|
11
9
|
DEFAULT_PACT_BROKER_BASE_URL = 'http://pact-broker'
|
@@ -30,7 +28,9 @@ module PactBroker
|
|
30
28
|
PactBroker::Client::Pacts.new base_url: base_url, client_options: client_options
|
31
29
|
end
|
32
30
|
|
31
|
+
def matrix
|
32
|
+
PactBroker::Client::Matrix.new base_url: base_url, client_options: client_options
|
33
|
+
end
|
33
34
|
end
|
34
35
|
end
|
35
|
-
|
36
|
-
end
|
36
|
+
end
|
@@ -1,6 +1,18 @@
|
|
1
|
+
require 'pact_broker/client/error'
|
2
|
+
|
1
3
|
module PactBroker
|
2
4
|
module Client
|
3
5
|
class Retry
|
6
|
+
class RescuableError
|
7
|
+
UNRESCUEABLE = [PactBroker::Client::Error]
|
8
|
+
|
9
|
+
def self.===(e)
|
10
|
+
case e
|
11
|
+
when *UNRESCUEABLE then false
|
12
|
+
else true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
4
16
|
|
5
17
|
def self.until_true options = {}
|
6
18
|
max_tries = options.fetch(:times, 3)
|
@@ -8,9 +20,9 @@ module PactBroker
|
|
8
20
|
while true
|
9
21
|
begin
|
10
22
|
return yield
|
11
|
-
rescue
|
23
|
+
rescue RescuableError => e
|
12
24
|
tries += 1
|
13
|
-
$stderr.puts "Error
|
25
|
+
$stderr.puts "Error making request - #{e.message}, attempt #{tries} of #{max_tries}"
|
14
26
|
sleep options
|
15
27
|
raise e if max_tries == tries
|
16
28
|
end
|
@@ -20,7 +32,6 @@ module PactBroker
|
|
20
32
|
def self.sleep options
|
21
33
|
Kernel.sleep options.fetch(:sleep, 5)
|
22
34
|
end
|
23
|
-
|
24
35
|
end
|
25
36
|
end
|
26
|
-
end
|
37
|
+
end
|
data/pact-broker-client.gemspec
CHANGED
@@ -24,12 +24,12 @@ Gem::Specification.new do |gem|
|
|
24
24
|
gem.add_runtime_dependency 'httparty'
|
25
25
|
gem.add_runtime_dependency 'json'
|
26
26
|
gem.add_runtime_dependency 'term-ansicolor'
|
27
|
+
gem.add_runtime_dependency 'table_print', '~> 1.5'
|
27
28
|
|
28
|
-
gem.add_development_dependency 'pry'
|
29
29
|
gem.add_development_dependency 'fakefs', '~> 0.4'
|
30
|
-
gem.add_development_dependency 'rspec-fire'
|
31
30
|
gem.add_development_dependency 'appraisal'
|
32
31
|
gem.add_development_dependency 'webmock', '~> 3.0'
|
33
32
|
gem.add_development_dependency 'conventional-changelog'
|
34
33
|
gem.add_development_dependency 'pact', '~> 1.16'
|
34
|
+
gem.add_development_dependency 'pry-byebug'
|
35
35
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'pact_broker/client/can_i_deploy'
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Client
|
5
|
+
describe CanIDeploy do
|
6
|
+
let(:pact_broker_base_url) { 'http://example.org' }
|
7
|
+
let(:version_selectors) { [{name: "Foo", version: "1"}] }
|
8
|
+
let(:pact_broker_client_options) { { foo: 'bar' } }
|
9
|
+
let(:matrix_client) { instance_double('PactBroker::Client::Matrix') }
|
10
|
+
let(:matrix) { {matrix: ['foo'], summary: {compatible: true}} }
|
11
|
+
let(:options) { {output: 'text' } }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow_any_instance_of(PactBroker::Client::PactBrokerClient).to receive(:matrix).and_return(matrix_client)
|
15
|
+
allow(matrix_client).to receive(:get).and_return(matrix)
|
16
|
+
allow(Matrix::Formatter).to receive(:call).and_return('text matrix')
|
17
|
+
end
|
18
|
+
|
19
|
+
subject { CanIDeploy.call(pact_broker_base_url, version_selectors, options, pact_broker_client_options) }
|
20
|
+
|
21
|
+
it "retrieves the matrix from the pact broker" do
|
22
|
+
expect(matrix_client).to receive(:get).with(version_selectors)
|
23
|
+
subject
|
24
|
+
end
|
25
|
+
|
26
|
+
it "creates a text table out of the matrix" do
|
27
|
+
expect(Matrix::Formatter).to receive(:call).with(['foo'], 'text')
|
28
|
+
subject
|
29
|
+
end
|
30
|
+
|
31
|
+
context "when compatible versions are found" do
|
32
|
+
it "returns a success response" do
|
33
|
+
expect(subject.success).to be true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns a success message with the text table" do
|
37
|
+
expect(subject.message).to include "Computer says yes"
|
38
|
+
expect(subject.message).to include "\n\ntext matrix"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when compatible versions are not found" do
|
43
|
+
let(:matrix) { {matrix: ['foo'], summary: {compatible: false}} }
|
44
|
+
|
45
|
+
it "returns a failure response" do
|
46
|
+
expect(subject.success).to be false
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns a failure message" do
|
50
|
+
expect(subject.message).to include "Computer says no"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when a PactBroker::Client::Error is raised" do
|
55
|
+
before do
|
56
|
+
allow(matrix_client).to receive(:get).and_raise(PactBroker::Client::Error.new('error text'))
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns a failure response" do
|
60
|
+
expect(subject.success).to be false
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns a failure message" do
|
64
|
+
expect(subject.message).to eq "error text"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when a StandardError is raised" do
|
69
|
+
before do
|
70
|
+
allow(Retry).to receive(:sleep).and_return(0)
|
71
|
+
allow($stderr).to receive(:puts)
|
72
|
+
allow(matrix_client).to receive(:get).and_raise(StandardError.new('error text'))
|
73
|
+
end
|
74
|
+
|
75
|
+
it "returns a failure response" do
|
76
|
+
expect(subject.success).to be false
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns a failure message and backtrace" do
|
80
|
+
expect(subject.message).to include "Error retrieving matrix. StandardError - error text\n"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'pact_broker/client/cli/broker'
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Client
|
5
|
+
module CLI
|
6
|
+
describe Broker do
|
7
|
+
before do
|
8
|
+
subject.options = OpenStruct.new(minimum_valid_options)
|
9
|
+
allow(VersionSelectorOptionsParser).to receive(:call).and_return(version_selectors)
|
10
|
+
allow(CanIDeploy).to receive(:call).and_return(result)
|
11
|
+
allow($stdout).to receive(:puts)
|
12
|
+
allow($stderr).to receive(:puts)
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:result) { instance_double('PactBroker::Client::CanIDeploy::Result', success: success, message: message) }
|
16
|
+
let(:success) { true }
|
17
|
+
let(:message) { 'message' }
|
18
|
+
let(:version_selectors) { ['selector1'] }
|
19
|
+
let(:minimum_valid_options) do
|
20
|
+
{
|
21
|
+
broker_base_url: 'http://pact-broker',
|
22
|
+
output: 'table',
|
23
|
+
verbose: 'verbose'
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
let(:invoke_can_i_deploy) { subject.can_i_deploy }
|
28
|
+
|
29
|
+
it "parses the pacticipant names and versions" do
|
30
|
+
expect(VersionSelectorOptionsParser).to receive(:call).with(ARGV)
|
31
|
+
invoke_can_i_deploy
|
32
|
+
end
|
33
|
+
|
34
|
+
it "invokes the CanIDeploy service" do
|
35
|
+
expect(CanIDeploy).to receive(:call).with('http://pact-broker', version_selectors, {output: 'table'}, {verbose: 'verbose'})
|
36
|
+
invoke_can_i_deploy
|
37
|
+
end
|
38
|
+
|
39
|
+
context "with basic auth" do
|
40
|
+
before do
|
41
|
+
subject.options = OpenStruct.new(minimum_valid_options.merge(broker_username: 'foo', broker_password: 'bar'))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "invokes the CanIDeploy service with the basic auth credentials" do
|
45
|
+
expect(CanIDeploy).to receive(:call).with(anything, anything, anything, {basic_auth: {username: "foo", password: "bar"}, verbose: 'verbose'})
|
46
|
+
invoke_can_i_deploy
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when successful" do
|
51
|
+
it "prints the message to stdout" do
|
52
|
+
expect($stdout).to receive(:puts).with(message)
|
53
|
+
invoke_can_i_deploy
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when not successful" do
|
58
|
+
let(:success) { false }
|
59
|
+
|
60
|
+
it "prints the message to stderr" do
|
61
|
+
expect($stdout).to receive(:puts).with(message)
|
62
|
+
begin
|
63
|
+
invoke_can_i_deploy
|
64
|
+
rescue SystemExit
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "exits with code 1" do
|
69
|
+
exited_with_error = false
|
70
|
+
begin
|
71
|
+
invoke_can_i_deploy
|
72
|
+
rescue SystemExit
|
73
|
+
exited_with_error = true
|
74
|
+
end
|
75
|
+
expect(exited_with_error).to be true
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -5,7 +5,7 @@ module PactBroker::Client::CLI
|
|
5
5
|
describe ".broker" do
|
6
6
|
before do
|
7
7
|
allow(PactBroker::Client::PublishPacts).to receive(:call).and_return(true)
|
8
|
-
subject.options = minimum_valid_options
|
8
|
+
subject.options = OpenStruct.new(minimum_valid_options)
|
9
9
|
end
|
10
10
|
|
11
11
|
let(:file_list) { ["spec/support/cli_test_pacts/foo.json"] }
|
@@ -64,7 +64,7 @@ module PactBroker::Client::CLI
|
|
64
64
|
|
65
65
|
context "with a tag" do
|
66
66
|
before do
|
67
|
-
subject.options = minimum_valid_options.merge(tag: ['foo'])
|
67
|
+
subject.options = OpenStruct.new(minimum_valid_options.merge(tag: ['foo']))
|
68
68
|
end
|
69
69
|
|
70
70
|
it "passes in the tag" do
|
@@ -81,7 +81,9 @@ module PactBroker::Client::CLI
|
|
81
81
|
|
82
82
|
context "with basic auth options specified" do
|
83
83
|
before do
|
84
|
-
subject.options =
|
84
|
+
subject.options = OpenStruct.new(
|
85
|
+
minimum_valid_options.merge(broker_username: 'foo', broker_password: 'bar')
|
86
|
+
)
|
85
87
|
end
|
86
88
|
|
87
89
|
it "passes in the basic auth options" do
|
@@ -90,7 +92,7 @@ module PactBroker::Client::CLI
|
|
90
92
|
anything,
|
91
93
|
anything,
|
92
94
|
anything,
|
93
|
-
{username: 'foo', password: 'bar'}
|
95
|
+
hash_including({basic_auth: {username: 'foo', password: 'bar'}})
|
94
96
|
)
|
95
97
|
invoke_broker
|
96
98
|
end
|