pact_broker-client 1.44.0 → 1.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +5 -9
- data/lib/pact_broker/client/base_command.rb +3 -0
- data/lib/pact_broker/client/can_i_deploy.rb +40 -5
- data/lib/pact_broker/client/cli/broker.rb +3 -1
- data/lib/pact_broker/client/cli/deployment_commands.rb +25 -5
- data/lib/pact_broker/client/deployments.rb +4 -4
- data/lib/pact_broker/client/deployments/record_support_ended.rb +103 -0
- data/lib/pact_broker/client/deployments/record_undeployment.rb +42 -35
- data/lib/pact_broker/client/hal/entity.rb +17 -2
- data/lib/pact_broker/client/pacticipants/create.rb +1 -1
- data/lib/pact_broker/client/version.rb +1 -1
- data/script/record-deployments-and-releases.sh +11 -3
- data/spec/fixtures/approvals/can_i_deploy_failure_dry_run.approved.txt +7 -0
- data/spec/fixtures/approvals/can_i_deploy_success_dry_run.approved.txt +7 -0
- data/spec/lib/pact_broker/client/can_i_deploy_spec.rb +62 -2
- data/spec/lib/pact_broker/client/cli/broker_can_i_deploy_spec.rb +15 -2
- data/spec/lib/pact_broker/client/deployments/record_support_ended_spec.rb +208 -0
- data/spec/lib/pact_broker/client/deployments/record_undeployment_spec.rb +219 -0
- data/spec/service_providers/pact_broker_client_register_repository_spec.rb +2 -2
- data/spec/service_providers/record_release_spec.rb +1 -4
- data/spec/service_providers/record_undeployment_spec.rb +166 -0
- data/spec/support/shared_context.rb +2 -1
- data/tasks/pact.rake +8 -6
- metadata +14 -3
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'pact_broker/client/deployments/record_undeployment'
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Client
|
5
|
+
module Deployments
|
6
|
+
describe RecordUndeployment do
|
7
|
+
let(:params) do
|
8
|
+
{
|
9
|
+
pacticipant_name: "Foo",
|
10
|
+
target: target,
|
11
|
+
environment_name: "test"
|
12
|
+
}
|
13
|
+
end
|
14
|
+
let(:target) { "customer-1" }
|
15
|
+
let(:output) { "text" }
|
16
|
+
let(:options) { { output: output, verbose: true } }
|
17
|
+
let(:pact_broker_base_url) { "http://broker" }
|
18
|
+
let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } }
|
19
|
+
|
20
|
+
let(:index_body_hash) do
|
21
|
+
{
|
22
|
+
_links: {
|
23
|
+
:'pb:environments' => {
|
24
|
+
href: environments_url
|
25
|
+
},
|
26
|
+
:'pb:pacticipant' => {
|
27
|
+
href: pacticipant_url
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:environments_hash) do
|
34
|
+
{
|
35
|
+
_links: {
|
36
|
+
:'pb:environments' => [
|
37
|
+
{
|
38
|
+
name: "test",
|
39
|
+
href: test_environment_url
|
40
|
+
}
|
41
|
+
]
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:environment_hash) do
|
47
|
+
{
|
48
|
+
_links: {
|
49
|
+
:'pb:currently-deployed-versions' => {
|
50
|
+
href: currently_deployed_versions_url
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:deployed_versions_hash) do
|
57
|
+
{
|
58
|
+
_embedded: {
|
59
|
+
deployedVersions: [
|
60
|
+
{
|
61
|
+
target: "customer-1",
|
62
|
+
_links: {
|
63
|
+
self: {
|
64
|
+
href: deployed_version_url_1
|
65
|
+
}
|
66
|
+
}
|
67
|
+
},
|
68
|
+
{
|
69
|
+
target: returned_target_2,
|
70
|
+
_links: {
|
71
|
+
self: {
|
72
|
+
href: deployed_version_url_2
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
]
|
77
|
+
}
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
let(:returned_target_2) { nil }
|
82
|
+
let(:deployed_version_hash) do
|
83
|
+
{
|
84
|
+
_embedded: {
|
85
|
+
version: {
|
86
|
+
number: "2"
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:environments_url) { "#{webmock_base_url}/environments" }
|
93
|
+
let(:test_environment_url) { "#{webmock_base_url}/environments/1234" }
|
94
|
+
let(:currently_deployed_versions_url) { "#{webmock_base_url}/currently-deployed-versions" }
|
95
|
+
let(:deployed_version_url_1) { "#{webmock_base_url}/deployed-version-1" }
|
96
|
+
let(:deployed_version_url_2) { "#{webmock_base_url}/deployed-version-2" }
|
97
|
+
let(:pacticipant_url) { "#{webmock_base_url}/pacticipant" }
|
98
|
+
|
99
|
+
let(:webmock_base_url) { "http://broker" }
|
100
|
+
|
101
|
+
let!(:index_request) do
|
102
|
+
stub_request(:get, "http://broker").to_return(status: 200, body: index_body_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
|
103
|
+
end
|
104
|
+
|
105
|
+
let!(:environments_request) do
|
106
|
+
stub_request(:get, "http://broker/environments").to_return(status: 200, body: environments_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
|
107
|
+
end
|
108
|
+
|
109
|
+
let!(:environment_request) do
|
110
|
+
stub_request(:get, test_environment_url).to_return(status: 200, body: environment_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
|
111
|
+
end
|
112
|
+
|
113
|
+
let!(:deployed_versions_request) do
|
114
|
+
stub_request(:get, currently_deployed_versions_url + "?pacticipant=Foo").to_return(status: 200, body: deployed_versions_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
|
115
|
+
end
|
116
|
+
|
117
|
+
let!(:deployed_version_patch_request_1) do
|
118
|
+
stub_request(:patch, deployed_version_url_1).with(body: { currentlyDeployed: false}.to_json).to_return(status: 200, body: deployed_version_hash.to_json, headers: { "Content-Type" => "application/hal+json" })
|
119
|
+
end
|
120
|
+
|
121
|
+
let!(:deployed_version_patch_request_2) do
|
122
|
+
stub_request(:patch, deployed_version_url_2).with(body: { currentlyDeployed: false}.to_json).to_return(status: 200, body: deployed_version_hash.to_json, headers: { "Content-Type" => "application/hal+json" })
|
123
|
+
end
|
124
|
+
|
125
|
+
let!(:pacticipant_request) do
|
126
|
+
stub_request(:get, pacticipant_url).to_return(status: pacticipant_request_status, body: {}.to_json, headers: { "Content-Type" => "application/hal+json" })
|
127
|
+
end
|
128
|
+
|
129
|
+
let(:pacticipant_request_status) { 200 }
|
130
|
+
|
131
|
+
subject { RecordUndeployment.call(params, options, pact_broker_client_options) }
|
132
|
+
|
133
|
+
its(:success) { is_expected.to eq true }
|
134
|
+
its(:message) { is_expected.to include "Recorded undeployment of Foo version 2 from test environment (target customer-1) in the Pact Broker" }
|
135
|
+
|
136
|
+
context "when there is no pb:environments relation in the index" do
|
137
|
+
let(:index_body_hash) do
|
138
|
+
{
|
139
|
+
_links: {}
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
its(:success) { is_expected.to be false }
|
144
|
+
its(:message) { is_expected.to include "support" }
|
145
|
+
end
|
146
|
+
|
147
|
+
context "when output is json" do
|
148
|
+
let(:output) { "json" }
|
149
|
+
its(:message) { is_expected.to eq [deployed_version_hash].to_json }
|
150
|
+
end
|
151
|
+
|
152
|
+
context "when there is an error returned from one of the deployed version updates (there should only ever be one deployed version, but testing just for the sake of it)" do
|
153
|
+
let!(:deployed_version_patch_request_2) do
|
154
|
+
stub_request(:patch, deployed_version_url_2).to_return(status: 400, body: { errors: { foo: ["some error"]}}.to_json, headers: { "Content-Type" => "application/hal+json" })
|
155
|
+
end
|
156
|
+
|
157
|
+
let(:returned_target_2) { "customer-1" }
|
158
|
+
|
159
|
+
its(:success) { is_expected.to be false }
|
160
|
+
its(:message) { is_expected.to include "Recorded undeployment of Foo version 2" }
|
161
|
+
its(:message) { is_expected.to include "some error" }
|
162
|
+
end
|
163
|
+
|
164
|
+
context "when there is no currently-deployed-versions relation in the environment resource" do
|
165
|
+
let(:environment_hash) do
|
166
|
+
{
|
167
|
+
_links: {}
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
its(:success) { is_expected.to be false }
|
172
|
+
its(:message) { is_expected.to include "support" }
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when a target is provided and there is no deployed version with a matching target" do
|
176
|
+
let(:target) { "wrong" }
|
177
|
+
let(:expected_message) { "Foo is not currently deployed to target 'wrong' in test environment. Please specify one of the following targets to record the undeployment from: customer-1, <no target>" }
|
178
|
+
|
179
|
+
its(:success) { is_expected.to be false }
|
180
|
+
its(:message) { is_expected.to include expected_message }
|
181
|
+
|
182
|
+
context "when output is json" do
|
183
|
+
let(:output) { "json" }
|
184
|
+
|
185
|
+
its(:message) { is_expected.to eq({ error: { message: expected_message } }.to_json) }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context "when a target is not provided and there is no deployed verison without a target" do
|
190
|
+
let(:target) { nil }
|
191
|
+
let(:returned_target_2) { "customer-2" }
|
192
|
+
|
193
|
+
its(:success) { is_expected.to be false }
|
194
|
+
its(:message) { is_expected.to include "Please specify one of the following targets to record the undeployment from: customer-1, customer-2" }
|
195
|
+
end
|
196
|
+
|
197
|
+
context "when there are no deployed versions for the pacticipant" do
|
198
|
+
let(:deployed_versions_hash) do
|
199
|
+
{
|
200
|
+
_embedded: {
|
201
|
+
deployedVersions: []
|
202
|
+
}
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
its(:success) { is_expected.to be false }
|
207
|
+
its(:message) { is_expected.to include "Foo is not currently deployed to test environment. Cannot record undeployment." }
|
208
|
+
|
209
|
+
context "when the pacticipant does not exist" do
|
210
|
+
let(:pacticipant_request_status) { 404 }
|
211
|
+
|
212
|
+
its(:success) { is_expected.to be false }
|
213
|
+
its(:message) { is_expected.to include "No pacticipant with name 'Foo' found" }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -14,7 +14,7 @@ module PactBroker::Client
|
|
14
14
|
with(
|
15
15
|
method: :patch,
|
16
16
|
path: '/pacticipants/Pricing%20Service',
|
17
|
-
headers:
|
17
|
+
headers: old_patch_request_headers,
|
18
18
|
body: {repository_url: repository_url} ).
|
19
19
|
will_respond_with(
|
20
20
|
status: 201,
|
@@ -33,7 +33,7 @@ module PactBroker::Client
|
|
33
33
|
with(
|
34
34
|
method: :patch,
|
35
35
|
path: '/pacticipants/Pricing%20Service',
|
36
|
-
headers:
|
36
|
+
headers: old_patch_request_headers,
|
37
37
|
body: { repository_url: repository_url }).
|
38
38
|
will_respond_with(
|
39
39
|
status: 200,
|
@@ -113,10 +113,7 @@ RSpec.describe "recording a release", pact: true, skip: !deployment_feature_on d
|
|
113
113
|
)
|
114
114
|
.will_respond_with(
|
115
115
|
status: 201,
|
116
|
-
headers: pact_broker_response_headers
|
117
|
-
body: {
|
118
|
-
target: target
|
119
|
-
}
|
116
|
+
headers: pact_broker_response_headers
|
120
117
|
)
|
121
118
|
end
|
122
119
|
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'service_providers/pact_helper'
|
2
|
+
require 'pact_broker/client/deployments/record_undeployment'
|
3
|
+
|
4
|
+
deployment_feature_on = ENV.fetch('PACT_BROKER_FEATURES', '').include?("deployments")
|
5
|
+
|
6
|
+
RSpec.describe "recording an undeployment", pact: true, skip: !deployment_feature_on do
|
7
|
+
include_context "pact broker"
|
8
|
+
include PactBrokerPactHelperMethods
|
9
|
+
|
10
|
+
let(:pacticipant_name) { "Foo" }
|
11
|
+
let(:environment_name) { "test" }
|
12
|
+
let(:output) { "text" }
|
13
|
+
let(:target) { "customer-1" }
|
14
|
+
let(:params) do
|
15
|
+
{
|
16
|
+
pacticipant_name: pacticipant_name,
|
17
|
+
environment_name: environment_name,
|
18
|
+
target: target
|
19
|
+
}
|
20
|
+
end
|
21
|
+
let(:options) do
|
22
|
+
{
|
23
|
+
output: output,
|
24
|
+
verbose: true
|
25
|
+
}
|
26
|
+
end
|
27
|
+
let(:webmock_base_url) { "http://broker" }
|
28
|
+
let(:pact_broker_client_options) { { pact_broker_base_url: webmock_base_url } }
|
29
|
+
let(:test_environment_placeholder_path) { "/HAL-REL-PLACEHOLDER-PB-ENVIRONMENT-16926ef3-590f-4e3f-838e-719717aa88c9" }
|
30
|
+
let(:currently_deployed_versions_placeholder_path) { "/PLACEHOLDER-ENVIRONMENT-CURRENTLY-DEPLOYED-16926ef3-590f-4e3f-838e-719717aa88c9" }
|
31
|
+
let(:deployed_version_placeholder_path) { "/PLACEHOLDER-DEPLOYED-VERSION-ff3adecf-cfc5-4653-a4e3-f1861092f8e0"}
|
32
|
+
|
33
|
+
subject { PactBroker::Client::Deployments::RecordUndeployment.call(params, options, pact_broker_client_options) }
|
34
|
+
|
35
|
+
let(:index_body_hash) do
|
36
|
+
{
|
37
|
+
_links: {
|
38
|
+
:'pb:environments' => {
|
39
|
+
href: "#{webmock_base_url}/environments"
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:environments_hash) do
|
46
|
+
{
|
47
|
+
_links: {
|
48
|
+
:'pb:environments' => [
|
49
|
+
{
|
50
|
+
name: "test",
|
51
|
+
href: pact_broker.mock_service_base_url + test_environment_placeholder_path
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
let!(:index_request) do
|
59
|
+
stub_request(:get, "http://broker").to_return(status: 200, body: index_body_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
|
60
|
+
end
|
61
|
+
|
62
|
+
let!(:environments_request) do
|
63
|
+
stub_request(:get, "http://broker/environments").to_return(status: 200, body: environments_hash.to_json, headers: { "Content-Type" => "application/hal+json" } )
|
64
|
+
end
|
65
|
+
|
66
|
+
def mock_test_environment
|
67
|
+
pact_broker
|
68
|
+
.given("an environment with name test and UUID 16926ef3-590f-4e3f-838e-719717aa88c9 exists")
|
69
|
+
.upon_receiving("a request for an environment")
|
70
|
+
.with(
|
71
|
+
method: "GET",
|
72
|
+
path: test_environment_placeholder_path,
|
73
|
+
headers: get_request_headers
|
74
|
+
)
|
75
|
+
.will_respond_with(
|
76
|
+
status: 200,
|
77
|
+
headers: pact_broker_response_headers,
|
78
|
+
body: {
|
79
|
+
_links: {
|
80
|
+
:'pb:currently-deployed-versions' => {
|
81
|
+
href: Pact.term( pact_broker.mock_service_base_url + currently_deployed_versions_placeholder_path, /^http.*/)
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def mock_deployed_versions_search_results
|
89
|
+
pact_broker
|
90
|
+
.given("an version is deployed to environment with UUID 16926ef3-590f-4e3f-838e-719717aa88c9 with target customer-1")
|
91
|
+
.upon_receiving("a request to list the versions deployed to an environment for a pacticipant name and target")
|
92
|
+
.with(
|
93
|
+
method: "GET",
|
94
|
+
path: currently_deployed_versions_placeholder_path,
|
95
|
+
query: { pacticipant: pacticipant_name },
|
96
|
+
headers: get_request_headers
|
97
|
+
)
|
98
|
+
.will_respond_with(
|
99
|
+
status: 200,
|
100
|
+
headers: pact_broker_response_headers,
|
101
|
+
body: {
|
102
|
+
_embedded: {
|
103
|
+
deployedVersions: [
|
104
|
+
{
|
105
|
+
target: target,
|
106
|
+
_links: {
|
107
|
+
self: {
|
108
|
+
href: Pact.term(pact_broker.mock_service_base_url + deployed_version_placeholder_path, /^http/)
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
]
|
113
|
+
}
|
114
|
+
}
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
def mock_mark_deployed_version_as_undeployed
|
119
|
+
pact_broker
|
120
|
+
.given("a currently deployed version exists")
|
121
|
+
.upon_receiving("a request to mark a deployed version as not currently deploye")
|
122
|
+
.with(
|
123
|
+
method: "PATCH",
|
124
|
+
path: deployed_version_placeholder_path,
|
125
|
+
body: { currentlyDeployed: false },
|
126
|
+
headers: patch_request_headers
|
127
|
+
)
|
128
|
+
.will_respond_with(
|
129
|
+
status: 200,
|
130
|
+
headers: pact_broker_response_headers,
|
131
|
+
body: deployed_version_hash
|
132
|
+
)
|
133
|
+
end
|
134
|
+
|
135
|
+
let(:deployed_version_hash) do
|
136
|
+
{
|
137
|
+
"currentlyDeployed" => false,
|
138
|
+
"_embedded" => {
|
139
|
+
"version" => {
|
140
|
+
"number" => Pact.like("2")
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
end
|
145
|
+
|
146
|
+
context "when the deployment is recorded successfully" do
|
147
|
+
before do
|
148
|
+
mock_test_environment
|
149
|
+
mock_deployed_versions_search_results
|
150
|
+
mock_mark_deployed_version_as_undeployed
|
151
|
+
end
|
152
|
+
|
153
|
+
it "returns a success message" do
|
154
|
+
expect(subject.success).to be true
|
155
|
+
expect(subject.message).to include "Recorded undeployment of Foo version 2 from test environment (target customer-1) in the Pact Broker"
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when the output is json" do
|
159
|
+
let(:output) { "json" }
|
160
|
+
|
161
|
+
it "returns the JSON payload" do
|
162
|
+
expect(JSON.parse(subject.message)).to eq [Pact::Reification.from_term(deployed_version_hash)]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -17,7 +17,8 @@ shared_context "pact broker" do
|
|
17
17
|
let(:pact_broker_version) { Pact::Term.new(:matcher => /\d+\.\d+\.\d+/, :generate => '1.0.0') }
|
18
18
|
let(:pact_broker_response_headers) { {'Content-Type' => 'application/hal+json;charset=utf-8'} }
|
19
19
|
let(:default_request_headers) { { 'Content-Type' => 'application/json'} }
|
20
|
-
let(:
|
20
|
+
let(:old_patch_request_headers) { { 'Content-Type' => 'application/json'} }
|
21
|
+
let(:patch_request_headers) { { 'Content-Type' => 'application/merge-patch+json'} }
|
21
22
|
let(:put_request_headers) { { 'Content-Type' => 'application/json', 'Accept' => 'application/hal+json'} }
|
22
23
|
let(:post_request_headers) { { 'Content-Type' => 'application/json', 'Accept' => 'application/hal+json'} }
|
23
24
|
let(:get_request_headers) { { 'Accept' => 'application/hal+json'} }
|
data/tasks/pact.rake
CHANGED
@@ -18,17 +18,19 @@ PactBroker::Client::PublicationTask.new(:remote) do | task |
|
|
18
18
|
end
|
19
19
|
|
20
20
|
PactBroker::Client::PublicationTask.new(:pactflow) do | task |
|
21
|
-
version = ENV
|
22
|
-
|
21
|
+
version = ENV.fetch('GITHUB_SHA')
|
22
|
+
branch = ENV.fetch('GITHUB_REF').gsub("refs/heads/", "")
|
23
|
+
feature = ENV.fetch('TEST_FEATURE', '')
|
24
|
+
tag = branch
|
23
25
|
|
24
|
-
if
|
25
|
-
version = "#{version}+#{
|
26
|
-
|
26
|
+
if feature != ''
|
27
|
+
version = "#{version}+#{feature}"
|
28
|
+
tag = "#{tag}+#{feature}"
|
27
29
|
end
|
28
30
|
|
29
31
|
require 'pact_broker/client/version'
|
30
32
|
task.auto_detect_version_properties = false
|
31
|
-
task.tags =
|
33
|
+
task.tags = [tag]
|
32
34
|
task.branch = nil
|
33
35
|
task.consumer_version = version
|
34
36
|
task.pact_broker_base_url = "https://pact-oss.pactflow.io"
|