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.
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