pact_broker-client 1.6.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|