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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +17 -0
  4. data/README.md +2 -1
  5. data/bin/pact-broker +4 -0
  6. data/gemfiles/default.gemfile.lock +29 -24
  7. data/gemfiles/ruby_under_22.gemfile.lock +25 -22
  8. data/lib/pact_broker/client/base_client.rb +16 -1
  9. data/lib/pact_broker/client/can_i_deploy.rb +80 -0
  10. data/lib/pact_broker/client/cli/broker.rb +52 -0
  11. data/lib/pact_broker/client/cli/publish.rb +9 -7
  12. data/lib/pact_broker/client/cli/version_selector_options_parser.rb +29 -0
  13. data/lib/pact_broker/client/matrix.rb +39 -0
  14. data/lib/pact_broker/client/matrix/formatter.rb +20 -0
  15. data/lib/pact_broker/client/matrix/json_formatter.rb +13 -0
  16. data/lib/pact_broker/client/matrix/text_formatter.rb +33 -0
  17. data/lib/pact_broker/client/pact_broker_client.rb +5 -5
  18. data/lib/pact_broker/client/retry.rb +15 -4
  19. data/lib/pact_broker/client/version.rb +1 -1
  20. data/pact-broker-client.gemspec +2 -2
  21. data/spec/lib/pact_broker/client/can_i_deploy_spec.rb +85 -0
  22. data/spec/lib/pact_broker/client/cli/broker_spec.rb +81 -0
  23. data/spec/lib/pact_broker/client/cli/publish_spec.rb +6 -4
  24. data/spec/lib/pact_broker/client/cli/version_selector_options_parser_spec.rb +43 -0
  25. data/spec/lib/pact_broker/client/matrix/text_formatter_spec.rb +29 -0
  26. data/spec/lib/pact_broker/client/matrix_spec.rb +16 -0
  27. data/spec/pacts/pact_broker_client-pact_broker.json +83 -0
  28. data/spec/service_providers/pact_broker_client_matrix_spec.rb +119 -0
  29. data/spec/spec_helper.rb +0 -5
  30. data/spec/support/matrix.json +23 -0
  31. data/spec/support/matrix.txt +3 -0
  32. data/spec/support/matrix_error.txt +3 -0
  33. 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: "-n", desc: "Pact Broker basic auth username"
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[:broker_base_url],
47
+ options.broker_base_url,
48
48
  file_list(pact_files),
49
- options[:consumer_app_version],
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[:tag]].compact
66
+ [*options.tag].compact
67
67
  end
68
68
 
69
69
  def pact_broker_client_options
70
- if options[:username]
70
+ if options.broker_password
71
71
  {
72
- username: options[:username],
73
- password: options[:password]
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,13 @@
1
+ require 'table_print'
2
+
3
+ module PactBroker
4
+ module Client
5
+ class Matrix
6
+ class JsonFormatter
7
+ def self.call(matrix_lines)
8
+ JSON.pretty_generate(matrix: matrix_lines)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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 StandardError => e
23
+ rescue RescuableError => e
12
24
  tries += 1
13
- $stderr.puts "Error publishing pact - #{e.message}, attempt #{tries} of #{max_tries}"
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
@@ -1,5 +1,5 @@
1
1
  module PactBroker
2
2
  module Client
3
- VERSION = '1.6.0'
3
+ VERSION = '1.7.0'
4
4
  end
5
5
  end
@@ -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 = minimum_valid_options.merge(username: 'foo', password: 'bar')
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