pact_broker 2.32.0 → 2.33.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +3 -0
  3. data/CHANGELOG.md +18 -0
  4. data/config/database.yml +5 -8
  5. data/db/migrations/20190510_set_version_sequence.rb +1 -0
  6. data/db/migrations/20190602_add_headers_column_to_webhooks.rb +6 -0
  7. data/db/migrations/20190603_migrate_webhook_headers.rb +10 -0
  8. data/lib/pact_broker/api/resources/index.rb +12 -0
  9. data/lib/pact_broker/api/resources/pact.rb +1 -1
  10. data/lib/pact_broker/api/resources/verifications.rb +1 -1
  11. data/lib/pact_broker/api/resources/webhook_execution.rb +1 -1
  12. data/lib/pact_broker/certificates/certificate.rb +1 -1
  13. data/lib/pact_broker/config/setting.rb +1 -1
  14. data/lib/pact_broker/db/data_migrations/migrate_webhook_headers.rb +30 -0
  15. data/lib/pact_broker/domain/pacticipant.rb +1 -1
  16. data/lib/pact_broker/domain/verification.rb +1 -1
  17. data/lib/pact_broker/domain/version.rb +1 -1
  18. data/lib/pact_broker/domain/webhook.rb +27 -1
  19. data/lib/pact_broker/domain/webhook_request.rb +9 -146
  20. data/lib/pact_broker/integrations/integration.rb +7 -0
  21. data/lib/pact_broker/matrix/repository.rb +0 -1
  22. data/lib/pact_broker/matrix/row.rb +58 -17
  23. data/lib/pact_broker/pacts/pact_publication.rb +8 -1
  24. data/lib/pact_broker/pacts/pact_version.rb +1 -1
  25. data/lib/pact_broker/pacts/repository.rb +5 -8
  26. data/lib/pact_broker/repositories/helpers.rb +5 -4
  27. data/lib/pact_broker/test/test_data_builder.rb +2 -2
  28. data/lib/pact_broker/version.rb +1 -1
  29. data/lib/pact_broker/versions/latest_version.rb +10 -0
  30. data/lib/pact_broker/webhooks/execution.rb +1 -1
  31. data/lib/pact_broker/webhooks/http_request_with_redacted_headers.rb +21 -0
  32. data/lib/pact_broker/webhooks/http_response_with_utf_8_safe_body.rb +21 -0
  33. data/lib/pact_broker/webhooks/job.rb +2 -2
  34. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +86 -0
  35. data/lib/pact_broker/webhooks/render.rb +10 -71
  36. data/lib/pact_broker/webhooks/repository.rb +2 -9
  37. data/lib/pact_broker/webhooks/service.rb +5 -5
  38. data/lib/pact_broker/webhooks/triggered_webhook.rb +1 -1
  39. data/lib/pact_broker/webhooks/webhook.rb +9 -30
  40. data/lib/pact_broker/webhooks/webhook_event.rb +1 -1
  41. data/lib/pact_broker/{domain → webhooks}/webhook_execution_result.rb +6 -5
  42. data/lib/pact_broker/webhooks/webhook_request_logger.rb +105 -0
  43. data/lib/pact_broker/webhooks/webhook_request_template.rb +7 -6
  44. data/pact_broker.gemspec +1 -0
  45. data/script/docker/db-restore.sh +5 -0
  46. data/script/docker/db-rm.sh +3 -0
  47. data/script/docker/db-start.sh +7 -0
  48. data/script/import-pg-database.sh +5 -0
  49. data/script/seed.rb +1 -1
  50. data/spec/features/execute_webhook_spec.rb +1 -1
  51. data/spec/features/publish_verification_spec.rb +1 -1
  52. data/spec/integration/webhooks/certificate_spec.rb +4 -6
  53. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +1 -1
  54. data/spec/lib/pact_broker/api/resources/verifications_spec.rb +1 -1
  55. data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +1 -1
  56. data/spec/lib/pact_broker/db/data_migrations/migrate_webhook_headers_spec.rb +78 -0
  57. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +8 -163
  58. data/spec/lib/pact_broker/domain/webhook_spec.rb +45 -8
  59. data/spec/lib/pact_broker/matrix/repository_spec.rb +24 -0
  60. data/spec/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names_spec.rb +4 -4
  61. data/spec/lib/pact_broker/pacts/pact_publication_spec.rb +60 -0
  62. data/spec/lib/pact_broker/webhooks/job_spec.rb +3 -3
  63. data/spec/lib/pact_broker/webhooks/render_spec.rb +22 -7
  64. data/spec/lib/pact_broker/webhooks/repository_spec.rb +24 -24
  65. data/spec/lib/pact_broker/webhooks/service_spec.rb +5 -5
  66. data/spec/lib/pact_broker/webhooks/webhook_request_logger_spec.rb +197 -0
  67. data/spec/lib/pact_broker/webhooks/webhook_request_template_spec.rb +13 -4
  68. data/spec/support/database.rb +1 -1
  69. data/spec/support/ssl_pact_broker_server.rb +46 -0
  70. data/tasks/database.rb +12 -0
  71. data/tasks/db.rake +26 -6
  72. data/tasks/docker_database.rb +25 -0
  73. metadata +35 -3
@@ -0,0 +1,78 @@
1
+ require 'pact_broker/db/data_migrations/migrate_webhook_headers'
2
+
3
+ module PactBroker
4
+ module DB
5
+ module DataMigrations
6
+ describe MigrateWebhookHeaders, migration: true do
7
+ describe ".call" do
8
+ before do
9
+ PactBroker::Database.migrate(20190602)
10
+ webhook_header_1
11
+ webhook_header_2
12
+ webhook_header_3
13
+ end
14
+
15
+ let(:now) { DateTime.new(2018, 2, 2) }
16
+
17
+ let(:webhook_1) do
18
+ create(:webhooks, {
19
+ uuid: "1",
20
+ method: "POST",
21
+ url: "http://example.org",
22
+ body: nil,
23
+ is_json_request_body: false,
24
+ enabled: true,
25
+ created_at: now,
26
+ updated_at: now
27
+ })
28
+ end
29
+ let(:webhook_header_1) do
30
+ create(:webhook_headers, {
31
+ name: "Foo",
32
+ value: "bar",
33
+ webhook_id: webhook_1[:id]
34
+ }, nil)
35
+ end
36
+
37
+ let(:webhook_header_2) do
38
+ create(:webhook_headers, {
39
+ name: "Wiffle",
40
+ value: "meep",
41
+ webhook_id: webhook_1[:id]
42
+ }, nil)
43
+ end
44
+
45
+ let(:webhook_2) do
46
+ create(:webhooks, {
47
+ uuid: "2",
48
+ method: "POST",
49
+ url: "http://example.org",
50
+ body: nil,
51
+ is_json_request_body: false,
52
+ enabled: true,
53
+ created_at: now,
54
+ updated_at: now
55
+ })
56
+ end
57
+
58
+ let(:webhook_header_3) do
59
+ create(:webhook_headers, {
60
+ name: "Foo2",
61
+ value: "bar2",
62
+ webhook_id: webhook_2[:id]
63
+ }, nil)
64
+ end
65
+
66
+ subject { MigrateWebhookHeaders.call(database) }
67
+
68
+ it "migrates the webhook headers from individual rows to json on the webhook row" do
69
+ subject
70
+ expect(JSON.parse(database[:webhooks].first[:headers])).to eq "Foo" => "bar", "Wiffle" => "meep"
71
+ expect(JSON.parse(database[:webhooks].order(:id).last[:headers])).to eq "Foo2" => "bar2"
72
+ expect(database[:webhook_headers].count).to eq 0
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -10,12 +10,7 @@ module PactBroker
10
10
  let(:url) { 'http://example.org/hook' }
11
11
  let(:headers) { {'Content-Type' => 'text/plain', 'Authorization' => 'foo'} }
12
12
  let(:body) { 'reqbody' }
13
- let(:logs) { StringIO.new }
14
13
  let(:logger) { double('logger').as_null_object }
15
- let(:execution_logger) { Logger.new(logs) }
16
- let(:options) { {failure_log_message: 'oops', show_response: show_response} }
17
- let(:show_response) { true }
18
- let(:logs) { execute.logs }
19
14
 
20
15
  subject do
21
16
  WebhookRequest.new(
@@ -27,7 +22,7 @@ module PactBroker
27
22
  body: body)
28
23
  end
29
24
 
30
- let(:execute) { subject.execute(options) }
25
+ let(:execute) { subject.execute }
31
26
 
32
27
  describe "description" do
33
28
  it "returns a brief description of the HTTP request" do
@@ -83,7 +78,7 @@ module PactBroker
83
78
  let!(:http_request) do
84
79
  stub_request(:post, "http://example.org/hook").
85
80
  with(:headers => {'Content-Type'=>'text/plain'}, :body => request_body).
86
- to_return(:status => 200, :body => "respbod", :headers => {'Content-Type' => 'text/foo, blah'})
81
+ to_return(:status => status, :body => "respbod", :headers => {'Content-Type' => 'text/foo, blah'})
87
82
  end
88
83
 
89
84
  before do
@@ -92,6 +87,7 @@ module PactBroker
92
87
  allow(WebhookRequest.logger).to receive(:debug)
93
88
  end
94
89
 
90
+ let(:status) { 200 }
95
91
  let(:request_body) { 'reqbody' }
96
92
 
97
93
  it "executes the configured request" do
@@ -99,98 +95,6 @@ module PactBroker
99
95
  expect(http_request).to have_been_made
100
96
  end
101
97
 
102
- it "logs the request" do
103
- expect(logger).to receive(:info).with(/POST.*example/)
104
- expect(logger).to receive(:debug).with(/.*text\/plain/)
105
- expect(logger).to receive(:debug).with(/.*reqbody/)
106
- execute
107
- end
108
-
109
- it "logs the response" do
110
- allow(logger).to receive(:info)
111
- allow(logger).to receive(:debug)
112
- expect(logger).to receive(:info).with(/response.*200/)
113
- expect(logger).to receive(:debug).with(/text\/foo/)
114
- expect(logger).to receive(:debug).with(/respbod/)
115
- execute
116
- end
117
-
118
- describe "execution logs" do
119
-
120
- it "logs the request method and path" do
121
- expect(logs).to include "POST http://example.org/hook"
122
- end
123
-
124
- it "logs the request headers" do
125
- expect(logs).to include "content-type: text/plain"
126
- end
127
-
128
- it "redacts potentially sensitive headers" do
129
- expect(logs).to include "authorization: **********"
130
- end
131
-
132
- it "logs the request body" do
133
- expect(logs).to include body
134
- end
135
-
136
- context "when show_response is true" do
137
- it "logs the response status" do
138
- expect(logs).to include "HTTP/1.0 200"
139
- end
140
-
141
- it "logs the response headers" do
142
- expect(logs).to include "content-type: text/foo, blah"
143
- end
144
-
145
- it "logs the response body" do
146
- expect(logs).to include "respbod"
147
- end
148
- end
149
-
150
- context "when show_response is false" do
151
- let(:show_response) { false }
152
-
153
- it "does not log the response status" do
154
- expect(logs).to_not include "HTTP/1.0 200"
155
- end
156
-
157
- it "does not log the response headers" do
158
- expect(logs).to_not include "content-type: text/foo, blah"
159
- end
160
-
161
- it "does not log the response body" do
162
- expect(logs).to_not include "respbod"
163
- end
164
-
165
- it "logs a message about why the response is hidden" do
166
- expect(logs).to include "security purposes"
167
- end
168
- end
169
-
170
- context "when the response code is a success" do
171
- it "does not log the failure_log_message" do
172
- allow_any_instance_of(WebhookExecutionResult).to receive(:success?).and_return(true)
173
- expect(logs).to_not include "oops"
174
- end
175
- end
176
-
177
- context "when the response code is not successful" do
178
- it "logs the failure_log_message" do
179
- allow_any_instance_of(WebhookExecutionResult).to receive(:success?).and_return(false)
180
- expect(logs).to include "oops"
181
- end
182
- end
183
-
184
- context "with basic auth" do
185
- let(:username) { 'username' }
186
- let(:password) { 'password' }
187
-
188
- it "logs the Authorization header with a starred value" do
189
- expect(logs).to include "authorization: **********"
190
- end
191
- end
192
- end
193
-
194
98
  describe "when a username and password are specified" do
195
99
 
196
100
  let!(:http_request_with_basic_auth) do
@@ -255,12 +159,8 @@ module PactBroker
255
159
  end
256
160
 
257
161
  context "when the request is successful" do
258
- it "returns a WebhookExecutionResult with success=true" do
259
- expect(execute.success?).to be true
260
- end
261
-
262
- it "sets the response on the result" do
263
- expect(execute.response).to be_instance_of(WebhookResponseWithUtf8SafeBody)
162
+ it "returns the response" do
163
+ expect(execute.response).to be_instance_of(Net::HTTPOK)
264
164
  end
265
165
  end
266
166
 
@@ -272,36 +172,12 @@ module PactBroker
272
172
  to_return(:status => 500, :body => "An error")
273
173
  end
274
174
 
275
- it "returns a WebhookExecutionResult with success=false" do
276
- expect(execute.success?).to be false
277
- end
278
-
279
- it "sets the response on the result" do
280
- expect(execute.response).to be_instance_of(WebhookResponseWithUtf8SafeBody)
281
- end
282
- end
283
-
284
- context "when the response body contains a non UTF-8 character" do
285
- let!(:http_request) do
286
- stub_request(:post, "http://example.org/hook").
287
- to_return(:status => 200, :body => "This has some \xC2 invalid chars")
288
- end
289
-
290
- it "removes the non UTF-8 characters before saving the logs so they don't blow up the database" do
291
- result = execute
292
- expect(result.logs).to include "This has some invalid chars"
293
- end
294
-
295
- it "logs that it has cleaned the string to the execution logger" do
296
- logger = double("logger").as_null_object
297
- allow(Logger).to receive(:new).and_return(logger)
298
- expect(logger).to receive(:debug).with(/Note that invalid UTF-8 byte sequences were removed/)
299
- execute
175
+ it "returns the response" do
176
+ expect(execute.response).to be_instance_of(Net::HTTPInternalServerError)
300
177
  end
301
178
  end
302
179
 
303
180
  context "when an error occurs executing the request" do
304
-
305
181
  class WebhookTestError < StandardError; end
306
182
 
307
183
  before do
@@ -309,39 +185,8 @@ module PactBroker
309
185
  allow(logger).to receive(:error)
310
186
  end
311
187
 
312
- it "logs the error" do
313
- expect(logger).to receive(:info).with(/Error.*WebhookTestError.*blah/)
314
- execute
315
- end
316
-
317
- it "returns a WebhookExecutionResult with success=false" do
318
- expect(execute.success?).to be false
319
- end
320
-
321
188
  it "returns a WebhookExecutionResult with an error" do
322
- expect(execute.error).to be_instance_of WebhookTestError
323
- end
324
-
325
- it "logs the failure_log_message" do
326
- expect(logs).to include "oops"
327
- end
328
-
329
- context "when show_response is true" do
330
- it "logs the exception information" do
331
- expect(logs).to include "blah"
332
- end
333
- end
334
-
335
- context "when show_response is false" do
336
- let(:show_response) { false }
337
-
338
- it "does not logs the exception information" do
339
- expect(logs).to_not include "blah"
340
- end
341
-
342
- it "logs a message about why the response is hidden" do
343
- expect(logs).to include "security purposes"
344
- end
189
+ expect { execute }.to raise_error WebhookTestError
345
190
  end
346
191
  end
347
192
  end
@@ -3,14 +3,16 @@ require 'pact_broker/domain/webhook'
3
3
  module PactBroker
4
4
  module Domain
5
5
  describe Webhook do
6
+ let(:uuid) { "uuid" }
6
7
  let(:consumer) { Pacticipant.new(name: 'Consumer')}
7
8
  let(:provider) { Pacticipant.new(name: 'Provider')}
8
- let(:request_template) { instance_double(PactBroker::Webhooks::WebhookRequestTemplate, build: request)}
9
- let(:request) { instance_double(PactBroker::Domain::WebhookRequest, execute: result) }
10
- let(:result) { double('result') }
9
+ let(:request_template) { instance_double(PactBroker::Webhooks::WebhookRequestTemplate, build: webhook_request)}
10
+ let(:webhook_request) { instance_double(PactBroker::Domain::WebhookRequest, execute: http_response, http_request: http_request) }
11
+ let(:http_request) { double('http request') }
12
+ let(:http_response) { double('http response') }
11
13
  let(:webhook_context) { { some: 'things' } }
12
- let(:execution_options) { { other: 'options' } }
13
- let(:options) { { webhook_context: webhook_context, execution_options: execution_options } }
14
+ let(:logging_options) { { other: 'options' } }
15
+ let(:options) { { webhook_context: webhook_context, logging_options: logging_options } }
14
16
  let(:pact) { double('pact') }
15
17
  let(:verification) { double('verification') }
16
18
  let(:logger) { double('logger').as_null_object }
@@ -19,7 +21,7 @@ module PactBroker
19
21
  allow(webhook).to receive(:logger).and_return(logger)
20
22
  end
21
23
 
22
- subject(:webhook) { Webhook.new(request: request_template, consumer: consumer, provider: provider) }
24
+ subject(:webhook) { Webhook.new(uuid: uuid, request: request_template, consumer: consumer, provider: provider) }
23
25
 
24
26
  describe "scope_description" do
25
27
  subject { webhook.scope_description }
@@ -50,9 +52,12 @@ module PactBroker
50
52
 
51
53
  describe "execute" do
52
54
  before do
53
- allow(request_template).to receive(:build).and_return(request)
55
+ allow(request_template).to receive(:build).and_return(webhook_request)
56
+ allow(PactBroker::Webhooks::WebhookRequestLogger).to receive(:new).and_return(webhook_request_logger)
54
57
  end
55
58
 
59
+ let(:webhook_request_logger) { instance_double(PactBroker::Webhooks::WebhookRequestLogger, log: "logs") }
60
+
56
61
  let(:execute) { subject.execute pact, verification, options }
57
62
 
58
63
  it "builds the request" do
@@ -64,15 +69,47 @@ module PactBroker
64
69
  end
65
70
 
66
71
  it "executes the request" do
67
- expect(request).to receive(:execute).with(execution_options)
72
+ expect(webhook_request).to receive(:execute)
73
+ execute
74
+ end
75
+
76
+ it "generates the execution logs" do
77
+ expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, http_response, nil)
68
78
  execute
69
79
  end
70
80
 
81
+ it "returns a WebhookExecutionResult" do
82
+ expect(execute.request).to_not be nil
83
+ expect(execute.response).to_not be nil
84
+ expect(execute.logs).to eq "logs"
85
+ expect(execute.error).to be nil
86
+ end
87
+
71
88
  it "logs before and after" do
72
89
  allow(logger).to receive(:info)
73
90
  expect(logger).to receive(:info).with(/Executing/)
74
91
  execute
75
92
  end
93
+
94
+ context "when an error is thrown" do
95
+ let(:error_class) { Class.new(StandardError) }
96
+
97
+ before do
98
+ allow(webhook_request).to receive(:execute).and_raise(error_class)
99
+ end
100
+
101
+ it "generates the execution logs" do
102
+ expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, nil, instance_of(error_class))
103
+ execute
104
+ end
105
+
106
+ it "returns a WebhookExecutionResult with an error" do
107
+ expect(execute.request).to_not be nil
108
+ expect(execute.response).to be nil
109
+ expect(execute.logs).to eq "logs"
110
+ expect(execute.error).to_not be nil
111
+ end
112
+ end
76
113
  end
77
114
  end
78
115
  end
@@ -1035,6 +1035,30 @@ module PactBroker
1035
1035
  expect(subject.rows.first.consumer_version_number).to eq "2"
1036
1036
  end
1037
1037
  end
1038
+
1039
+ describe "deploying a provider when there is a three way dependency between 3 pacticipants" do
1040
+ before do
1041
+ # A->B, A->C, B->C, deploying C
1042
+ td.create_pact_with_hierarchy("B", "1", "C")
1043
+ .create_verification(provider_version: "10")
1044
+ .create_consumer("A")
1045
+ .create_consumer_version("2")
1046
+ .create_pact
1047
+ .create_verification(provider_version: "10")
1048
+ .use_provider("B")
1049
+ .create_pact
1050
+ end
1051
+
1052
+ let(:selectors) { [ { pacticipant_name: "C", pacticipant_version_number: "10" } ] }
1053
+ let(:options) { { latestby: "cvp", limit: "100", latest: true} }
1054
+ let(:rows) { Repository.new.find(selectors, options) }
1055
+
1056
+ subject { shorten_rows(rows) }
1057
+
1058
+ it "only includes rows that involve the specified pacticipant" do
1059
+ expect(subject.all?{ | row | row.include?("C") } ).to be true
1060
+ end
1061
+ end
1038
1062
  end
1039
1063
  end
1040
1064
  end
@@ -8,7 +8,7 @@ module PactBroker
8
8
  describe FindPotentialDuplicatePacticipantNames do
9
9
 
10
10
  describe "split" do
11
- TEST_CASES = [
11
+ SPLIT_TEST_CASES = [
12
12
  ["a-foo-service", ["a", "foo", "service"]],
13
13
  ["a_foo_service", ["a", "foo", "service"]],
14
14
  ["FooAService", ["foo", "a", "service"]],
@@ -19,7 +19,7 @@ module PactBroker
19
19
  ["S3_Bucket_Service", ["s3", "bucket", "service"]],
20
20
  ]
21
21
 
22
- TEST_CASES.each do | input, output |
22
+ SPLIT_TEST_CASES.each do | input, output |
23
23
  it "splits #{input} into #{output.inspect}" do
24
24
  expect(FindPotentialDuplicatePacticipantNames.split(input)).to eq output
25
25
  end
@@ -31,7 +31,7 @@ module PactBroker
31
31
  subject { FindPotentialDuplicatePacticipantNames.call(new_name, existing_names) }
32
32
 
33
33
 
34
- TEST_CASES = [
34
+ CALL_TEST_CASES = [
35
35
  ["accounts", ["accounts-receivable"], []],
36
36
  ["Accounts", ["Accounts Receivable"], []],
37
37
  ["The Accounts", ["Accounts"], []],
@@ -47,7 +47,7 @@ module PactBroker
47
47
  ['Contract_Service', ['ContractsService', 'Contracts Service', 'contracts-service', 'Contacts', 'Something'], ['ContractsService', 'Contracts Service', 'contracts-service']]
48
48
  ]
49
49
 
50
- TEST_CASES.each do | the_new_name, the_existing_names, the_expected_duplicates |
50
+ CALL_TEST_CASES.each do | the_new_name, the_existing_names, the_expected_duplicates |
51
51
  context "when the new name is #{the_new_name} and the existing names are #{the_existing_names.inspect}" do
52
52
  let(:new_name) { the_new_name }
53
53
  let(:existing_names) { the_existing_names }