pact_broker 2.20.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|