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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/CHANGELOG.md +23 -0
  4. data/db/migrations/20180608_add_verification_to_triggered_webhook.rb +7 -0
  5. data/lib/pact_broker/api/contracts/webhook_contract.rb +13 -4
  6. data/lib/pact_broker/api/decorators/webhook_request_decorator.rb +5 -1
  7. data/lib/pact_broker/doc/views/webhooks.markdown +8 -1
  8. data/lib/pact_broker/domain/pact.rb +1 -1
  9. data/lib/pact_broker/domain/version.rb +1 -1
  10. data/lib/pact_broker/domain/webhook.rb +2 -2
  11. data/lib/pact_broker/domain/webhook_request.rb +24 -16
  12. data/lib/pact_broker/locale/en.yml +1 -0
  13. data/lib/pact_broker/pacts/pact_publication.rb +5 -0
  14. data/lib/pact_broker/pacts/pact_version.rb +6 -1
  15. data/lib/pact_broker/pacts/service.rb +11 -5
  16. data/lib/pact_broker/verifications/service.rb +2 -1
  17. data/lib/pact_broker/version.rb +1 -1
  18. data/lib/pact_broker/webhooks/render.rb +48 -5
  19. data/lib/pact_broker/webhooks/repository.rb +2 -1
  20. data/lib/pact_broker/webhooks/service.rb +8 -8
  21. data/lib/pact_broker/webhooks/triggered_webhook.rb +2 -1
  22. data/spec/integration/webhooks/certificate_spec.rb +1 -1
  23. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +24 -1
  24. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +7 -0
  25. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +68 -26
  26. data/spec/lib/pact_broker/domain/webhook_spec.rb +4 -3
  27. data/spec/lib/pact_broker/matrix/head_row_spec.rb +1 -2
  28. data/spec/lib/pact_broker/pacts/service_spec.rb +5 -4
  29. data/spec/lib/pact_broker/verifications/service_spec.rb +9 -0
  30. data/spec/lib/pact_broker/webhooks/render_spec.rb +92 -8
  31. data/spec/lib/pact_broker/webhooks/repository_spec.rb +11 -1
  32. data/spec/lib/pact_broker/webhooks/service_spec.rb +24 -11
  33. data/spec/support/test_data_builder.rb +6 -1
  34. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e14b4974d382ad35da338f8b493f4ea721105a7c
4
- data.tar.gz: 3d27068e75bea109b5bc3bac0262061ce973f574
3
+ metadata.gz: 4e08f85dd95e1f881127b7592ac697e7b327e0ab
4
+ data.tar.gz: c13635638b9768858e0fd87c46a593d440586ba8
5
5
  SHA512:
6
- metadata.gz: bf62aadde7feb83cb578fccb4a853530e1d2c3967f9abfc85d649ff9640c58f410bcfaa5b6a0d347444cf1aab47f7bf5e68d00f67c71d34c9d4fc8ebb3fc8e25
7
- data.tar.gz: fb663dbb7503fb6f1d43f53c820257844a2573f9cf1913995e22d04f8cc3e15c53acf59e56fe5bb9e049ef5d9d915e660eeec99f58defcc46b5d22d71df392d4
6
+ metadata.gz: daff9fab78dd9a83bd51f0010c6c3b754c4aeb30eb6bdc40cb22772c7d654de4d7af91882c3e5429d687c63c9464ef326e6c4d095e58cf379b8e3a807f04448d
7
+ data.tar.gz: 1b305e38c05e8d2395260f4909439aa354c7ebd4b555a36fe38531b306a612b1a05bcce611880b8a17d0db1652dffd9847271f4eebe8d7e080daba2a98da6f83
data/.travis.yml CHANGED
@@ -2,7 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 2.4.1
4
4
  - 2.3.4
5
- - 2.2.7
6
5
  services:
7
6
  - postgresql
8
7
  - mysql
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
 
@@ -0,0 +1,7 @@
1
+ Sequel.migration do
2
+ change do
3
+ alter_table(:triggered_webhooks) do
4
+ add_foreign_key(:verification_id, :verifications)
5
+ end
6
+ end
7
+ end
@@ -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 = URI(url)
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 = URI(url).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(URI(url).host, host_whitelist).any?
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 : headers }
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.pactUrl}`: the "permalink" URL to the newly published pact (the URL specifying the consumer version URL, rather than the "/latest" format.)
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 execute pact, options = {}
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 Webhooks::RedactLogs.call("#{name}: #{value}")
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=#{headers}, body=#{body}"
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 pact
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, :reciprocal => :pact_version
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
- def pact_has_changed_since_previous_version? pact
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 && pact.json_content != previous_pact.json_content
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
- if pact_has_changed_since_previous_version? pact
126
- webhook_service.execute_webhooks pact, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED
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
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.20.0'
2
+ VERSION = '2.21.0'
3
3
  end
@@ -1,11 +1,22 @@
1
1
  module PactBroker
2
2
  module Webhooks
3
3
  class Render
4
- def self.call(body, pact, verification = nil, &escaper)
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.consumerVersionNumber}' => pact.consumer_version_number
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(body) do | body, (key, value) |
18
- body.gsub(key, value)
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", show_response: PactBroker.configuration.show_webhook_response?}
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: {'Content-Type' => 'text/plain', 'Authorization' => 'foo'},
33
+ headers: headers,
31
34
  username: username,
32
35
  password: password,
33
36
  body: body)
34
37
  end
35
38
 
36
- let(:logs) { subject.execute(pact, options).logs }
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 nil
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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: [REDACTED]"
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
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(subject.execute(pact, options).success?).to be true
298
+ expect(execute.success?).to be true
257
299
  end
258
300
 
259
301
  it "sets the response on the result" do
260
- expect(subject.execute(pact, options).response).to be_instance_of(Net::HTTPOK)
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(subject.execute(pact, options).success?).to be false
315
+ expect(execute.success?).to be false
274
316
  end
275
317
 
276
318
  it "sets the response on the result" do
277
- expect(subject.execute(pact, options).response).to be_instance_of(Net::HTTPInternalServerError)
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 = subject.execute(pact, options)
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
- subject.execute(pact, options)
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
- subject.execute(pact, options)
353
+ execute
312
354
  end
313
355
 
314
356
  it "returns a WebhookExecutionResult with success=false" do
315
- expect(subject.execute(pact, options).success?).to be false
357
+ expect(execute.success?).to be false
316
358
  end
317
359
 
318
360
  it "returns a WebhookExecutionResult with an error" do
319
- expect(subject.execute(pact, options).error).to be_instance_of WebhookTestError
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 "#pact_has_changed_since_previous_version?" do
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.pact_has_changed_since_previous_version? pact }
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
- it "returns false" do
64
- expect(subject).to be_falsey
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(:body) do
13
- "Foo ${pactbroker.pactUrl} ${pactbroker.consumerVersionNumber}"
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(:pact) do
17
- instance_double("pact", consumer_version_number: "1.2.3+foo")
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
- subject { Render.call(body, pact, nil) }
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
- it { is_expected.to eq "Foo http://foo 1.2.3+foo" }
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(body, pact, nil) do | value |
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 "Foo http%3A%2F%2Ffoo 1.2.3%2Bfoo" }
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.20.0
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-07 00:00:00.000000000 Z
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