pact_broker 2.22.0 → 2.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +1 -0
  4. data/db/migrations/20180611_make_webhook_pacticipant_ids_optional.rb +11 -0
  5. data/example/config.ru +2 -0
  6. data/lib/pact_broker/api.rb +9 -3
  7. data/lib/pact_broker/api/contracts/webhook_contract.rb +36 -0
  8. data/lib/pact_broker/api/decorators/pact_decorator.rb +22 -1
  9. data/lib/pact_broker/api/decorators/pact_webhooks_status_decorator.rb +1 -26
  10. data/lib/pact_broker/api/decorators/triggered_webhook_decorator.rb +33 -0
  11. data/lib/pact_broker/api/decorators/triggered_webhooks_decorator.rb +19 -0
  12. data/lib/pact_broker/api/decorators/verification_decorator.rb +6 -0
  13. data/lib/pact_broker/api/decorators/webhook_decorator.rb +32 -18
  14. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +27 -0
  15. data/lib/pact_broker/api/decorators/{webhook_request_decorator.rb → webhook_request_template_decorator.rb} +1 -1
  16. data/lib/pact_broker/api/pact_broker_urls.rb +21 -1
  17. data/lib/pact_broker/api/resources/all_webhooks.rb +82 -0
  18. data/lib/pact_broker/api/resources/base_resource.rb +18 -0
  19. data/lib/pact_broker/api/resources/error_handler.rb +5 -1
  20. data/lib/pact_broker/api/resources/pact_triggered_webhooks.rb +41 -0
  21. data/lib/pact_broker/api/resources/pact_webhooks.rb +2 -15
  22. data/lib/pact_broker/api/resources/pact_webhooks_status.rb +1 -1
  23. data/lib/pact_broker/api/resources/verification_triggered_webhooks.rb +45 -0
  24. data/lib/pact_broker/api/resources/webhook_execution.rb +1 -5
  25. data/lib/pact_broker/api/resources/webhooks.rb +69 -6
  26. data/lib/pact_broker/configuration.rb +5 -2
  27. data/lib/pact_broker/doc/controllers/app.rb +1 -2
  28. data/lib/pact_broker/doc/views/pact-webhooks.markdown +3 -0
  29. data/lib/pact_broker/doc/views/webhooks.markdown +53 -33
  30. data/lib/pact_broker/domain/pact.rb +4 -0
  31. data/lib/pact_broker/domain/webhook.rb +19 -3
  32. data/lib/pact_broker/domain/webhook_execution_result.rb +6 -1
  33. data/lib/pact_broker/domain/webhook_request.rb +87 -65
  34. data/lib/pact_broker/locale/en.yml +1 -0
  35. data/lib/pact_broker/matrix/repository.rb +3 -1
  36. data/lib/pact_broker/pacts/placeholder_pact.rb +17 -0
  37. data/lib/pact_broker/pacts/repository.rb +14 -0
  38. data/lib/pact_broker/pacts/service.rb +6 -2
  39. data/lib/pact_broker/ui/view_models/index_item.rb +1 -1
  40. data/lib/pact_broker/verifications/placeholder_verification.rb +23 -0
  41. data/lib/pact_broker/verifications/repository.rb +9 -0
  42. data/lib/pact_broker/verifications/service.rb +5 -1
  43. data/lib/pact_broker/version.rb +1 -1
  44. data/lib/pact_broker/webhooks/repository.rb +54 -4
  45. data/lib/pact_broker/webhooks/service.rb +37 -2
  46. data/lib/pact_broker/webhooks/webhook.rb +4 -3
  47. data/lib/pact_broker/webhooks/webhook_event.rb +8 -0
  48. data/lib/pact_broker/webhooks/webhook_request_template.rb +72 -0
  49. data/pact_broker.gemspec +1 -1
  50. data/script/seed.rb +32 -51
  51. data/spec/features/create_webhook_spec.rb +85 -36
  52. data/spec/features/execute_webhook_spec.rb +9 -18
  53. data/spec/features/get_triggered_webhooks_for_pact_spec.rb +20 -0
  54. data/spec/features/get_triggered_webhooks_for_verification_spec.rb +21 -0
  55. data/spec/fixtures/webhook_valid_with_pacticipants.json +23 -0
  56. data/spec/integration/webhooks/certificate_spec.rb +2 -2
  57. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +98 -2
  58. data/spec/lib/pact_broker/api/decorators/triggered_webhook_decorator_spec.rb +64 -0
  59. data/spec/lib/pact_broker/api/decorators/triggered_webhooks_decorator_spec.rb +28 -0
  60. data/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +8 -0
  61. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +37 -1
  62. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +34 -1
  63. data/spec/lib/pact_broker/api/decorators/{webhook_request_decorator_spec.rb → webhook_request_template_decorator_spec.rb} +7 -9
  64. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +22 -0
  65. data/spec/lib/pact_broker/api/resources/{pact_webhooks_spec.rb → all_webhooks_spec.rb} +46 -80
  66. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +34 -0
  67. data/spec/lib/pact_broker/api/resources/pact_triggered_webhooks_spec.rb +54 -0
  68. data/spec/lib/pact_broker/api/resources/pacticipant_spec.rb +1 -6
  69. data/spec/lib/pact_broker/api/resources/tag_spec.rb +1 -6
  70. data/spec/lib/pact_broker/api/resources/verification_triggered_webhooks_spec.rb +68 -0
  71. data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +2 -8
  72. data/spec/lib/pact_broker/api/resources/webhooks_spec.rb +216 -21
  73. data/spec/lib/pact_broker/configuration_spec.rb +30 -0
  74. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +20 -64
  75. data/spec/lib/pact_broker/domain/webhook_spec.rb +40 -11
  76. data/spec/lib/pact_broker/matrix/repository_spec.rb +33 -0
  77. data/spec/lib/pact_broker/pacts/pact_version_spec.rb +1 -0
  78. data/spec/lib/pact_broker/pacts/repository_spec.rb +39 -1
  79. data/spec/lib/pact_broker/ui/view_models/index_item_spec.rb +1 -1
  80. data/spec/lib/pact_broker/verifications/repository_spec.rb +37 -0
  81. data/spec/lib/pact_broker/verifications/service_spec.rb +2 -2
  82. data/spec/lib/pact_broker/webhooks/render_spec.rb +15 -0
  83. data/spec/lib/pact_broker/webhooks/repository_spec.rb +149 -30
  84. data/spec/lib/pact_broker/webhooks/service_spec.rb +84 -7
  85. data/spec/lib/pact_broker/webhooks/webhook_request_template_spec.rb +81 -0
  86. data/spec/service_consumers/pact_helper.rb +2 -0
  87. data/spec/service_consumers/provider_states_for_pact_broker_client.rb +8 -0
  88. data/spec/service_consumers/provider_states_for_pact_ruby.rb +132 -0
  89. data/spec/support/test_data_builder.rb +30 -7
  90. metadata +37 -9
@@ -0,0 +1,82 @@
1
+ require 'pact_broker/services'
2
+ require 'pact_broker/api/resources/base_resource'
3
+ require 'pact_broker/api/decorators/webhooks_decorator'
4
+ require 'pact_broker/api/decorators/webhook_decorator'
5
+ require 'pact_broker/api/contracts/webhook_contract'
6
+
7
+ module PactBroker
8
+ module Api
9
+ module Resources
10
+ class AllWebhooks < BaseResource
11
+
12
+ def content_types_provided
13
+ [["application/hal+json", :to_json]]
14
+ end
15
+
16
+ def content_types_accepted
17
+ [["application/json", :from_json]]
18
+ end
19
+
20
+ def allowed_methods
21
+ ["GET", "POST"]
22
+ end
23
+
24
+ def create_path
25
+ webhook_url next_uuid, base_url
26
+ end
27
+
28
+ def post_is_create?
29
+ true
30
+ end
31
+
32
+ def malformed_request?
33
+ if request.post?
34
+ return invalid_json? || validation_errors?(webhook)
35
+ end
36
+ false
37
+ end
38
+
39
+ def to_json
40
+ Decorators::WebhooksDecorator.new(webhooks).to_json(user_options: decorator_context(resource_title: "Webhooks"))
41
+ end
42
+
43
+ def from_json
44
+ saved_webhook = webhook_service.create next_uuid, webhook, consumer, provider
45
+ response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(user_options: { base_url: base_url })
46
+ end
47
+
48
+ private
49
+
50
+ def validation_errors? webhook
51
+ errors = webhook_service.errors(webhook)
52
+
53
+ unless errors.empty?
54
+ set_json_validation_error_messages(errors.messages)
55
+ end
56
+
57
+ !errors.empty?
58
+ end
59
+
60
+ def consumer
61
+ webhook.consumer ? pacticipant_service.find_pacticipant_by_name(webhook.consumer.name) : nil
62
+ end
63
+
64
+ def provider
65
+ webhook.provider ? pacticipant_service.find_pacticipant_by_name(webhook.provider.name) : nil
66
+ end
67
+
68
+ def webhooks
69
+ webhook_service.find_all
70
+ end
71
+
72
+ def webhook
73
+ @webhook ||= Decorators::WebhookDecorator.new(PactBroker::Domain::Webhook.new).from_json(request_body)
74
+ end
75
+
76
+ def next_uuid
77
+ @next_uuid ||= webhook_service.next_uuid
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -152,6 +152,24 @@ module PactBroker
152
152
  def with_matrix_refresh &block
153
153
  matrix_service.refresh(identifier_from_path, &block)
154
154
  end
155
+
156
+ def find_pacticipant name, role
157
+ pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
158
+ set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
159
+ end
160
+ end
161
+
162
+ def consumer
163
+ @consumer ||= identifier_from_path[:consumer_name] && find_pacticipant(identifier_from_path[:consumer_name], "consumer")
164
+ end
165
+
166
+ def provider
167
+ @provider ||= identifier_from_path[:provider_name] && find_pacticipant(identifier_from_path[:provider_name], "provider")
168
+ end
169
+
170
+ def pact
171
+ @pact ||= pact_service.find_pact(pact_params)
172
+ end
155
173
  end
156
174
  end
157
175
  end
@@ -11,7 +11,11 @@ module PactBroker
11
11
  def self.call e, request, response
12
12
  logger.error e
13
13
  logger.error e.backtrace
14
- response.body = {:message => e.message, :backtrace => e.backtrace }.to_json
14
+ response_body = { error: { :message => e.message } }
15
+ if PactBroker.configuration.show_backtrace_in_error_response?
16
+ response_body[:error][:backtrace] = e.backtrace
17
+ end
18
+ response.body = response_body.to_json
15
19
  report(e, request) if reportable?(e)
16
20
  end
17
21
 
@@ -0,0 +1,41 @@
1
+ require 'pact_broker/api/decorators/triggered_webhooks_decorator'
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ class PactTriggeredWebhooks < BaseResource
7
+ def allowed_methods
8
+ ["GET"]
9
+ end
10
+
11
+ def content_types_provided
12
+ [["application/hal+json", :to_json]]
13
+ end
14
+
15
+ def resource_exists?
16
+ !!pact
17
+ end
18
+
19
+ def to_json
20
+ Decorators::TriggeredWebhooksDecorator.new(triggered_webhooks).to_json(decorator_options)
21
+ end
22
+
23
+ private
24
+
25
+ def triggered_webhooks
26
+ webhook_service.find_triggered_webhooks_for_pact(pact)
27
+ end
28
+
29
+ def resource_title
30
+ "Webhooks triggered by the publication of the #{pact.name[0].downcase}#{pact.name[1..-1]}"
31
+ end
32
+
33
+ def decorator_options
34
+ {
35
+ user_options: decorator_context.merge(resource_title: resource_title)
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  require 'pact_broker/api/resources/base_resource'
3
2
  require 'pact_broker/api/decorators/webhook_decorator'
4
3
  require 'pact_broker/api/decorators/webhooks_decorator'
@@ -24,7 +23,8 @@ module PactBroker
24
23
  end
25
24
 
26
25
  def resource_exists?
27
- consumer && provider
26
+ (identifier_from_path[:consumer_name].nil? || consumer) &&
27
+ (identifier_from_path[:provider_name].nil? || provider)
28
28
  end
29
29
 
30
30
  def malformed_request?
@@ -76,19 +76,6 @@ module PactBroker
76
76
  @next_uuid ||= webhook_service.next_uuid
77
77
  end
78
78
 
79
- def consumer
80
- @consumer ||= find_pacticipant(identifier_from_path[:consumer_name], "consumer")
81
- end
82
-
83
- def provider
84
- @provider ||= find_pacticipant(identifier_from_path[:provider_name], "provider")
85
- end
86
-
87
- def find_pacticipant name, role
88
- pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
89
- set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
90
- end
91
- end
92
79
 
93
80
  end
94
81
  end
@@ -27,7 +27,7 @@ module PactBroker
27
27
  private
28
28
 
29
29
  def latest_triggered_webhooks
30
- @latest_triggered_webhooks ||= webhook_service.find_latest_triggered_webhooks(consumer, provider)
30
+ @latest_triggered_webhooks ||= webhook_service.find_latest_triggered_webhooks_for_pact(pact)
31
31
  end
32
32
 
33
33
  def pact
@@ -0,0 +1,45 @@
1
+ require 'pact_broker/api/decorators/triggered_webhooks_decorator'
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ class VerificationTriggeredWebhooks < BaseResource
7
+ def allowed_methods
8
+ ["GET"]
9
+ end
10
+
11
+ def content_types_provided
12
+ [["application/hal+json", :to_json]]
13
+ end
14
+
15
+ def resource_exists?
16
+ !!verification
17
+ end
18
+
19
+ def to_json
20
+ Decorators::TriggeredWebhooksDecorator.new(triggered_webhooks).to_json(decorator_options)
21
+ end
22
+
23
+ private
24
+
25
+ def triggered_webhooks
26
+ webhook_service.find_triggered_webhooks_for_verification(verification)
27
+ end
28
+
29
+ def resource_title
30
+ "Webhooks triggered by the publication of verification result #{verification.number}"
31
+ end
32
+
33
+ def decorator_options
34
+ {
35
+ user_options: decorator_context.merge(resource_title: resource_title)
36
+ }
37
+ end
38
+
39
+ def verification
40
+ @verification ||= verification_service.find(identifier_from_path)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -14,7 +14,7 @@ module PactBroker
14
14
  end
15
15
 
16
16
  def process_post
17
- webhook_execution_result = webhook_service.execute_webhook_now webhook, pact
17
+ webhook_execution_result = webhook_service.test_execution(webhook)
18
18
  response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
19
19
  response.body = post_response_body webhook_execution_result
20
20
  if webhook_execution_result.success?
@@ -39,10 +39,6 @@ module PactBroker
39
39
  @webhook ||= webhook_service.find_by_uuid uuid
40
40
  end
41
41
 
42
- def pact
43
- @pact ||= pact_service.find_latest_pact consumer_name: webhook.consumer_name, provider_name: webhook.provider_name
44
- end
45
-
46
42
  def uuid
47
43
  identifier_from_path[:uuid]
48
44
  end
@@ -1,29 +1,92 @@
1
- require 'pact_broker/services'
2
1
  require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/api/decorators/webhook_decorator'
3
3
  require 'pact_broker/api/decorators/webhooks_decorator'
4
+ require 'pact_broker/api/contracts/webhook_contract'
4
5
 
5
6
  module PactBroker
6
7
  module Api
7
8
  module Resources
8
-
9
9
  class Webhooks < BaseResource
10
10
 
11
+ def allowed_methods
12
+ ["POST", "GET"]
13
+ end
14
+
11
15
  def content_types_provided
12
16
  [["application/hal+json", :to_json]]
13
17
  end
14
18
 
15
- def allowed_methods
16
- ["GET"]
19
+ def content_types_accepted
20
+ [["application/json", :from_json]]
21
+ end
22
+
23
+ def resource_exists?
24
+ (identifier_from_path[:consumer_name].nil? || consumer) &&
25
+ (identifier_from_path[:provider_name].nil? || provider)
26
+ end
27
+
28
+ def malformed_request?
29
+ if request.post?
30
+ return invalid_json? || validation_errors?(webhook)
31
+ end
32
+ false
33
+ end
34
+
35
+ def validation_errors? webhook
36
+ errors = webhook_service.errors(webhook)
37
+
38
+ unless errors.empty?
39
+ response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
40
+ response.body = { errors: errors.messages }.to_json
41
+ end
42
+
43
+ !errors.empty?
44
+ end
45
+
46
+ def create_path
47
+ webhook_url next_uuid, base_url
48
+ end
49
+
50
+ def post_is_create?
51
+ true
52
+ end
53
+
54
+ def from_json
55
+ saved_webhook = webhook_service.create next_uuid, webhook, consumer, provider
56
+ response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(user_options: { base_url: base_url })
17
57
  end
18
58
 
19
59
  def to_json
20
- Decorators::WebhooksDecorator.new(webhooks).to_json(user_options: decorator_context(resource_title: "Webhooks"))
60
+ Decorators::WebhooksDecorator.new(webhooks).to_json(user_options: decorator_context(resource_title: 'Pact webhooks'))
21
61
  end
22
62
 
63
+ private
64
+
23
65
  def webhooks
24
- webhook_service.find_all
66
+ webhook_service.find_by_consumer_and_provider consumer, provider
67
+ end
68
+
69
+ def webhook
70
+ @webhook ||= Decorators::WebhookDecorator.new(PactBroker::Domain::Webhook.new).from_json(request_body)
25
71
  end
26
72
 
73
+ def next_uuid
74
+ @next_uuid ||= webhook_service.next_uuid
75
+ end
76
+
77
+ def consumer
78
+ @consumer ||= identifier_from_path[:consumer_name] && find_pacticipant(identifier_from_path[:consumer_name], "consumer")
79
+ end
80
+
81
+ def provider
82
+ @provider ||= identifier_from_path[:provider_name] && find_pacticipant(identifier_from_path[:provider_name], "provider")
83
+ end
84
+
85
+ def find_pacticipant name, role
86
+ pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
87
+ set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
88
+ end
89
+ end
27
90
  end
28
91
  end
29
92
  end
@@ -73,8 +73,7 @@ module PactBroker
73
73
  config.version_parser = PactBroker::Versions::ParseSemanticVersion
74
74
  config.sha_generator = PactBroker::Pacts::GenerateSha
75
75
  config.base_equality_only_on_content_that_affects_verification_results = false
76
- # TODO change this to true
77
- config.order_versions_by_date = false
76
+ config.order_versions_by_date = true
78
77
  config.semver_formats = ["%M.%m.%p%s%d", "%M.%m", "%M"]
79
78
  config.webhook_retry_schedule = [10, 60, 120, 300, 600, 1200] #10 sec, 1 min, 2 min, 5 min, 10 min, 20 min => 38 minutes
80
79
  config.check_for_potential_duplicate_pacticipant_names = true
@@ -92,6 +91,10 @@ module PactBroker
92
91
  }
93
92
  end
94
93
 
94
+ def show_backtrace_in_error_response?
95
+ !!(ENV['RACK_ENV'] && ENV['RACK_ENV'].downcase != 'production')
96
+ end
97
+
95
98
  def authentication_configured?
96
99
  !!authenticate || !!authenticate_with_basic_auth
97
100
  end
@@ -13,8 +13,7 @@ module PactBroker
13
13
 
14
14
  MAPPINGS = {
15
15
  'webhooks-create' => 'webhooks',
16
- 'webhooks-webhooks' => 'webhooks',
17
- 'pact-webhooks' => 'webhooks',
16
+ 'webhooks-webhooks' => 'webhooks'
18
17
  }.freeze
19
18
 
20
19
  helpers do
@@ -0,0 +1,3 @@
1
+ # Pact webhooks
2
+
3
+ This resource is deprecated, as it requires both a consumer and provider to be specified. See [/doc/webhooks](/doc/webhooks) for the latest documentation on creating webhooks that can have an optional consumer and provider.
@@ -2,27 +2,28 @@
2
2
 
3
3
  *Collection resource*
4
4
 
5
- Path: `/webhooks/provider/PROVIDER/consumer/CONSUMER`
5
+ Path: `/webhooks`
6
6
 
7
7
  Allowed methods: `GET`, `POST`
8
8
 
9
9
  *Individual resource*
10
10
 
11
- Path: `/webhook
12
- s/UUID`
11
+ Path: `/webhook/UUID`
13
12
 
14
13
  Allowed methods: `GET`, `PUT`, `DELETE`
15
14
 
15
+ Webhooks are HTTP requests that are executed asynchronously after certain events occur in the Pact Broker, that can be used to create a workflow or notify people of changes to the data contained in the Pact Broker. The most common use for webhooks is to trigger builds when a pact has changed or a verification result has been published, but they can also be used for conveying information like posting notifications to Slack, or commit statuses to Github.
16
+
16
17
  ### Creating
17
18
 
18
- 1. To create a webhook, in the HAL Browser, navigate to the pact you want to create the webhook for
19
- (Click "Go to Entry Point", then select "latest-pacts", then select the pact you want to create the webhook for.)
20
- 2. Click the "NON-GET" button for the "pact-webhooks" relation.
21
- 3. Paste in the webhook JSON (example shown below) in the body section and click "Make Request".
19
+ To create a webhook, send a `POST` request to `/webhooks` with the body described below. You can do this through the API Browser by clicking on the `NON-GET` button for the `pb:webhooks` relation on the index, pasting in the JSON body, and clicking "Make Request".
22
20
 
23
- An example webhook to trigger a Bamboo job when a contract has changed.
21
+ Below is an example webhook to trigger a Bamboo job when any contract for the provider "Foo" has changed. Both provider and consumer are optional - omitting either indicates that any pacticipant in that role will be matched. Webhooks with neither provider nor consumer specified are "global" webhooks that will trigger for any consumer/provider pair.
24
22
 
25
23
  {
24
+ "provider": {
25
+ "name": "Bar"
26
+ },
26
27
  "events": [{
27
28
  "name": "contract_content_changed"
28
29
  }],
@@ -46,45 +47,42 @@ A request body can be specified as well.
46
47
  "request": {
47
48
  "method": "POST",
48
49
  "url": "http://example.org/something",
50
+ "headers": {
51
+ "Content-Type": "application/json"
52
+ },
49
53
  "body": {
50
54
  "some" : "json"
51
55
  }
52
56
  }
53
57
  }
54
58
 
55
- **BEWARE** The password can be reverse engineered from the database, so make a separate account for the Pact Broker to use, don't use your personal account!
56
-
57
- <a name="whitelist"></a>
58
- #### Webhook Whitelist
59
-
60
- To ensure that webhooks cannot be used maliciously to expose either data about your contracts or your internal network, the following validation rules are applied to webhooks via the Pact Broker configuration settings.
61
-
62
- * **Scheme**: Must be included in the `webhook_scheme_whitelist`, which by default only includes `https`. You can change this to include `http` if absolutely necessary, however, keep in mind that the body of any http traffic is visible to the network. You can load a self signed certificate into the Pact Broker to be used for https connections using [script/insert-self-signed-certificate-from-url.rb](https://github.com/pact-foundation/pact_broker/blob/master/script/insert-self-signed-certificate-from-url.rb) in the
63
- Pact Broker Github repository.
64
-
65
- * **HTTP method**: Must be included in the `webhook_http_method_whitelist`, which by default only includes `POST`. It is highly recommended that only `POST` requests are allowed to ensure that webhooks cannot be used to retrieve sensitive information from hosts within the same network.
66
-
67
- * **Host**: If the `webhook_host_whitelist` contains any entries, the host must match one or more of the entries. By default, it is empty. For security purposes, if the host whitelist is empty, the response details will not be logged to the UI (though they can be seen in the application logs at debug level).
68
-
69
- The host whitelist may contain hostnames (eg `"github.com"`), IPs (eg `"192.0.345.4"`), network ranges (eg `"10.0.0.0/8"`) or regular expressions (eg `/.*\.foo\.com$/`). Note that IPs are not resolved, so if you specify an IP range, you need to use the IP in the webhook URL. If you wish to allow webhooks to any host (not recommended!), you can set `webhook_host_whitelist` to `[/.*/]`. Beware of any sensitive endpoints that may be exposed within the same network.
70
-
71
- The recommended set of values to start with are:
72
-
73
- * your CI server's hostname (for triggering builds)
74
- * your company chat (eg. Slack, for publishing notifications)
75
- * your code repository (eg. Github, for sending commit statuses)
59
+ To specify an XML body, you will need to use a correctly escaped string (or use single quotes if allowed).
60
+
61
+ {
62
+ "events": [{
63
+ "name": "contract_content_changed"
64
+ }],
65
+ "request": {
66
+ "method": "POST",
67
+ "url": "http://example.org/something",
68
+ "headers": {
69
+ "Content-Type": "application/xml"
70
+ },
71
+ "body": "<xml \"foo\"=\"bar\"></xml>"
72
+ }
73
+ }
76
74
 
77
- Alternatively, you could use a regular expression to limit requests to your company's domain. eg `/.*\.foo\.com$/` (don't forget the end of string anchor). You can test Ruby regular expressions at [rubular.com](http://rubular.com).
75
+ **BEWARE** While the basic auth password, and any header containing the word `authorization` or `token` will be redacted from the UI and the logs, the password could be reverse engineered from the database, so make a separate account for the Pact Broker to use in your webhooks. Don't use your personal account!
78
76
 
79
77
  #### Event types
80
78
 
81
- `contract_content_changed:` triggered when the content of the contract has changed since the previous publication. Uses plain string equality, so changes to the ordering of hash keys, or whitespace changes will trigger this webhook.
79
+ `contract_content_changed:` triggered when the content of the contract has changed since the previous publication. If `base_equality_only_on_content_that_affects_verification_results` is set to `true` in the configuration (the default), any changes to whitespace, ordering of keys, or the ordering of the `interactions` or `messages` will be ignored, and will not trigger this event.
82
80
 
83
81
  `provider_verification_published:` triggered whenever a provider publishes a verification.
84
82
 
85
83
  ### Dynamic variable substitution
86
84
 
87
- The following variables may be used in the request parameters or body, and will be replaced with their appropriate values at runtime.
85
+ The following variables may be used in the request path, parameters or body, and will be replaced with their appropriate values at runtime.
88
86
 
89
87
  `${pactbroker.consumerName}`: the consumer name
90
88
  `${pactbroker.providerName}`: the provider name
@@ -111,9 +109,31 @@ Example usage:
111
109
  }
112
110
  }
113
111
 
112
+ <a name="whitelist"></a>
113
+ ### Webhook Whitelist
114
+
115
+ To ensure that webhooks cannot be used maliciously to expose either data about your contracts or your internal network, the following validation rules are applied to webhooks via the Pact Broker configuration settings.
116
+
117
+ * **Scheme**: Must be included in the `webhook_scheme_whitelist`, which by default only includes `https`. You can change this to include `http` if absolutely necessary, however, keep in mind that the body of any http traffic is visible to the network. You can load a self signed certificate into the Pact Broker to be used for https connections using [script/insert-self-signed-certificate-from-url.rb](https://github.com/pact-foundation/pact_broker/blob/master/script/insert-self-signed-certificate-from-url.rb) in the
118
+ Pact Broker Github repository.
119
+
120
+ * **HTTP method**: Must be included in the `webhook_http_method_whitelist`, which by default only includes `POST`. It is highly recommended that only `POST` requests are allowed to ensure that webhooks cannot be used to retrieve sensitive information from hosts within the same network.
121
+
122
+ * **Host**: If the `webhook_host_whitelist` contains any entries, the host must match one or more of the entries. By default, it is empty. For security purposes, if the host whitelist is empty, the response details will not be logged to the UI (though they can be seen in the application logs at debug level).
123
+
124
+ The host whitelist may contain hostnames (eg `"github.com"`), IPs (eg `"192.0.345.4"`), network ranges (eg `"10.0.0.0/8"`) or regular expressions (eg `/.*\.foo\.com$/`). Note that IPs are not resolved, so if you specify an IP range, you need to use the IP in the webhook URL. If you wish to allow webhooks to any host (not recommended!), you can set `webhook_host_whitelist` to `[/.*/]`. Beware of any sensitive endpoints that may be exposed within the same network.
125
+
126
+ The recommended set of values to start with are:
127
+
128
+ * your CI server's hostname (for triggering builds)
129
+ * your company chat (eg. Slack, for publishing notifications)
130
+ * your code repository (eg. Github, for sending commit statuses)
131
+
132
+ Alternatively, you could use a regular expression to limit requests to your company's domain. eg `/.*\.foo\.com$/` (don't forget the end of string anchor). You can test Ruby regular expressions at [rubular.com](http://rubular.com).
133
+
114
134
  ### Testing
115
135
 
116
- To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "execute" relation. The response or error will be shown in the window.
136
+ To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "pb:execute" relation. The latest matching pact/verification will be used in the template, or a placeholder if none exists. The response or error will be shown in the window.
117
137
 
118
138
  ### Deleting
119
139