pact_broker 2.20.0 → 2.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +23 -0
- data/db/migrations/20180608_add_verification_to_triggered_webhook.rb +7 -0
- data/lib/pact_broker/api/contracts/webhook_contract.rb +13 -4
- data/lib/pact_broker/api/decorators/webhook_request_decorator.rb +5 -1
- data/lib/pact_broker/doc/views/webhooks.markdown +8 -1
- data/lib/pact_broker/domain/pact.rb +1 -1
- data/lib/pact_broker/domain/version.rb +1 -1
- data/lib/pact_broker/domain/webhook.rb +2 -2
- data/lib/pact_broker/domain/webhook_request.rb +24 -16
- data/lib/pact_broker/locale/en.yml +1 -0
- data/lib/pact_broker/pacts/pact_publication.rb +5 -0
- data/lib/pact_broker/pacts/pact_version.rb +6 -1
- data/lib/pact_broker/pacts/service.rb +11 -5
- data/lib/pact_broker/verifications/service.rb +2 -1
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/webhooks/render.rb +48 -5
- data/lib/pact_broker/webhooks/repository.rb +2 -1
- data/lib/pact_broker/webhooks/service.rb +8 -8
- data/lib/pact_broker/webhooks/triggered_webhook.rb +2 -1
- data/spec/integration/webhooks/certificate_spec.rb +1 -1
- data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +24 -1
- data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +7 -0
- data/spec/lib/pact_broker/domain/webhook_request_spec.rb +68 -26
- data/spec/lib/pact_broker/domain/webhook_spec.rb +4 -3
- data/spec/lib/pact_broker/matrix/head_row_spec.rb +1 -2
- data/spec/lib/pact_broker/pacts/service_spec.rb +5 -4
- data/spec/lib/pact_broker/verifications/service_spec.rb +9 -0
- data/spec/lib/pact_broker/webhooks/render_spec.rb +92 -8
- data/spec/lib/pact_broker/webhooks/repository_spec.rb +11 -1
- data/spec/lib/pact_broker/webhooks/service_spec.rb +24 -11
- data/spec/support/test_data_builder.rb +6 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e08f85dd95e1f881127b7592ac697e7b327e0ab
|
4
|
+
data.tar.gz: c13635638b9768858e0fd87c46a593d440586ba8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: daff9fab78dd9a83bd51f0010c6c3b754c4aeb30eb6bdc40cb22772c7d654de4d7af91882c3e5429d687c63c9464ef326e6c4d095e58cf379b8e3a807f04448d
|
7
|
+
data.tar.gz: 1b305e38c05e8d2395260f4909439aa354c7ebd4b555a36fe38531b306a612b1a05bcce611880b8a17d0db1652dffd9847271f4eebe8d7e080daba2a98da6f83
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
<a name="v2.21.0"></a>
|
2
|
+
### v2.21.0 (2018-06-10)
|
3
|
+
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* add ${pactbroker.consumerVersionTags} and ${pactbroker.providerVersionTags} to webhook templates ([e5121b1](/../../commit/e5121b1))
|
8
|
+
* add ${pactbroker.verificationResultUrl} to webhook templates ([e19c9c9](/../../commit/e19c9c9))
|
9
|
+
* redact potentially sensitive headers in the webhook resource ([619c7e9](/../../commit/619c7e9))
|
10
|
+
* add ${pactbroker.githubVerificationStatus} to webhook templates ([abccf7a](/../../commit/abccf7a))
|
11
|
+
* add ${pactbroker.providerVersionNumber} to webhook templates ([86bc1ec](/../../commit/86bc1ec))
|
12
|
+
|
13
|
+
|
14
|
+
#### Bug Fixes
|
15
|
+
|
16
|
+
* correctly trigger contract_content_changed webhooks when first version of a pact is published ([73a06ff](/../../commit/73a06ff))
|
17
|
+
* correctly handle template parameters in URL when rendering webhook resource ([a4b69db](/../../commit/a4b69db))
|
18
|
+
* correct call to execute webhooks when pact changes ([076afe6](/../../commit/076afe6))
|
19
|
+
* ensure webhook hosts cannot contain templated parameters ([fe05919](/../../commit/fe05919))
|
20
|
+
* allow pact broker template parameter in URL ([c91d04e](/../../commit/c91d04e))
|
21
|
+
* correct logic for showing webhook response bodies in logs when a whitelist is configured ([db2f9d1](/../../commit/db2f9d1))
|
22
|
+
|
23
|
+
|
1
24
|
<a name="v2.20.0"></a>
|
2
25
|
### v2.20.0 (2018-06-03)
|
3
26
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'reform'
|
2
2
|
require 'reform/form'
|
3
3
|
require 'pact_broker/webhooks/check_host_whitelist'
|
4
|
+
require 'pact_broker/webhooks/render'
|
4
5
|
|
5
6
|
module PactBroker
|
6
7
|
module Api
|
@@ -70,7 +71,7 @@ module PactBroker
|
|
70
71
|
end
|
71
72
|
|
72
73
|
def valid_url?(url)
|
73
|
-
uri =
|
74
|
+
uri = parse_uri(url)
|
74
75
|
uri.scheme && uri.host
|
75
76
|
rescue URI::InvalidURIError
|
76
77
|
false
|
@@ -83,7 +84,7 @@ module PactBroker
|
|
83
84
|
end
|
84
85
|
|
85
86
|
def allowed_webhook_scheme?(url)
|
86
|
-
scheme =
|
87
|
+
scheme = parse_uri(url).scheme
|
87
88
|
PactBroker.configuration.webhook_scheme_whitelist.any? do | allowed_scheme |
|
88
89
|
scheme.downcase == allowed_scheme.downcase
|
89
90
|
end
|
@@ -91,19 +92,27 @@ module PactBroker
|
|
91
92
|
|
92
93
|
def allowed_webhook_host?(url)
|
93
94
|
if host_whitelist.any?
|
94
|
-
PactBroker::Webhooks::CheckHostWhitelist.call(
|
95
|
+
PactBroker::Webhooks::CheckHostWhitelist.call(parse_uri(url).host, host_whitelist).any?
|
95
96
|
else
|
96
97
|
true
|
97
98
|
end
|
98
99
|
end
|
99
100
|
|
101
|
+
def non_templated_host?(url)
|
102
|
+
parse_uri(url).host == parse_uri(url, 'differentplaceholder').host
|
103
|
+
end
|
104
|
+
|
100
105
|
def host_whitelist
|
101
106
|
PactBroker.configuration.webhook_host_whitelist
|
102
107
|
end
|
108
|
+
|
109
|
+
def parse_uri(uri_string, placeholder = 'placeholder')
|
110
|
+
URI(uri_string.gsub(PactBroker::Webhooks::Render::TEMPLATE_PARAMETER_REGEXP, placeholder))
|
111
|
+
end
|
103
112
|
end
|
104
113
|
|
105
114
|
required(:http_method).filled(:valid_method?, :allowed_webhook_method?)
|
106
|
-
required(:url).filled(:valid_url?, :allowed_webhook_scheme?, :allowed_webhook_host?)
|
115
|
+
required(:url).filled(:valid_url?, :allowed_webhook_scheme?, :allowed_webhook_host?, :non_templated_host?)
|
107
116
|
end
|
108
117
|
end
|
109
118
|
|
@@ -7,11 +7,15 @@ module PactBroker
|
|
7
7
|
|
8
8
|
property :method
|
9
9
|
property :url
|
10
|
-
property :headers, getter: lambda { | _ | headers.empty? ? nil :
|
10
|
+
property :headers, getter: lambda { | _ | headers.empty? ? nil : self.redacted_headers }
|
11
11
|
property :body
|
12
12
|
property :username
|
13
13
|
property :password, getter: lambda { | _ | display_password }
|
14
14
|
|
15
|
+
|
16
|
+
def redacted_headers
|
17
|
+
represented.headers
|
18
|
+
end
|
15
19
|
end
|
16
20
|
end
|
17
21
|
end
|
@@ -86,8 +86,15 @@ Pact Broker Github repository.
|
|
86
86
|
|
87
87
|
The following variables may be used in the request parameters or body, and will be replaced with their appropriate values at runtime.
|
88
88
|
|
89
|
-
`${pactbroker.
|
89
|
+
`${pactbroker.consumerName}`: the consumer name
|
90
|
+
`${pactbroker.providerName}`: the provider name
|
90
91
|
`${pactbroker.consumerVersionNumber}`: the version number of the most recent consumer version associated with the pact content.
|
92
|
+
`${pactbroker.providerVersionNumber}`: the provider version number for the verification result
|
93
|
+
`${pactbroker.consumerVersionTags}`: the list of tag names for the most recent consumer version associated with the pact content, separated by ", ".
|
94
|
+
`${pactbroker.providerVersionTags}`: the list of tag names for the provider version associated with the verification result, separated by ", ".
|
95
|
+
`${pactbroker.githubVerificationStatus}`: the verification status using the correct keywords for posting to the the [Github commit status API](https://developer.github.com/v3/repos/statuses).
|
96
|
+
`${pactbroker.pactUrl}`: the "permalink" URL to the newly published pact (the URL specifying the consumer version URL, rather than the "/latest" format.)
|
97
|
+
`${pactbroker.verificationResultUrl}`: the URL to the relevant verification result.
|
91
98
|
|
92
99
|
Example usage:
|
93
100
|
|
@@ -6,7 +6,7 @@ module PactBroker
|
|
6
6
|
module Domain
|
7
7
|
class Pact
|
8
8
|
|
9
|
-
attr_accessor :id, :provider, :consumer_version, :consumer, :created_at, :json_content, :consumer_version_number, :revision_number, :pact_version_sha
|
9
|
+
attr_accessor :id, :provider, :consumer_version, :consumer, :created_at, :json_content, :consumer_version_number, :revision_number, :pact_version_sha, :latest_verification
|
10
10
|
|
11
11
|
def initialize attributes
|
12
12
|
attributes.each_pair do | key, value |
|
@@ -11,7 +11,7 @@ module PactBroker
|
|
11
11
|
set_primary_key :id
|
12
12
|
one_to_many :pact_publications, order: :revision_number, class: "PactBroker::Pacts::PactPublication", key: :consumer_version_id
|
13
13
|
associate(:many_to_one, :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id)
|
14
|
-
one_to_many :tags, :reciprocal => :version
|
14
|
+
one_to_many :tags, :reciprocal => :version, order: :created_at
|
15
15
|
|
16
16
|
dataset_module do
|
17
17
|
include PactBroker::Repositories::Helpers
|
@@ -32,9 +32,9 @@ module PactBroker
|
|
32
32
|
request && request.description
|
33
33
|
end
|
34
34
|
|
35
|
-
def execute pact, options
|
35
|
+
def execute pact, verification, options
|
36
36
|
logger.info "Executing #{self}"
|
37
|
-
request.execute pact, options
|
37
|
+
request.execute pact, verification, options
|
38
38
|
end
|
39
39
|
|
40
40
|
def to_s
|
@@ -4,7 +4,6 @@ require 'pact_broker/domain/webhook_execution_result'
|
|
4
4
|
require 'pact_broker/logging'
|
5
5
|
require 'pact_broker/messages'
|
6
6
|
require 'net/http'
|
7
|
-
require 'pact_broker/webhooks/redact_logs'
|
8
7
|
require 'pact_broker/webhooks/render'
|
9
8
|
require 'pact_broker/api/pact_broker_urls'
|
10
9
|
require 'pact_broker/build_http_options'
|
@@ -27,6 +26,7 @@ module PactBroker
|
|
27
26
|
|
28
27
|
include PactBroker::Logging
|
29
28
|
include PactBroker::Messages
|
29
|
+
HEADERS_TO_REDACT = [/authorization/i, /token/i]
|
30
30
|
|
31
31
|
attr_accessor :method, :url, :headers, :body, :username, :password, :uuid
|
32
32
|
|
@@ -45,18 +45,25 @@ module PactBroker
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def description
|
48
|
-
"#{method.upcase} #{URI(url).host}"
|
48
|
+
"#{method.upcase} #{URI(url.gsub(PactBroker::Webhooks::Render::TEMPLATE_PARAMETER_REGEXP, 'placeholder')).host}"
|
49
49
|
end
|
50
50
|
|
51
51
|
def display_password
|
52
52
|
password.nil? ? nil : "**********"
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
55
|
+
def redacted_headers
|
56
|
+
headers.each_with_object({}) do | (name, value), new_headers |
|
57
|
+
redact = HEADERS_TO_REDACT.any?{ | pattern | name =~ pattern }
|
58
|
+
new_headers[name] = redact ? "**********" : value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute pact, verification, options = {}
|
56
63
|
logs = StringIO.new
|
57
64
|
execution_logger = Logger.new(logs)
|
58
65
|
begin
|
59
|
-
execute_and_build_result(pact, options, logs, execution_logger)
|
66
|
+
execute_and_build_result(pact, verification, options, logs, execution_logger)
|
60
67
|
rescue StandardError => e
|
61
68
|
handle_error_and_build_result(e, options, logs, execution_logger)
|
62
69
|
end
|
@@ -64,9 +71,9 @@ module PactBroker
|
|
64
71
|
|
65
72
|
private
|
66
73
|
|
67
|
-
def execute_and_build_result pact, options, logs, execution_logger
|
68
|
-
uri = build_uri(pact)
|
69
|
-
req = build_request(uri, pact, execution_logger)
|
74
|
+
def execute_and_build_result pact, verification, options, logs, execution_logger
|
75
|
+
uri = build_uri(pact, verification)
|
76
|
+
req = build_request(uri, pact, verification, execution_logger)
|
70
77
|
response = do_request(uri, req)
|
71
78
|
log_response(response, execution_logger, options)
|
72
79
|
result = WebhookExecutionResult.new(response, logs.string)
|
@@ -80,19 +87,20 @@ module PactBroker
|
|
80
87
|
WebhookExecutionResult.new(nil, logs.string, e)
|
81
88
|
end
|
82
89
|
|
83
|
-
def build_request uri, pact, execution_logger
|
90
|
+
def build_request uri, pact, verification, execution_logger
|
84
91
|
req = http_request(uri)
|
85
|
-
execution_logger.info "HTTP/1.1 #{method.upcase} #{url_with_credentials(pact)}"
|
92
|
+
execution_logger.info "HTTP/1.1 #{method.upcase} #{url_with_credentials(pact, verification)}"
|
86
93
|
|
94
|
+
headers_to_log = redacted_headers
|
87
95
|
headers.each_pair do | name, value |
|
88
|
-
execution_logger.info
|
96
|
+
execution_logger.info "#{name}: #{headers_to_log[name]}"
|
89
97
|
req[name] = value
|
90
98
|
end
|
91
99
|
|
92
100
|
req.basic_auth(username, password) if username
|
93
101
|
|
94
102
|
unless body.nil?
|
95
|
-
req.body = PactBroker::Webhooks::Render.call(String === body ? body : body.to_json, pact)
|
103
|
+
req.body = PactBroker::Webhooks::Render.call(String === body ? body : body.to_json, pact, verification)
|
96
104
|
end
|
97
105
|
|
98
106
|
execution_logger.info(req.body) if req.body
|
@@ -166,19 +174,19 @@ module PactBroker
|
|
166
174
|
end
|
167
175
|
|
168
176
|
def to_s
|
169
|
-
"#{method.upcase} #{url}, username=#{username}, password=#{display_password}, headers=#{
|
177
|
+
"#{method.upcase} #{url}, username=#{username}, password=#{display_password}, headers=#{redacted_headers}, body=#{body}"
|
170
178
|
end
|
171
179
|
|
172
180
|
def http_request(uri)
|
173
181
|
Net::HTTP.const_get(method.capitalize).new(uri)
|
174
182
|
end
|
175
183
|
|
176
|
-
def build_uri
|
177
|
-
URI(PactBroker::Webhooks::Render.call(url, pact){ | value | CGI::escape(value)} )
|
184
|
+
def build_uri(pact, verification)
|
185
|
+
URI(PactBroker::Webhooks::Render.call(url, pact, verification){ | value | CGI::escape(value)} )
|
178
186
|
end
|
179
187
|
|
180
|
-
def url_with_credentials pact
|
181
|
-
u = build_uri(pact)
|
188
|
+
def url_with_credentials pact, verification
|
189
|
+
u = build_uri(pact, verification)
|
182
190
|
u.userinfo = "#{CGI::escape username}:#{display_password}" if username
|
183
191
|
u
|
184
192
|
end
|
@@ -7,6 +7,7 @@ en:
|
|
7
7
|
valid_version_number?: "Version number '%{value}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
|
8
8
|
name_in_path_matches_name_in_pact?: "does not match %{left} name in path ('%{right}')."
|
9
9
|
valid_consumer_version_number?: "Consumer version number '%{value}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
|
10
|
+
non_templated_host?: "cannot have a template parameter in the host"
|
10
11
|
|
11
12
|
pact_broker:
|
12
13
|
messages:
|
@@ -26,6 +26,10 @@ module PactBroker
|
|
26
26
|
LatestTaggedPactPublications.where(id: id).select(:tag_name).collect{|t| t[:tag_name]}
|
27
27
|
end
|
28
28
|
|
29
|
+
def latest_verification
|
30
|
+
pact_version.latest_verification
|
31
|
+
end
|
32
|
+
|
29
33
|
def to_domain
|
30
34
|
PactBroker::Domain::Pact.new(
|
31
35
|
id: id,
|
@@ -36,6 +40,7 @@ module PactBroker
|
|
36
40
|
revision_number: revision_number,
|
37
41
|
json_content: pact_version.content,
|
38
42
|
pact_version_sha: pact_version.sha,
|
43
|
+
latest_verification: latest_verification,
|
39
44
|
created_at: created_at
|
40
45
|
)
|
41
46
|
end
|
@@ -3,7 +3,8 @@ require 'sequel'
|
|
3
3
|
module PactBroker
|
4
4
|
module Pacts
|
5
5
|
class PactVersion < Sequel::Model(:pact_versions)
|
6
|
-
one_to_many :pact_publications, :
|
6
|
+
one_to_many :pact_publications, reciprocal: :pact_version
|
7
|
+
one_to_many :verifications, reciprocal: :verification, order: :id, :class => "PactBroker::Domain::Verification"
|
7
8
|
|
8
9
|
def name
|
9
10
|
"Pact between #{consumer_name} and #{provider_name}"
|
@@ -31,6 +32,10 @@ module PactBroker
|
|
31
32
|
.last
|
32
33
|
end
|
33
34
|
|
35
|
+
def latest_verification
|
36
|
+
verifications.last
|
37
|
+
end
|
38
|
+
|
34
39
|
def consumer_versions
|
35
40
|
PactBroker::Domain::Version.where(id: PactBroker::Pacts::PactPublication.select(:consumer_version_id).where(pact_version_id: id)).order(:order)
|
36
41
|
end
|
@@ -94,9 +94,10 @@ module PactBroker
|
|
94
94
|
distinct
|
95
95
|
end
|
96
96
|
|
97
|
-
|
97
|
+
# TODO also take into account overridden revisions
|
98
|
+
def pact_is_new_or_pact_has_changed_since_previous_version? pact
|
98
99
|
previous_pact = pact_repository.find_previous_pact pact
|
99
|
-
previous_pact
|
100
|
+
previous_pact.nil? || pact.json_content != previous_pact.json_content
|
100
101
|
end
|
101
102
|
|
102
103
|
private
|
@@ -107,7 +108,9 @@ module PactBroker
|
|
107
108
|
updated_pact = pact_repository.update existing_pact.id, params
|
108
109
|
|
109
110
|
if existing_pact.json_content != updated_pact.json_content
|
110
|
-
webhook_service.execute_webhooks updated_pact, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED
|
111
|
+
webhook_service.execute_webhooks updated_pact, nil, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED
|
112
|
+
else
|
113
|
+
logger.debug "Pact has not changed since previous revision, not triggering webhooks"
|
111
114
|
end
|
112
115
|
|
113
116
|
updated_pact
|
@@ -122,8 +125,11 @@ module PactBroker
|
|
122
125
|
end
|
123
126
|
|
124
127
|
def trigger_webhooks pact
|
125
|
-
|
126
|
-
|
128
|
+
# TODO add tests for this
|
129
|
+
if pact_is_new_or_pact_has_changed_since_previous_version?(pact)
|
130
|
+
webhook_service.execute_webhooks pact, nil, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED
|
131
|
+
else
|
132
|
+
logger.debug "Pact has not changed since previous version, not triggering webhooks"
|
127
133
|
end
|
128
134
|
end
|
129
135
|
end
|
@@ -22,8 +22,9 @@ module PactBroker
|
|
22
22
|
provider_version_number = params.fetch('providerApplicationVersion')
|
23
23
|
PactBroker::Api::Decorators::VerificationDecorator.new(verification).from_hash(params)
|
24
24
|
verification.number = next_verification_number
|
25
|
-
webhook_service.execute_webhooks pact, PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED
|
26
25
|
verification = verification_repository.create(verification, provider_version_number, pact)
|
26
|
+
webhook_service.execute_webhooks pact, verification, PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED
|
27
|
+
verification
|
27
28
|
end
|
28
29
|
|
29
30
|
def errors params
|
data/lib/pact_broker/version.rb
CHANGED
@@ -1,11 +1,22 @@
|
|
1
1
|
module PactBroker
|
2
2
|
module Webhooks
|
3
3
|
class Render
|
4
|
-
|
4
|
+
|
5
|
+
TEMPLATE_PARAMETER_REGEXP = /\$\{pactbroker\.[^\}]+\}/
|
6
|
+
|
7
|
+
def self.call(template, pact, trigger_verification = nil, &escaper)
|
5
8
|
base_url = PactBroker.configuration.base_url
|
9
|
+
verification = trigger_verification || (pact && pact.latest_verification)
|
6
10
|
params = {
|
7
|
-
'${pactbroker.pactUrl}' => PactBroker::Api::PactBrokerUrls.pact_url(base_url, pact),
|
8
|
-
'${pactbroker.
|
11
|
+
'${pactbroker.pactUrl}' => pact ? PactBroker::Api::PactBrokerUrls.pact_url(base_url, pact) : "",
|
12
|
+
'${pactbroker.verificationResultUrl}' => verification_url(verification),
|
13
|
+
'${pactbroker.consumerVersionNumber}' => pact ? pact.consumer_version_number : "",
|
14
|
+
'${pactbroker.providerVersionNumber}' => verification ? verification.provider_version_number : "",
|
15
|
+
'${pactbroker.providerVersionTags}' => provider_version_tags(verification),
|
16
|
+
'${pactbroker.consumerVersionTags}' => consumer_version_tags(pact),
|
17
|
+
'${pactbroker.consumerName}' => pact ? pact.consumer_name : "",
|
18
|
+
'${pactbroker.providerName}' => pact ? pact.provider_name : "",
|
19
|
+
'${pactbroker.githubVerificationStatus}' => github_verification_status(verification)
|
9
20
|
}
|
10
21
|
|
11
22
|
if escaper
|
@@ -14,8 +25,40 @@ module PactBroker
|
|
14
25
|
end
|
15
26
|
end
|
16
27
|
|
17
|
-
params.inject(
|
18
|
-
|
28
|
+
params.inject(template) do | template, (key, value) |
|
29
|
+
template.gsub(key, value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.github_verification_status verification
|
34
|
+
if verification
|
35
|
+
verification.success ? "success" : "failure"
|
36
|
+
else
|
37
|
+
"pending"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.verification_url verification
|
42
|
+
if verification
|
43
|
+
PactBroker::Api::PactBrokerUrls.verification_url(verification, PactBroker.configuration.base_url)
|
44
|
+
else
|
45
|
+
""
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.consumer_version_tags pact
|
50
|
+
if pact
|
51
|
+
pact.consumer_version.tags.collect(&:name).join(", ")
|
52
|
+
else
|
53
|
+
""
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.provider_version_tags verification
|
58
|
+
if verification
|
59
|
+
verification.provider_version.tags.collect(&:name).join(", ")
|
60
|
+
else
|
61
|
+
""
|
19
62
|
end
|
20
63
|
end
|
21
64
|
end
|
@@ -78,11 +78,12 @@ module PactBroker
|
|
78
78
|
.collect(&:to_domain)
|
79
79
|
end
|
80
80
|
|
81
|
-
def create_triggered_webhook trigger_uuid, webhook, pact, trigger_type
|
81
|
+
def create_triggered_webhook trigger_uuid, webhook, pact, verification, trigger_type
|
82
82
|
db_webhook = Webhook.where(uuid: webhook.uuid).single_record
|
83
83
|
TriggeredWebhook.create(
|
84
84
|
status: TriggeredWebhook::STATUS_NOT_RUN,
|
85
85
|
pact_publication_id: pact.id,
|
86
|
+
verification: verification,
|
86
87
|
webhook: db_webhook,
|
87
88
|
webhook_uuid: db_webhook.uuid,
|
88
89
|
trigger_uuid: trigger_uuid,
|
@@ -59,9 +59,9 @@ module PactBroker
|
|
59
59
|
webhook_repository.find_all
|
60
60
|
end
|
61
61
|
|
62
|
-
def self.execute_webhook_now webhook, pact
|
63
|
-
triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, USER)
|
64
|
-
options = { failure_log_message: "Webhook execution failed"
|
62
|
+
def self.execute_webhook_now webhook, pact, verification = nil
|
63
|
+
triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, verification, USER)
|
64
|
+
options = { failure_log_message: "Webhook execution failed"}
|
65
65
|
webhook_execution_result = execute_triggered_webhook_now triggered_webhook, options
|
66
66
|
if webhook_execution_result.success?
|
67
67
|
webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_SUCCESS
|
@@ -72,7 +72,7 @@ module PactBroker
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def self.execute_triggered_webhook_now triggered_webhook, options
|
75
|
-
webhook_execution_result = triggered_webhook.execute options
|
75
|
+
webhook_execution_result = triggered_webhook.execute options.merge(show_response: PactBroker.configuration.show_webhook_response?)
|
76
76
|
webhook_repository.create_execution triggered_webhook, webhook_execution_result
|
77
77
|
webhook_execution_result
|
78
78
|
end
|
@@ -85,21 +85,21 @@ module PactBroker
|
|
85
85
|
webhook_repository.find_by_consumer_and_provider consumer, provider
|
86
86
|
end
|
87
87
|
|
88
|
-
def self.execute_webhooks pact, event_name
|
88
|
+
def self.execute_webhooks pact, verification, event_name
|
89
89
|
webhooks = webhook_repository.find_by_consumer_and_provider_and_event_name pact.consumer, pact.provider, event_name
|
90
90
|
|
91
91
|
if webhooks.any?
|
92
|
-
run_later(webhooks, pact, event_name)
|
92
|
+
run_later(webhooks, pact, verification, event_name)
|
93
93
|
else
|
94
94
|
logger.debug "No webhook found for consumer \"#{pact.consumer.name}\" and provider \"#{pact.provider.name}\""
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
-
def self.run_later webhooks, pact, event_name
|
98
|
+
def self.run_later webhooks, pact, verification, event_name
|
99
99
|
trigger_uuid = next_uuid
|
100
100
|
webhooks.each do | webhook |
|
101
101
|
begin
|
102
|
-
triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, RESOURCE_CREATION)
|
102
|
+
triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, verification, RESOURCE_CREATION)
|
103
103
|
logger.info "Scheduling job for #{webhook.description} with uuid #{webhook.uuid}"
|
104
104
|
# Bit of a dodgey hack to make sure the request transaction has finished before we execute the webhook
|
105
105
|
Job.perform_in(5, triggered_webhook: triggered_webhook)
|
@@ -37,6 +37,7 @@ module PactBroker
|
|
37
37
|
associate(:one_to_many, :webhook_executions, :class => "PactBroker::Webhooks::Execution", :key => :triggered_webhook_id, :primary_key => :id, :order => :id)
|
38
38
|
associate(:many_to_one, :webhook, :class => "PactBroker::Webhooks::Webhook", :key => :webhook_id, :primary_key => :id)
|
39
39
|
associate(:many_to_one, :pact_publication, :class => "PactBroker::Pacts::PactPublication", :key => :pact_publication_id, :primary_key => :id)
|
40
|
+
associate(:many_to_one, :verification, :class => "PactBroker::Domain::Verification", :key => :verification_id, :primary_key => :id)
|
40
41
|
associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
|
41
42
|
associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
|
42
43
|
|
@@ -49,7 +50,7 @@ module PactBroker
|
|
49
50
|
# getting a random 'no method to_domain for null' error
|
50
51
|
# not sure on which object, so splitting this out into two lines
|
51
52
|
pact = pact_publication.to_domain
|
52
|
-
webhook.to_domain.execute(pact, options)
|
53
|
+
webhook.to_domain.execute(pact, verification, options)
|
53
54
|
end
|
54
55
|
|
55
56
|
def consumer_name
|
@@ -16,7 +16,7 @@ describe "executing a webhook to a server with a self signed certificate" do
|
|
16
16
|
|
17
17
|
let(:pact) { td.create_pact_with_hierarchy.and_return(:pact) }
|
18
18
|
|
19
|
-
subject { webhook_request.execute(pact) }
|
19
|
+
subject { webhook_request.execute(pact, nil) }
|
20
20
|
|
21
21
|
context "without the correct cacert" do
|
22
22
|
it "fails" do
|
@@ -179,8 +179,31 @@ module PactBroker
|
|
179
179
|
expect(subject.errors[:"request.url"]).to eq ["is not a valid URL eg. http://example.org"]
|
180
180
|
end
|
181
181
|
end
|
182
|
-
end
|
183
182
|
|
183
|
+
context "with a URL that has templated parameters in it" do
|
184
|
+
let(:json) do
|
185
|
+
valid_webhook_with do |hash|
|
186
|
+
hash['request']['url'] = 'https://foo/commits/${pactbroker.consumerVersionNumber}'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
it "is empty" do
|
191
|
+
expect(subject.errors).to be_empty
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "with a URL that has templated parameters in the host" do
|
196
|
+
let(:json) do
|
197
|
+
valid_webhook_with do |hash|
|
198
|
+
hash['request']['url'] = 'https://${pactbroker.consumerVersionNumber}/commits'
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it "contains an error" do
|
203
|
+
expect(subject.errors[:"request.url"]).to eq ["cannot have a template parameter in the host"]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
184
207
|
end
|
185
208
|
end
|
186
209
|
end
|
@@ -88,6 +88,13 @@ module PactBroker
|
|
88
88
|
expect(parsed_json[:request]).to_not have_key :headers
|
89
89
|
end
|
90
90
|
end
|
91
|
+
|
92
|
+
context 'when the headers contain sensitve information' do
|
93
|
+
let(:headers) { { 'Authorization' => 'foo' } }
|
94
|
+
it 'redacts them' do
|
95
|
+
expect(parsed_json[:request][:headers][:'Authorization']).to eq "**********"
|
96
|
+
end
|
97
|
+
end
|
91
98
|
end
|
92
99
|
|
93
100
|
describe 'from_json' do
|
@@ -16,29 +16,41 @@ module PactBroker
|
|
16
16
|
let(:username) { nil }
|
17
17
|
let(:password) { nil }
|
18
18
|
let(:url) { 'http://example.org/hook' }
|
19
|
+
let(:headers) { {'Content-Type' => 'text/plain', 'Authorization' => 'foo'} }
|
19
20
|
let(:body) { 'body' }
|
20
21
|
let(:logs) { StringIO.new }
|
21
22
|
let(:execution_logger) { Logger.new(logs) }
|
22
23
|
let(:options) { {failure_log_message: 'oops', show_response: show_response} }
|
23
24
|
let(:show_response) { true }
|
24
25
|
let(:pact) { instance_double('PactBroker::Domain::Pact') }
|
26
|
+
let(:verification) { instance_double('PactBroker::Domain::Verification') }
|
27
|
+
let(:logs) { execute.logs }
|
25
28
|
|
26
29
|
subject do
|
27
30
|
WebhookRequest.new(
|
28
31
|
method: 'post',
|
29
32
|
url: url,
|
30
|
-
headers:
|
33
|
+
headers: headers,
|
31
34
|
username: username,
|
32
35
|
password: password,
|
33
36
|
body: body)
|
34
37
|
end
|
35
38
|
|
36
|
-
let(:
|
39
|
+
let(:execute) { subject.execute(pact, verification, options) }
|
40
|
+
|
37
41
|
|
38
42
|
describe "description" do
|
39
43
|
it "returns a brief description of the HTTP request" do
|
40
44
|
expect(subject.description).to eq 'POST example.org'
|
41
45
|
end
|
46
|
+
|
47
|
+
context "when the URL has a template parameter in it" do
|
48
|
+
let(:url) { "http://foo/commits/${pactbroker.consumerVersionNumber}" }
|
49
|
+
|
50
|
+
it "doesn't explode" do
|
51
|
+
expect(subject.description).to eq 'POST foo'
|
52
|
+
end
|
53
|
+
end
|
42
54
|
end
|
43
55
|
|
44
56
|
describe "display_password" do
|
@@ -55,6 +67,36 @@ module PactBroker
|
|
55
67
|
end
|
56
68
|
end
|
57
69
|
|
70
|
+
describe "redacted_headers" do
|
71
|
+
let(:headers) do
|
72
|
+
{
|
73
|
+
'Authorization' => 'foo',
|
74
|
+
'X-authorization' => 'bar',
|
75
|
+
'token' => 'bar',
|
76
|
+
'Token' => 'bar',
|
77
|
+
'X-Auth-Token' => 'bar',
|
78
|
+
'X-Authorization-Token' => 'bar',
|
79
|
+
'OK' => 'ok'
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:expected_headers) do
|
84
|
+
{
|
85
|
+
'Authorization' => '**********',
|
86
|
+
'X-authorization' => '**********',
|
87
|
+
'token' => '**********',
|
88
|
+
'Token' => '**********',
|
89
|
+
'X-Auth-Token' => '**********',
|
90
|
+
'X-Authorization-Token' => '**********',
|
91
|
+
'OK' => 'ok'
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
it "redacts sensitive headers" do
|
96
|
+
expect(subject.redacted_headers).to eq expected_headers
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
58
100
|
describe "execute" do
|
59
101
|
let!(:http_request) do
|
60
102
|
stub_request(:post, "http://example.org/hook").
|
@@ -65,20 +107,20 @@ module PactBroker
|
|
65
107
|
let(:request_body) { 'body' }
|
66
108
|
|
67
109
|
it "renders the url template" do
|
68
|
-
expect(PactBroker::Webhooks::Render).to receive(:call).with("http://example.org/hook", pact) do | content, pact, verification, &block |
|
110
|
+
expect(PactBroker::Webhooks::Render).to receive(:call).with("http://example.org/hook", pact, verification) do | content, pact, verification, &block |
|
69
111
|
expect(content).to eq "http://example.org/hook"
|
70
112
|
expect(pact).to be pact
|
71
|
-
expect(verification).to be
|
113
|
+
expect(verification).to be verification
|
72
114
|
expect(block.call("foo bar")).to eq "foo+bar"
|
73
115
|
"http://example.org/hook"
|
74
116
|
end
|
75
|
-
|
117
|
+
execute
|
76
118
|
end
|
77
119
|
|
78
120
|
context "when the body is a string" do
|
79
121
|
it "renders the body template with the String" do
|
80
|
-
expect(PactBroker::Webhooks::Render).to receive(:call).with('body', pact)
|
81
|
-
|
122
|
+
expect(PactBroker::Webhooks::Render).to receive(:call).with('body', pact, verification)
|
123
|
+
execute
|
82
124
|
end
|
83
125
|
end
|
84
126
|
|
@@ -87,20 +129,20 @@ module PactBroker
|
|
87
129
|
let(:request_body) { '{"foo":"bar"}' }
|
88
130
|
|
89
131
|
it "renders the body template with JSON" do
|
90
|
-
expect(PactBroker::Webhooks::Render).to receive(:call).with(request_body, pact)
|
91
|
-
|
132
|
+
expect(PactBroker::Webhooks::Render).to receive(:call).with(request_body, pact, verification)
|
133
|
+
execute
|
92
134
|
end
|
93
135
|
end
|
94
136
|
|
95
137
|
it "executes the configured request" do
|
96
|
-
|
138
|
+
execute
|
97
139
|
expect(http_request).to have_been_made
|
98
140
|
end
|
99
141
|
|
100
142
|
it "logs the request" do
|
101
143
|
allow(PactBroker.logger).to receive(:info)
|
102
144
|
expect(PactBroker.logger).to receive(:info).with(/POST.*example.*text.*body/)
|
103
|
-
|
145
|
+
execute
|
104
146
|
end
|
105
147
|
|
106
148
|
it "logs the response" do
|
@@ -109,7 +151,7 @@ module PactBroker
|
|
109
151
|
expect(PactBroker.logger).to receive(:info).with(/response.*200/)
|
110
152
|
expect(PactBroker.logger).to receive(:debug).with(/content-type/)
|
111
153
|
expect(PactBroker.logger).to receive(:debug).with(/respbod/)
|
112
|
-
|
154
|
+
execute
|
113
155
|
end
|
114
156
|
|
115
157
|
describe "execution logs" do
|
@@ -123,7 +165,7 @@ module PactBroker
|
|
123
165
|
end
|
124
166
|
|
125
167
|
it "redacts potentially sensitive headers" do
|
126
|
-
expect(logs).to include "Authorization:
|
168
|
+
expect(logs).to include "Authorization: **********"
|
127
169
|
end
|
128
170
|
|
129
171
|
it "logs the request body" do
|
@@ -204,7 +246,7 @@ module PactBroker
|
|
204
246
|
let(:password) { "password" }
|
205
247
|
|
206
248
|
it "uses the credentials" do
|
207
|
-
|
249
|
+
execute
|
208
250
|
expect(http_request_with_basic_auth).to have_been_made
|
209
251
|
end
|
210
252
|
end
|
@@ -214,7 +256,7 @@ module PactBroker
|
|
214
256
|
let(:password) { "p@$$w0rd!" }
|
215
257
|
|
216
258
|
it "uses the credentials" do
|
217
|
-
|
259
|
+
execute
|
218
260
|
expect(http_request_with_basic_auth).to have_been_made
|
219
261
|
end
|
220
262
|
end
|
@@ -231,7 +273,7 @@ module PactBroker
|
|
231
273
|
end
|
232
274
|
|
233
275
|
it "uses SSL" do
|
234
|
-
|
276
|
+
execute
|
235
277
|
expect(https_request).to have_been_made
|
236
278
|
end
|
237
279
|
end
|
@@ -246,18 +288,18 @@ module PactBroker
|
|
246
288
|
end
|
247
289
|
|
248
290
|
it "executes the request without a body" do
|
249
|
-
|
291
|
+
execute
|
250
292
|
expect(http_request).to have_been_made
|
251
293
|
end
|
252
294
|
end
|
253
295
|
|
254
296
|
context "when the request is successful" do
|
255
297
|
it "returns a WebhookExecutionResult with success=true" do
|
256
|
-
expect(
|
298
|
+
expect(execute.success?).to be true
|
257
299
|
end
|
258
300
|
|
259
301
|
it "sets the response on the result" do
|
260
|
-
expect(
|
302
|
+
expect(execute.response).to be_instance_of(Net::HTTPOK)
|
261
303
|
end
|
262
304
|
end
|
263
305
|
|
@@ -270,11 +312,11 @@ module PactBroker
|
|
270
312
|
end
|
271
313
|
|
272
314
|
it "returns a WebhookExecutionResult with success=false" do
|
273
|
-
expect(
|
315
|
+
expect(execute.success?).to be false
|
274
316
|
end
|
275
317
|
|
276
318
|
it "sets the response on the result" do
|
277
|
-
expect(
|
319
|
+
expect(execute.response).to be_instance_of(Net::HTTPInternalServerError)
|
278
320
|
end
|
279
321
|
end
|
280
322
|
|
@@ -285,7 +327,7 @@ module PactBroker
|
|
285
327
|
end
|
286
328
|
|
287
329
|
it "removes the non UTF-8 characters before saving the logs so they don't blow up the database" do
|
288
|
-
result =
|
330
|
+
result = execute
|
289
331
|
expect(result.logs).to include "This has some invalid chars"
|
290
332
|
end
|
291
333
|
|
@@ -293,7 +335,7 @@ module PactBroker
|
|
293
335
|
logger = double("logger").as_null_object
|
294
336
|
allow(Logger).to receive(:new).and_return(logger)
|
295
337
|
expect(logger).to receive(:debug).with(/Note that invalid UTF-8 byte sequences were removed/)
|
296
|
-
|
338
|
+
execute
|
297
339
|
end
|
298
340
|
end
|
299
341
|
|
@@ -308,15 +350,15 @@ module PactBroker
|
|
308
350
|
|
309
351
|
it "logs the error" do
|
310
352
|
expect(PactBroker.logger).to receive(:error).with(/Error.*WebhookTestError.*blah/)
|
311
|
-
|
353
|
+
execute
|
312
354
|
end
|
313
355
|
|
314
356
|
it "returns a WebhookExecutionResult with success=false" do
|
315
|
-
expect(
|
357
|
+
expect(execute.success?).to be false
|
316
358
|
end
|
317
359
|
|
318
360
|
it "returns a WebhookExecutionResult with an error" do
|
319
|
-
expect(
|
361
|
+
expect(execute.error).to be_instance_of WebhookTestError
|
320
362
|
end
|
321
363
|
|
322
364
|
it "logs the failure_log_message" do
|
@@ -12,6 +12,7 @@ module PactBroker
|
|
12
12
|
let(:request) { instance_double(PactBroker::Domain::WebhookRequest, execute: nil)}
|
13
13
|
let(:options) { double('options') }
|
14
14
|
let(:pact) { double('pact') }
|
15
|
+
let(:verification) { double('verification') }
|
15
16
|
|
16
17
|
subject { Webhook.new(request: request, consumer: consumer, provider: provider,) }
|
17
18
|
|
@@ -24,14 +25,14 @@ module PactBroker
|
|
24
25
|
describe "execute" do
|
25
26
|
|
26
27
|
it "executes the request" do
|
27
|
-
expect(request).to receive(:execute).with(pact, options)
|
28
|
-
subject.execute pact, options
|
28
|
+
expect(request).to receive(:execute).with(pact, verification, options)
|
29
|
+
subject.execute pact, verification, options
|
29
30
|
end
|
30
31
|
|
31
32
|
it "logs before and after" do
|
32
33
|
allow(PactBroker.logger).to receive(:info)
|
33
34
|
expect(PactBroker.logger).to receive(:info).with(/Executing/)
|
34
|
-
subject.execute pact, options
|
35
|
+
subject.execute pact, verification, options
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
@@ -50,10 +50,9 @@ module PactBroker
|
|
50
50
|
.create_pact
|
51
51
|
end
|
52
52
|
|
53
|
-
subject { HeadRow.eager(:consumer_version_tags).eager(:latest_verification_for_consumer_version_tag).order(:pact_publication_id).all }
|
53
|
+
subject { HeadRow.where(verification_id: nil, consumer_version_tag_name: "prod").eager(:consumer_version_tags).eager(:latest_verification_for_consumer_version_tag).order(:pact_publication_id).all }
|
54
54
|
|
55
55
|
it "returns the most recent verification for the previous version with the same tag" do
|
56
|
-
expect(subject.last.verification_id).to be nil # this pact version has not been verified directly
|
57
56
|
expect(subject.last.latest_verification_for_consumer_version_tag.provider_version.number).to eq "11"
|
58
57
|
end
|
59
58
|
end
|
@@ -30,7 +30,7 @@ module PactBroker
|
|
30
30
|
|
31
31
|
end
|
32
32
|
|
33
|
-
describe "#
|
33
|
+
describe "#pact_is_new_or_pact_has_changed_since_previous_version?" do
|
34
34
|
let(:json_content) { { 'some' => 'json'}.to_json }
|
35
35
|
let(:pact) { instance_double(PactBroker::Domain::Pact, json_content: json_content)}
|
36
36
|
|
@@ -38,7 +38,7 @@ module PactBroker
|
|
38
38
|
allow_any_instance_of(Pacts::Repository).to receive(:find_previous_pact).and_return(previous_pact)
|
39
39
|
end
|
40
40
|
|
41
|
-
subject { Service.
|
41
|
+
subject { Service.pact_is_new_or_pact_has_changed_since_previous_version? pact }
|
42
42
|
|
43
43
|
context "when a previous pact is found" do
|
44
44
|
let(:previous_pact) { instance_double(PactBroker::Domain::Pact, json_content: previous_json_content)}
|
@@ -60,8 +60,9 @@ module PactBroker
|
|
60
60
|
|
61
61
|
context "when a previous pact is not found" do
|
62
62
|
let(:previous_pact) { nil }
|
63
|
-
|
64
|
-
|
63
|
+
|
64
|
+
it "returns true" do
|
65
|
+
expect(subject).to be_truthy
|
65
66
|
end
|
66
67
|
end
|
67
68
|
end
|
@@ -9,6 +9,10 @@ module PactBroker
|
|
9
9
|
subject { PactBroker::Verifications::Service }
|
10
10
|
|
11
11
|
describe "#create" do
|
12
|
+
before do
|
13
|
+
allow(PactBroker::Webhooks::Service).to receive(:execute_webhooks)
|
14
|
+
end
|
15
|
+
|
12
16
|
let(:params) { {'success' => true, 'providerApplicationVersion' => '4.5.6'} }
|
13
17
|
let(:pact) { TestDataBuilder.new.create_pact_with_hierarchy.and_return(:pact) }
|
14
18
|
let(:create_verification) { subject.create 3, params, pact }
|
@@ -36,6 +40,11 @@ module PactBroker
|
|
36
40
|
expect(verification.provider_version).to_not be nil
|
37
41
|
expect(verification.provider_version_number).to eq '4.5.6'
|
38
42
|
end
|
43
|
+
|
44
|
+
it "invokes the webhooks for the verification" do
|
45
|
+
verification = create_verification
|
46
|
+
expect(PactBroker::Webhooks::Service).to have_received(:execute_webhooks).with(pact, verification, PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED)
|
47
|
+
end
|
39
48
|
end
|
40
49
|
|
41
50
|
describe "#errors" do
|
@@ -7,28 +7,112 @@ module PactBroker
|
|
7
7
|
describe "#call" do
|
8
8
|
before do
|
9
9
|
allow(PactBroker::Api::PactBrokerUrls).to receive(:pact_url).and_return("http://foo")
|
10
|
+
allow(PactBroker::Api::PactBrokerUrls).to receive(:verification_url) do | verification, base_url |
|
11
|
+
expect(verification).to_not be nil
|
12
|
+
"http://verification"
|
13
|
+
end
|
10
14
|
end
|
11
15
|
|
12
|
-
let(:
|
13
|
-
"
|
16
|
+
let(:pact) do
|
17
|
+
double("pact",
|
18
|
+
consumer_version: consumer_version,
|
19
|
+
consumer_version_number: "1.2.3+foo",
|
20
|
+
consumer_name: "Foo",
|
21
|
+
provider_name: "Bar",
|
22
|
+
latest_verification: nil)
|
14
23
|
end
|
15
24
|
|
16
|
-
let(:
|
17
|
-
|
25
|
+
let(:pact_with_no_verification) { pact }
|
26
|
+
|
27
|
+
let(:pact_with_successful_verification) do
|
28
|
+
double("pact",
|
29
|
+
consumer_version: consumer_version,
|
30
|
+
consumer_version_number: "1.2.3+foo",
|
31
|
+
consumer_name: "Foo",
|
32
|
+
provider_name: "Bar",
|
33
|
+
latest_verification: verification)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:pact_with_failed_verification) do
|
37
|
+
double("pact",
|
38
|
+
consumer_version: consumer_version,
|
39
|
+
consumer_version_number: "1.2.3+foo",
|
40
|
+
consumer_name: "Foo",
|
41
|
+
provider_name: "Bar",
|
42
|
+
latest_verification: failed_verification)
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:verification) do
|
46
|
+
double("verification", provider_version_number: "3", success: true, provider_version: provider_version)
|
47
|
+
end
|
48
|
+
|
49
|
+
let(:failed_verification) do
|
50
|
+
double("verification", provider_version_number: "3", success: false, provider_version: provider_version)
|
51
|
+
end
|
52
|
+
|
53
|
+
let(:provider_version) do
|
54
|
+
double("version", tags: provider_tags)
|
18
55
|
end
|
19
56
|
|
20
|
-
|
57
|
+
let(:consumer_version) do
|
58
|
+
double("version", tags: consumer_tags)
|
59
|
+
end
|
60
|
+
|
61
|
+
let(:provider_tags) do
|
62
|
+
[ double("tag", name: "test"), double("tag", name: "prod") ]
|
63
|
+
end
|
64
|
+
|
65
|
+
let(:consumer_tags) do
|
66
|
+
[ double("tag", name: "test") ]
|
67
|
+
end
|
68
|
+
|
69
|
+
let(:nil_pact) { nil }
|
70
|
+
let(:nil_verification) { nil }
|
21
71
|
|
22
|
-
|
72
|
+
subject { Render.call(template, pact, verification) }
|
73
|
+
|
74
|
+
TEST_CASES = [
|
75
|
+
["${pactbroker.pactUrl}", "http://foo", :pact, :verification],
|
76
|
+
["${pactbroker.consumerVersionNumber}", "1.2.3+foo", :pact, :verification],
|
77
|
+
["${pactbroker.providerVersionNumber}", "3", :pact, :verification],
|
78
|
+
["${pactbroker.providerVersionNumber}", "", :pact, :nil_verification],
|
79
|
+
["${pactbroker.consumerName}", "Foo", :pact, :verification],
|
80
|
+
["${pactbroker.providerName}", "Bar", :pact, :verification],
|
81
|
+
["${pactbroker.githubVerificationStatus}", "success", :pact, :verification],
|
82
|
+
["${pactbroker.githubVerificationStatus}", "failure", :pact, :failed_verification],
|
83
|
+
["${pactbroker.githubVerificationStatus}", "pending", :nil_pact, :nil_verification],
|
84
|
+
["${pactbroker.githubVerificationStatus}", "pending", :pact_with_no_verification, :nil_verification],
|
85
|
+
["${pactbroker.githubVerificationStatus}", "success", :pact_with_successful_verification, :nil_verification],
|
86
|
+
["${pactbroker.githubVerificationStatus}", "failure", :pact_with_failed_verification, :nil_verification],
|
87
|
+
["${pactbroker.verificationResultUrl}", "", :pact_with_no_verification, :nil_verification],
|
88
|
+
["${pactbroker.verificationResultUrl}", "http://verification", :pact_with_successful_verification, :nil_verification],
|
89
|
+
["${pactbroker.verificationResultUrl}", "http://verification", :pact_with_successful_verification, :verification],
|
90
|
+
["${pactbroker.providerVersionTags}", "test, prod", :pact_with_successful_verification, :verification],
|
91
|
+
["${pactbroker.consumerVersionTags}", "test", :pact_with_successful_verification, :verification],
|
92
|
+
]
|
93
|
+
|
94
|
+
TEST_CASES.each do | (template, expected_output, pact_var_name, verification_var_name) |
|
95
|
+
context "with #{pact_var_name} and #{verification_var_name}" do
|
96
|
+
it "replaces #{template} with #{expected_output.inspect}" do
|
97
|
+
the_pact = send(pact_var_name)
|
98
|
+
the_verification = send(verification_var_name)
|
99
|
+
output = Render.call(template, the_pact, the_verification)
|
100
|
+
expect(output).to eq expected_output
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
23
104
|
|
24
105
|
context "with an escaper" do
|
25
106
|
subject do
|
26
|
-
Render.call(
|
107
|
+
Render.call(template, pact, verification) do | value |
|
27
108
|
CGI.escape(value)
|
28
109
|
end
|
29
110
|
end
|
111
|
+
let(:template) do
|
112
|
+
"${pactbroker.pactUrl}"
|
113
|
+
end
|
30
114
|
|
31
|
-
it { is_expected.to eq "
|
115
|
+
it { is_expected.to eq "http%3A%2F%2Ffoo" }
|
32
116
|
end
|
33
117
|
end
|
34
118
|
end
|
@@ -333,14 +333,16 @@ module PactBroker
|
|
333
333
|
.create_webhook
|
334
334
|
.create_consumer_version
|
335
335
|
.create_pact
|
336
|
+
.create_verification
|
336
337
|
end
|
337
338
|
|
338
|
-
subject { Repository.new.create_triggered_webhook '1234', td.webhook, td.pact, 'publication' }
|
339
|
+
subject { Repository.new.create_triggered_webhook '1234', td.webhook, td.pact, td.verification, 'publication' }
|
339
340
|
|
340
341
|
it "creates a TriggeredWebhook" do
|
341
342
|
expect(subject.webhook_uuid ).to eq td.webhook.uuid
|
342
343
|
expect(subject.consumer).to eq td.consumer
|
343
344
|
expect(subject.provider).to eq td.provider
|
345
|
+
expect(subject.verification).to eq td.verification
|
344
346
|
expect(subject.trigger_uuid).to eq '1234'
|
345
347
|
expect(subject.trigger_type).to eq 'publication'
|
346
348
|
end
|
@@ -364,6 +366,14 @@ module PactBroker
|
|
364
366
|
it "sets the PactPublication" do
|
365
367
|
expect(subject.pact_publication.id).to eq td.pact.id
|
366
368
|
end
|
369
|
+
|
370
|
+
context "without a verification" do
|
371
|
+
subject { Repository.new.create_triggered_webhook '1234', td.webhook, td.pact, nil, 'publication' }
|
372
|
+
|
373
|
+
it "does not set the verification" do
|
374
|
+
expect(subject.verification).to be nil
|
375
|
+
end
|
376
|
+
end
|
367
377
|
end
|
368
378
|
|
369
379
|
describe "create_execution" do
|
@@ -30,6 +30,7 @@ module PactBroker
|
|
30
30
|
|
31
31
|
describe ".execute_webhooks" do
|
32
32
|
|
33
|
+
let(:verification) { instance_double(PactBroker::Domain::Verification)}
|
33
34
|
let(:pact) { instance_double(PactBroker::Domain::Pact, consumer: consumer, provider: provider, consumer_version: consumer_version)}
|
34
35
|
let(:consumer_version) { PactBroker::Domain::Version.new(number: '1.2.3') }
|
35
36
|
let(:consumer) { PactBroker::Domain::Pacticipant.new(name: 'Consumer') }
|
@@ -43,7 +44,7 @@ module PactBroker
|
|
43
44
|
allow(Job).to receive(:perform_in)
|
44
45
|
end
|
45
46
|
|
46
|
-
subject { Service.execute_webhooks pact, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED }
|
47
|
+
subject { Service.execute_webhooks pact, verification, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED }
|
47
48
|
|
48
49
|
it "finds the webhooks" do
|
49
50
|
expect_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_by_consumer_and_provider_and_event_name).with(consumer, provider, PactBroker::Webhooks::WebhookEvent::DEFAULT_EVENT_NAME)
|
@@ -52,7 +53,7 @@ module PactBroker
|
|
52
53
|
|
53
54
|
context "when webhooks are found" do
|
54
55
|
it "executes the webhook" do
|
55
|
-
expect(Service).to receive(:run_later).with(webhooks, pact, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED)
|
56
|
+
expect(Service).to receive(:run_later).with(webhooks, pact, verification, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED)
|
56
57
|
subject
|
57
58
|
end
|
58
59
|
end
|
@@ -97,17 +98,11 @@ module PactBroker
|
|
97
98
|
.create_consumer_version
|
98
99
|
.create_pact
|
99
100
|
.create_webhook(method: 'GET', url: 'http://example.org')
|
101
|
+
.create_verification
|
100
102
|
.and_return(:pact)
|
101
103
|
end
|
102
104
|
|
103
|
-
subject { PactBroker::Webhooks::Service.execute_webhook_now td.webhook, pact }
|
104
|
-
|
105
|
-
it "executes the triggered webhook with the correct options" do
|
106
|
-
allow(PactBroker.configuration).to receive(:show_webhook_response?).and_return('foo')
|
107
|
-
expected_options = { :failure_log_message => "Webhook execution failed", :show_response => 'foo' }
|
108
|
-
expect_any_instance_of(PactBroker::Webhooks::TriggeredWebhook).to receive(:execute).with(expected_options).and_call_original
|
109
|
-
subject
|
110
|
-
end
|
105
|
+
subject { PactBroker::Webhooks::Service.execute_webhook_now td.webhook, pact, td.verification }
|
111
106
|
|
112
107
|
it "executes the HTTP request of the webhook" do
|
113
108
|
subject
|
@@ -118,6 +113,16 @@ module PactBroker
|
|
118
113
|
expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(1)
|
119
114
|
end
|
120
115
|
|
116
|
+
it "saves the pact" do
|
117
|
+
subject
|
118
|
+
expect(PactBroker::Webhooks::TriggeredWebhook.order(:id).last.pact_publication_id).to_not be nil
|
119
|
+
end
|
120
|
+
|
121
|
+
it "saves the verification" do
|
122
|
+
subject
|
123
|
+
expect(PactBroker::Webhooks::TriggeredWebhook.order(:id).last.verification_id).to_not be nil
|
124
|
+
end
|
125
|
+
|
121
126
|
it "saves the execution" do
|
122
127
|
expect { subject }.to change { PactBroker::Webhooks::Execution.count }.by(1)
|
123
128
|
end
|
@@ -141,17 +146,25 @@ module PactBroker
|
|
141
146
|
.create_provider
|
142
147
|
.create_consumer_version
|
143
148
|
.create_pact
|
149
|
+
.create_verification
|
144
150
|
.create_webhook(method: 'GET', url: 'http://example.org', events: events)
|
145
151
|
.and_return(:pact)
|
146
152
|
end
|
147
153
|
|
148
|
-
subject { PactBroker::Webhooks::Service.execute_webhooks pact, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED }
|
154
|
+
subject { PactBroker::Webhooks::Service.execute_webhooks pact, td.verification, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED }
|
149
155
|
|
150
156
|
it "executes the HTTP request of the webhook" do
|
151
157
|
subject
|
152
158
|
expect(http_request).to have_been_made
|
153
159
|
end
|
154
160
|
|
161
|
+
it "executes the webhook with the correct options" do
|
162
|
+
allow(PactBroker.configuration).to receive(:show_webhook_response?).and_return('foo')
|
163
|
+
expected_options = {:show_response => 'foo' }
|
164
|
+
expect_any_instance_of(PactBroker::Domain::WebhookRequest).to receive(:execute).with(anything, anything, hash_including(expected_options)).and_call_original
|
165
|
+
subject
|
166
|
+
end
|
167
|
+
|
155
168
|
it "saves the triggered webhook" do
|
156
169
|
expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(1)
|
157
170
|
end
|
@@ -35,6 +35,7 @@ class TestDataBuilder
|
|
35
35
|
attr_reader :provider
|
36
36
|
attr_reader :consumer_version
|
37
37
|
attr_reader :pact
|
38
|
+
attr_reader :verification
|
38
39
|
attr_reader :webhook
|
39
40
|
attr_reader :webhook_execution
|
40
41
|
attr_reader :triggered_webhook
|
@@ -254,10 +255,14 @@ class TestDataBuilder
|
|
254
255
|
self
|
255
256
|
end
|
256
257
|
|
258
|
+
def create_verification_webhook params = {}
|
259
|
+
create_webhook params.merge(events: [{ name: PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED }])
|
260
|
+
end
|
261
|
+
|
257
262
|
def create_triggered_webhook params = {}
|
258
263
|
params.delete(:comment)
|
259
264
|
trigger_uuid = params[:trigger_uuid] || webhook_service.next_uuid
|
260
|
-
@triggered_webhook = webhook_repository.create_triggered_webhook trigger_uuid, @webhook, @pact, PactBroker::Webhooks::Service::RESOURCE_CREATION
|
265
|
+
@triggered_webhook = webhook_repository.create_triggered_webhook trigger_uuid, @webhook, @pact, nil, PactBroker::Webhooks::Service::RESOURCE_CREATION
|
261
266
|
@triggered_webhook.update(status: params[:status]) if params[:status]
|
262
267
|
set_created_at_if_set params[:created_at], :triggered_webhooks, {id: @triggered_webhook.id}
|
263
268
|
self
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pact_broker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bethany Skurrie
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-06-
|
13
|
+
date: 2018-06-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httparty
|
@@ -617,6 +617,7 @@ files:
|
|
617
617
|
- db/migrations/20180330_refresh_matrix.rb
|
618
618
|
- db/migrations/20180523_create_latest_verifications_for_consumer_version_tags.rb
|
619
619
|
- db/migrations/20180524_create_latest_verifications_for_consumer_and_provider.rb
|
620
|
+
- db/migrations/20180608_add_verification_to_triggered_webhook.rb
|
620
621
|
- db/migrations/migration_helper.rb
|
621
622
|
- db/pact_broker_database.sqlite3
|
622
623
|
- db/test/backwards_compatibility/.rspec
|