pact_broker-client 1.36.0 → 1.38.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release_gem.yml +1 -0
  3. data/CHANGELOG.md +32 -0
  4. data/Rakefile +2 -0
  5. data/bin/pact-broker +6 -0
  6. data/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +180 -0
  7. data/lib/pact_broker/client.rb +1 -1
  8. data/lib/pact_broker/client/backports.rb +13 -0
  9. data/lib/pact_broker/client/cli/broker.rb +85 -30
  10. data/lib/pact_broker/client/cli/create_or_update_webhook_long_desc.txt +3 -1
  11. data/lib/pact_broker/client/cli/create_webhook_long_desc.txt +2 -0
  12. data/lib/pact_broker/client/cli/custom_thor.rb +11 -17
  13. data/lib/pact_broker/client/git.rb +3 -0
  14. data/lib/pact_broker/client/hal/entity.rb +44 -3
  15. data/lib/pact_broker/client/hal/http_client.rb +38 -2
  16. data/lib/pact_broker/client/hal/links.rb +39 -0
  17. data/lib/pact_broker/client/hal_client_methods.rb +11 -0
  18. data/lib/pact_broker/client/hash_refinements.rb +19 -0
  19. data/lib/pact_broker/client/matrix/text_formatter.rb +2 -0
  20. data/lib/pact_broker/client/publish_pacts.rb +2 -1
  21. data/lib/pact_broker/client/tasks/publication_task.rb +2 -0
  22. data/lib/pact_broker/client/version.rb +1 -1
  23. data/lib/pact_broker/client/versions/record_deployment.rb +109 -0
  24. data/lib/pact_broker/client/versions/record_undeployment.rb +125 -0
  25. data/pact-broker-client.gemspec +1 -1
  26. data/script/record-deployment.sh +4 -0
  27. data/script/trigger-release.sh +1 -1
  28. data/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb +38 -4
  29. data/spec/lib/pact_broker/client/cli/broker_publish_spec.rb +1 -1
  30. data/spec/lib/pact_broker/client/cli/custom_thor_spec.rb +1 -7
  31. data/spec/lib/pact_broker/client/hal/entity_spec.rb +4 -3
  32. data/spec/lib/pact_broker/client/hal/http_client_spec.rb +64 -7
  33. data/spec/lib/pact_broker/client/versions/describe_spec.rb +0 -1
  34. data/spec/lib/pact_broker/client/versions/record_deployment_spec.rb +82 -0
  35. data/spec/pacts/pact_broker_client-pact_broker.json +181 -0
  36. data/spec/service_providers/record_deployment_spec.rb +219 -0
  37. data/spec/spec_helper.rb +2 -0
  38. metadata +17 -7
@@ -0,0 +1,125 @@
1
+ require 'pact_broker/client/hal_client_methods'
2
+ require 'pact_broker/client/error'
3
+ require 'pact_broker/client/command_result'
4
+
5
+ module PactBroker
6
+ module Client
7
+ class Versions
8
+ class RecordUndeployment
9
+ include PactBroker::Client::HalClientMethods
10
+
11
+ NOT_SUPPORTED_MESSAGE = "This version of the Pact Broker does not support recording undeployments. Please upgrade to version 2.80.0 or later."
12
+
13
+ def self.call(params, pact_broker_base_url, pact_broker_client_options)
14
+ new(params, pact_broker_base_url, pact_broker_client_options).call
15
+ end
16
+
17
+ def initialize(params, pact_broker_base_url, pact_broker_client_options)
18
+ @pact_broker_base_url = pact_broker_base_url
19
+ @pacticipant_name = params.fetch(:pacticipant_name)
20
+ @version_number = params.fetch(:version_number)
21
+ @environment_name = params.fetch(:environment_name)
22
+ @output = params.fetch(:output)
23
+ @pact_broker_client_options = pact_broker_client_options
24
+ end
25
+
26
+ def call
27
+ check_environment_exists
28
+ # record_undeployment
29
+
30
+ PactBroker::Client::CommandResult.new(true, result_message)
31
+ rescue PactBroker::Client::Error => e
32
+ PactBroker::Client::CommandResult.new(false, e.message)
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :pact_broker_base_url, :pact_broker_client_options
38
+ attr_reader :pacticipant_name, :version_number, :environment_name, :replaced_previous_deployed_version, :output
39
+ attr_reader :deployed_version_resource
40
+
41
+ def check_environment_exists
42
+ deployed_versions = currently_deployed_versions_for_pacticipant
43
+ .get(version: version_number)
44
+ .embedded_entities("deployedVersions")
45
+ deployed_versions
46
+ end
47
+
48
+ def currently_deployed_versions_for_pacticipant
49
+ @currently_deployed_versions_for_pacticipant ||= index_resource
50
+ ._link!("pb:environments")
51
+ .get!(name: environment_name)
52
+ .embedded_entities("environments")
53
+ .tap { |it| raise "Environment not found '#{environment_name}'" if it.empty? }
54
+ .first
55
+ ._link!("pb:currently-deployed-versions-for-pacticipant")
56
+ .expand(pacticipant: pacticipant_name)
57
+ end
58
+
59
+ def currently_deployed_versions_for_pacticipant_version
60
+ @currently_deployed_versions ||= currently_deployed_versions_for_pacticipant
61
+ .get(version: version_number)
62
+ .embedded_entities("deployedVersions")
63
+ end
64
+
65
+ # def record_deployment
66
+ # @deployed_version_resource =
67
+ # get_record_deployment_relation
68
+ # .post(record_deployment_request_body)
69
+ # .assert_success!
70
+ # end
71
+
72
+ # def get_record_deployment_relation
73
+ # record_deployment_links = get_pacticipant_version._links!("pb:record-deployment")
74
+ # link_for_environment = record_deployment_links.find(environment_name)
75
+ # if link_for_environment
76
+ # link_for_environment
77
+ # else
78
+ # check_environment_exists
79
+ # # Force the exception to be raised
80
+ # record_deployment_links.find!(environment_name, "Environment '#{environment_name}' is not an available option for recording a deployment of #{pacticipant_name}.")
81
+ # end
82
+ # end
83
+
84
+ # def get_pacticipant_version
85
+ # index_resource
86
+ # ._link!("pb:pacticipant-version")
87
+ # .expand(pacticipant: pacticipant_name, version: version_number)
88
+ # .get
89
+ # .assert_success!(404 => "#{pacticipant_name} version #{version_number} not found")
90
+ # end
91
+
92
+ # def record_deployment_request_body
93
+ # { replacedPreviousDeployedVersion: replaced_previous_deployed_version }
94
+ # end
95
+
96
+ def result_message
97
+ ""
98
+ # if output == "text"
99
+ # message = "Recorded deployment of #{pacticipant_name} version #{version_number} to #{environment_name} in #{pact_broker_name}."
100
+ # suffix = replaced_previous_deployed_version ? " Marked previous deployed version as undeployed." : ""
101
+ # message + suffix
102
+ # elsif output == "json"
103
+ # deployed_version_resource.response.raw_body
104
+ # else
105
+ # ""
106
+ # end
107
+ end
108
+
109
+ def pact_broker_name
110
+ is_pactflow? ? "Pactflow" : "the Pact Broker"
111
+ end
112
+
113
+ def is_pactflow?
114
+ deployed_version_resource.response.headers.keys.any?{ | header_name | header_name.downcase.include?("pactflow") }
115
+ end
116
+
117
+ def check_if_command_supported
118
+ unless index_resource.can?("pb:environments")
119
+ raise PactBroker::Client::Error.new(NOT_SUPPORTED_MESSAGE)
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -32,5 +32,5 @@ Gem::Specification.new do |gem|
32
32
  gem.add_development_dependency 'webmock', '~> 3.0'
33
33
  gem.add_development_dependency 'conventional-changelog', '~>1.3'
34
34
  gem.add_development_dependency 'pact', '~> 1.16'
35
- gem.add_development_dependency 'pact-support', '1.15.0'
35
+ gem.add_development_dependency 'pact-support', '~> 1.16'
36
36
  end
@@ -0,0 +1,4 @@
1
+ PACT_BROKER_FEATURES=deployments bundle exec bin/pact-broker record-deployment \
2
+ --pacticipant Foo --version 1.0.0 --environment prod --broker-base-url http://localhost:9292
3
+
4
+
@@ -19,7 +19,7 @@ output=$(curl -v -X POST https://api.github.com/repos/${repository_slug}/dispatc
19
19
  -H "Authorization: Bearer $GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES" \
20
20
  -d "{\"event_type\": \"release-triggered\", \"client_payload\": {\"increment\": ${increment}}}" 2>&1)
21
21
 
22
- if ! echo "${output}" | grep "HTTP\/1.1 204" > /dev/null; then
22
+ if ! echo "${output}" | grep "HTTP\/.* 204" > /dev/null; then
23
23
  echo "$output" | sed "s/${GITHUB_ACCESS_TOKEN_FOR_PF_RELEASES}/********/g"
24
24
  echo "Failed to trigger release"
25
25
  exit 1
@@ -1,4 +1,6 @@
1
1
  require 'pact_broker/client/cli/broker'
2
+ require 'pact_broker/client/cli/version_selector_options_parser'
3
+ require 'pact_broker/client/can_i_deploy'
2
4
 
3
5
  module PactBroker
4
6
  module Client
@@ -111,13 +113,45 @@ module PactBroker
111
113
  end
112
114
 
113
115
  it "exits with code 1" do
114
- exited_with_error = false
116
+ exited_explicitly = false
117
+ exited_explicitlyerror = nil
115
118
  begin
116
119
  invoke_can_i_deploy
117
- rescue SystemExit
118
- exited_with_error = true
120
+ rescue SystemExit => e
121
+ exited_explicitly = true
122
+ error = e
123
+ end
124
+ expect(exited_explicitly).to be true
125
+ expect(error.status).to be 1
126
+ end
127
+
128
+ context "when an exit status is specified" do
129
+ before do
130
+ allow(ENV).to receive(:fetch).and_call_original
131
+ allow(ENV).to receive(:fetch).with('PACT_BROKER_CAN_I_DEPLOY_EXIT_CODE_BETA', '').and_return("0")
132
+ end
133
+
134
+ it "exits with the specified code" do
135
+ exited_explicitly = false
136
+ error = nil
137
+ begin
138
+ invoke_can_i_deploy
139
+ rescue SystemExit => e
140
+ exited_explicitly = true
141
+ error = e
142
+ end
143
+ expect(exited_explicitly).to be true
144
+ expect(error.status).to be 0
145
+ end
146
+
147
+ it "prints the configured exit code" do
148
+ expect($stderr).to receive(:puts).with("Exiting can-i-deploy with configured exit code 0")
149
+ expect($stdout).to receive(:puts).with(message)
150
+ begin
151
+ invoke_can_i_deploy
152
+ rescue SystemExit
153
+ end
119
154
  end
120
- expect(exited_with_error).to be true
121
155
  end
122
156
  end
123
157
  end
@@ -28,7 +28,7 @@ module PactBroker::Client::CLI
28
28
  "http://pact-broker",
29
29
  ["spec/support/cli_test_pacts/foo.json"],
30
30
  { number: "1.2.3", tags: [], version_required: false },
31
- { verbose: nil }
31
+ { }
32
32
  )
33
33
  invoke_broker
34
34
  end
@@ -52,12 +52,9 @@ module PactBroker::Client::CLI
52
52
  ENV['PACT_BROKER_TOKEN'] = 'token'
53
53
  end
54
54
 
55
- it "populates the options from the environment variables" do
55
+ it "populates the base URL from the environment variables" do
56
56
  expect(Delegate).to receive(:call) do | options |
57
57
  expect(options.broker_base_url).to eq 'http://foo'
58
- expect(options.broker_username).to eq 'username'
59
- expect(options.broker_password).to eq 'password'
60
- expect(options.broker_token).to eq 'token'
61
58
  end
62
59
  TestThor.start(%w{test_using_env_vars})
63
60
  end
@@ -65,9 +62,6 @@ module PactBroker::Client::CLI
65
62
  it "does not override a value specifed on the command line" do
66
63
  expect(Delegate).to receive(:call) do | options |
67
64
  expect(options.broker_base_url).to eq 'http://bar'
68
- expect(options.broker_username).to eq 'username'
69
- expect(options.broker_password).to eq 'password'
70
- expect(options.broker_token).to eq 'token'
71
65
  end
72
66
  TestThor.start(%w{test_using_env_vars --broker-base-url http://bar})
73
67
  end
@@ -9,8 +9,9 @@ module PactBroker::Client
9
9
  end
10
10
 
11
11
  let(:provider_response) do
12
- double('response', body: provider_hash, success?: true)
12
+ double('response', body: provider_hash, success?: true, status: status)
13
13
  end
14
+ let(:status) { 200 }
14
15
 
15
16
  let(:provider_hash) do
16
17
  {
@@ -72,12 +73,12 @@ module PactBroker::Client
72
73
  subject(:entity) { ErrorEntity.new("http://pact", pact_hash, http_client) }
73
74
 
74
75
  it "raises an error" do
75
- expect { entity.assert_success! }.to raise_error ErrorResponseReturned, "Error retrieving http://pact status= "
76
+ expect { entity.assert_success! }.to raise_error ErrorResponseReturned, "Error retrieving http://pact status="
76
77
  end
77
78
  end
78
79
 
79
80
  context "when the response is not successful and there is a response" do
80
- let(:response) { double('response', code: 200, raw_body: "body") }
81
+ let(:response) { double('response', status: 200, raw_body: "body") }
81
82
 
82
83
  subject(:entity) { ErrorEntity.new("http://pact", pact_hash, http_client, response) }
83
84
 
@@ -3,13 +3,13 @@ require 'pact_broker/client/hal/http_client'
3
3
  module PactBroker::Client
4
4
  module Hal
5
5
  describe HttpClient do
6
- before do
7
- allow(Retry).to receive(:until_truthy_or_max_times) { |&block| block.call }
8
- end
9
-
10
6
  subject { HttpClient.new(username: 'foo', password: 'bar') }
11
7
 
12
8
  describe "get" do
9
+ before do
10
+ allow(subject).to receive(:until_truthy_or_max_times) { |&block| block.call }
11
+ end
12
+
13
13
  let!(:request) do
14
14
  stub_request(:get, "http://example.org/").
15
15
  with( headers: {
@@ -41,18 +41,22 @@ module PactBroker::Client
41
41
  end
42
42
  end
43
43
 
44
-
45
44
  it "retries on failure" do
46
- expect(Retry).to receive(:until_truthy_or_max_times)
45
+ expect(subject).to receive(:until_truthy_or_max_times)
47
46
  do_get
48
47
  end
49
48
 
50
49
  it "returns a response" do
51
50
  expect(do_get.body).to eq({"some" => "json"})
52
51
  end
52
+
53
53
  end
54
54
 
55
55
  describe "post" do
56
+ before do
57
+ allow(subject).to receive(:until_truthy_or_max_times) { |&block| block.call }
58
+ end
59
+
56
60
  let!(:request) do
57
61
  stub_request(:post, "http://example.org/").
58
62
  with( headers: {
@@ -75,7 +79,7 @@ module PactBroker::Client
75
79
  end
76
80
 
77
81
  it "calls Retry.until_truthy_or_max_times" do
78
- expect(Retry).to receive(:until_truthy_or_max_times)
82
+ expect(subject).to receive(:until_truthy_or_max_times)
79
83
  do_post
80
84
  end
81
85
 
@@ -100,6 +104,59 @@ module PactBroker::Client
100
104
  end
101
105
  end
102
106
  end
107
+
108
+ describe "integration test" do
109
+ before do
110
+ allow(subject).to receive(:sleep)
111
+ end
112
+
113
+ let(:do_get) { subject.get('http://example.org') }
114
+
115
+ context "with a 50x error is returned less than the max number of tries" do
116
+ let!(:request) do
117
+ stub_request(:get, "http://example.org").
118
+ to_return({ status: 500 }, { status: 502 }, { status: 503 }, { status: 200 })
119
+ end
120
+
121
+ it "retries" do
122
+ expect(do_get.status).to eq 200
123
+ end
124
+ end
125
+
126
+ context "with a 50x error is returned more than the max number of tries" do
127
+ let!(:request) do
128
+ stub_request(:get, "http://example.org").
129
+ to_return({ status: 500 }, { status: 501 }, { status: 502 }, { status: 503 }, { status: 504 })
130
+ end
131
+
132
+ it "retries and returns the last 50x response" do
133
+ expect(do_get.status).to eq 504
134
+ end
135
+ end
136
+
137
+ context "when exceptions are raised" do
138
+ before do
139
+ allow($stderr).to receive(:puts)
140
+ end
141
+
142
+ let!(:request) do
143
+ stub_request(:get, "http://example.org")
144
+ .to_raise(Errno::ECONNREFUSED)
145
+ end
146
+
147
+ it "logs the error" do
148
+ expect($stderr).to receive(:puts).with(/Errno::ECONNREFUSED/)
149
+ begin
150
+ do_get
151
+ rescue Errno::ECONNREFUSED
152
+ end
153
+ end
154
+
155
+ it "retries and raises the last exception" do
156
+ expect { do_get }.to raise_error(Errno::ECONNREFUSED)
157
+ end
158
+ end
159
+ end
103
160
  end
104
161
  end
105
162
  end
@@ -5,7 +5,6 @@ module PactBroker
5
5
  class Versions
6
6
  describe Describe do
7
7
  describe ".call" do
8
-
9
8
  let(:versions_client) do
10
9
  instance_double('PactBroker::Client::Versions', latest: version_hash)
11
10
  end
@@ -0,0 +1,82 @@
1
+ require 'pact_broker/client/versions/record_deployment'
2
+
3
+ module PactBroker
4
+ module Client
5
+ class Versions
6
+ describe RecordDeployment do
7
+ describe ".call" do
8
+ let(:broker_base_url) { "http://broker" }
9
+ let(:index_body_hash) do
10
+ {
11
+ _links: {}
12
+ }
13
+ end
14
+ let!(:index_request) do
15
+ stub_request(:get, broker_base_url).to_return(status: 200, body: index_body_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
16
+ end
17
+
18
+ let(:replaced_previous_deployed_version) { true }
19
+
20
+ let(:params) do
21
+ {
22
+ pacticipant_name: "Foo",
23
+ version_number: "1",
24
+ environment_name: "test",
25
+ replaced_previous_deployed_version: replaced_previous_deployed_version,
26
+ output: "text"
27
+ }
28
+ end
29
+
30
+ let(:pact_broker_client_options) { {} }
31
+
32
+ subject { RecordDeployment.call(params, broker_base_url, pact_broker_client_options) }
33
+
34
+ context "when the pb:environments relation does not exist" do
35
+ it "returns an error response" do
36
+ expect(subject.success).to be false
37
+ expect(subject.message).to include "does not support"
38
+ end
39
+ end
40
+
41
+ context "when the response headers contain Pactflow" do
42
+ before do
43
+ allow_any_instance_of(RecordDeployment).to receive(:check_if_command_supported)
44
+ allow_any_instance_of(RecordDeployment).to receive(:check_environment_exists)
45
+ allow_any_instance_of(RecordDeployment).to receive(:record_deployment)
46
+ allow_any_instance_of(RecordDeployment).to receive(:deployed_version_resource).and_return(deployed_version_resource)
47
+ end
48
+
49
+ let(:response_headers) { { "X-Pactflow-Sha" => "abc" } }
50
+
51
+ let(:deployed_version_resource) do
52
+ double('PactBroker::Client::Hal::Entity', response: double('response', headers: response_headers) )
53
+ end
54
+
55
+ it "indicates the API was Pactflow" do
56
+ expect(subject.message).to include "Recorded deployment of Foo version 1 to test in Pactflow"
57
+ end
58
+ end
59
+
60
+ context "when replaced_previous_deployed_version is false" do
61
+ before do
62
+ allow_any_instance_of(RecordDeployment).to receive(:check_if_command_supported)
63
+ allow_any_instance_of(RecordDeployment).to receive(:check_environment_exists)
64
+ allow_any_instance_of(RecordDeployment).to receive(:record_deployment)
65
+ allow_any_instance_of(RecordDeployment).to receive(:pact_broker_name).and_return("")
66
+ end
67
+
68
+ let(:replaced_previous_deployed_version) { false }
69
+
70
+ let(:deployed_version_resource) do
71
+ double('PactBroker::Client::Hal::Entity', response: double('response', headers: response_headers) )
72
+ end
73
+
74
+ it "does not include the message about marking the previous version as undeployed" do
75
+ expect(subject.message).to_not include "undeployed"
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end