pact_broker 2.31.0 → 2.32.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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/db/migrations/20190509_create_version_sequence.rb +8 -0
  4. data/db/migrations/20190510_set_version_sequence.rb +9 -0
  5. data/db/migrations/20190511_create_integrations_view.rb +19 -0
  6. data/db/migrations/20190523_add_enabled_column_to_webhooks.rb +5 -0
  7. data/db/migrations/20190524_set_webhooks_enabled.rb +10 -0
  8. data/db/migrations/20190525_add_description_column_to_webhooks.rb +5 -0
  9. data/lib/pact_broker/api.rb +3 -0
  10. data/lib/pact_broker/api/decorators/base_decorator.rb +0 -3
  11. data/lib/pact_broker/api/decorators/integration_decorator.rb +40 -0
  12. data/lib/pact_broker/api/decorators/integrations_decorator.rb +19 -0
  13. data/lib/pact_broker/api/decorators/pact_decorator.rb +1 -1
  14. data/lib/pact_broker/api/decorators/pact_details_decorator.rb +1 -6
  15. data/lib/pact_broker/api/decorators/verifiable_pact_decorator.rb +12 -0
  16. data/lib/pact_broker/api/decorators/verifiable_pacts_decorator.rb +20 -0
  17. data/lib/pact_broker/api/decorators/webhook_decorator.rb +5 -1
  18. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +0 -1
  19. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +2 -2
  20. data/lib/pact_broker/api/pact_broker_urls.rb +37 -3
  21. data/lib/pact_broker/api/resources/base_resource.rb +4 -0
  22. data/lib/pact_broker/api/resources/dashboard.rb +1 -1
  23. data/lib/pact_broker/api/resources/integrations.rb +10 -2
  24. data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +8 -14
  25. data/lib/pact_broker/api/resources/pact.rb +15 -3
  26. data/lib/pact_broker/api/resources/verification.rb +0 -2
  27. data/lib/pact_broker/api/resources/verifications.rb +18 -1
  28. data/lib/pact_broker/api/resources/webhook.rb +1 -1
  29. data/lib/pact_broker/api/resources/webhook_execution.rb +18 -9
  30. data/lib/pact_broker/app.rb +10 -11
  31. data/lib/pact_broker/db.rb +2 -2
  32. data/lib/pact_broker/db/data_migrations/helpers.rb +11 -0
  33. data/lib/pact_broker/db/data_migrations/set_latest_version_sequence_value.rb +29 -0
  34. data/lib/pact_broker/db/data_migrations/set_webhooks_enabled.rb +17 -0
  35. data/lib/pact_broker/db/migrate_data.rb +2 -0
  36. data/lib/pact_broker/db/seed_example_data.rb +6 -3
  37. data/lib/pact_broker/domain/order_versions.rb +14 -1
  38. data/lib/pact_broker/domain/verification.rb +4 -0
  39. data/lib/pact_broker/domain/webhook.rb +13 -3
  40. data/lib/pact_broker/domain/webhook_request.rb +3 -2
  41. data/lib/pact_broker/index/service.rb +3 -0
  42. data/lib/pact_broker/integrations/integration.rb +10 -0
  43. data/lib/pact_broker/integrations/service.rb +5 -0
  44. data/lib/pact_broker/matrix/parse_query.rb +2 -0
  45. data/lib/pact_broker/matrix/row.rb +8 -0
  46. data/lib/pact_broker/matrix/service.rb +1 -2
  47. data/lib/pact_broker/pacts/service.rb +16 -9
  48. data/lib/pact_broker/test/test_data_builder.rb +6 -2
  49. data/lib/pact_broker/verifications/sequence.rb +0 -2
  50. data/lib/pact_broker/verifications/service.rb +10 -2
  51. data/lib/pact_broker/version.rb +1 -1
  52. data/lib/pact_broker/versions/sequence.rb +38 -0
  53. data/lib/pact_broker/webhooks/job.rb +19 -7
  54. data/lib/pact_broker/webhooks/render.rb +30 -13
  55. data/lib/pact_broker/webhooks/repository.rb +3 -4
  56. data/lib/pact_broker/webhooks/service.rb +60 -34
  57. data/lib/pact_broker/webhooks/trigger_service.rb +6 -6
  58. data/lib/pact_broker/webhooks/webhook.rb +9 -1
  59. data/lib/pact_broker/webhooks/webhook_request_template.rb +11 -7
  60. data/lib/rack/pact_broker/database_transaction.rb +6 -2
  61. data/script/publish-new.sh +23 -1
  62. data/script/publish.sh +13 -4
  63. data/script/seed.rb +53 -52
  64. data/spec/features/create_webhook_spec.rb +2 -0
  65. data/spec/features/execute_webhook_spec.rb +4 -3
  66. data/spec/features/get_integrations_spec.rb +17 -0
  67. data/spec/features/get_pacts_to_verify_spec.rb +7 -3
  68. data/spec/features/publish_verification_spec.rb +8 -1
  69. data/spec/features/update_webhook_spec.rb +47 -0
  70. data/spec/integration/webhooks/certificate_spec.rb +1 -1
  71. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +12 -0
  72. data/spec/lib/pact_broker/api/decorators/integration_decorator_spec.rb +82 -0
  73. data/spec/lib/pact_broker/api/decorators/integrations_decorator_spec.rb +29 -0
  74. data/spec/lib/pact_broker/api/decorators/pact_decorator_spec.rb +12 -4
  75. data/spec/lib/pact_broker/api/decorators/verifiable_pact_decorator_spec.rb +30 -0
  76. data/spec/lib/pact_broker/api/decorators/verifiable_pacts_decorator_spec.rb +29 -0
  77. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +20 -1
  78. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +0 -4
  79. data/spec/lib/pact_broker/api/decorators/webhooks_decorator_spec.rb +5 -4
  80. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +64 -1
  81. data/spec/lib/pact_broker/api/resources/pact_spec.rb +0 -3
  82. data/spec/lib/pact_broker/api/resources/verifications_spec.rb +40 -10
  83. data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +13 -3
  84. data/spec/lib/pact_broker/app_spec.rb +28 -4
  85. data/spec/lib/pact_broker/db/data_migrations/set_latest_version_sequence_value_spec.rb +68 -0
  86. data/spec/lib/pact_broker/domain/order_versions_spec.rb +1 -4
  87. data/spec/lib/pact_broker/domain/webhook_spec.rb +10 -6
  88. data/spec/lib/pact_broker/matrix/parse_query_spec.rb +15 -7
  89. data/spec/lib/pact_broker/pacts/service_spec.rb +13 -5
  90. data/spec/lib/pact_broker/verifications/service_spec.rb +15 -3
  91. data/spec/lib/pact_broker/versions/repository_spec.rb +1 -1
  92. data/spec/lib/pact_broker/webhooks/job_spec.rb +52 -15
  93. data/spec/lib/pact_broker/webhooks/render_spec.rb +28 -7
  94. data/spec/lib/pact_broker/webhooks/repository_spec.rb +23 -32
  95. data/spec/lib/pact_broker/webhooks/service_spec.rb +186 -62
  96. data/spec/lib/pact_broker/webhooks/trigger_service_spec.rb +6 -5
  97. data/spec/lib/pact_broker/webhooks/webhook_request_template_spec.rb +12 -5
  98. data/spec/lib/rack/pact_broker/database_transaction_spec.rb +10 -0
  99. data/spec/lib/rack/pact_broker/ui_request_filter_spec.rb +9 -0
  100. data/spec/migrations/23_pact_versions_spec.rb +7 -1
  101. data/spec/support/database_cleaner.rb +11 -1
  102. data/spec/support/metadata_test_server.rb +40 -0
  103. data/spec/support/verification_job.rb +34 -0
  104. data/spec/support/webhook_endpoint_middleware.rb +22 -0
  105. metadata +37 -2
@@ -19,6 +19,9 @@ module PactBroker
19
19
  .eager(:latest_triggered_webhooks)
20
20
  .eager(:webhooks)
21
21
 
22
+ rows = rows.consumer(options[:consumer_name]) if options[:consumer_name]
23
+ rows = rows.provider(options[:provider_name]) if options[:provider_name]
24
+
22
25
  if !options[:tags]
23
26
  rows = rows.where(consumer_version_tag_name: nil)
24
27
  else
@@ -0,0 +1,10 @@
1
+ require 'pact_broker/db'
2
+
3
+ module PactBroker
4
+ module Integrations
5
+ class Integration < Sequel::Model
6
+ associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
7
+ associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
8
+ end
9
+ end
10
+ end
@@ -1,6 +1,7 @@
1
1
  require 'pact_broker/services'
2
2
  require 'pact_broker/repositories'
3
3
  require 'pact_broker/logging'
4
+ require 'pact_broker/integrations/integration'
4
5
 
5
6
  module PactBroker
6
7
  module Integrations
@@ -9,6 +10,10 @@ module PactBroker
9
10
  extend PactBroker::Services
10
11
  include PactBroker::Logging
11
12
 
13
+ def self.find_all
14
+ PactBroker::Integrations::Integration.eager(:consumer).eager(:provider).all
15
+ end
16
+
12
17
  def self.delete(consumer_name, provider_name)
13
18
  consumer = pacticipant_service.find_pacticipant_by_name(consumer_name)
14
19
  provider = pacticipant_service.find_pacticipant_by_name(provider_name)
@@ -27,6 +27,8 @@ module PactBroker
27
27
  end
28
28
  if params.key?('limit') && params['limit'] != ''
29
29
  options[:limit] = params['limit']
30
+ else
31
+ options[:limit] = "100"
30
32
  end
31
33
  if params.key?('latest') && params['latest'] != ''
32
34
  options[:latest] = params['latest'] == 'true'
@@ -25,6 +25,14 @@ module PactBroker
25
25
  include PactBroker::Repositories::Helpers
26
26
  include PactBroker::Logging
27
27
 
28
+ def consumer consumer_name
29
+ where(name_like(:consumer_name, consumer_name))
30
+ end
31
+
32
+ def provider provider_name
33
+ where(name_like(:provider_name, provider_name))
34
+ end
35
+
28
36
  def matching_selectors selectors
29
37
  if selectors.size == 1
30
38
  where_consumer_or_provider_is(selectors.first)
@@ -16,9 +16,8 @@ module PactBroker
16
16
  QueryResultsWithDeploymentStatusSummary.new(query_results.rows, query_results.selectors, query_results.options, query_results.resolved_selectors, query_results.integrations, deployment_status_summary)
17
17
  end
18
18
 
19
- def find_for_consumer_and_provider params
19
+ def find_for_consumer_and_provider params, options = {}
20
20
  selectors = [{ pacticipant_name: params[:consumer_name] }, { pacticipant_name: params[:provider_name] }]
21
- options = { latestby: 'cvpv' }
22
21
  find(selectors, options)
23
22
  end
24
23
 
@@ -40,20 +40,20 @@ module PactBroker
40
40
  pact_repository.delete(params)
41
41
  end
42
42
 
43
- def create_or_update_pact params
43
+ def create_or_update_pact params, webhook_options
44
44
  provider = pacticipant_repository.find_by_name_or_create params[:provider_name]
45
45
  consumer = pacticipant_repository.find_by_name_or_create params[:consumer_name]
46
46
  consumer_version = version_repository.find_by_pacticipant_id_and_number_or_create consumer.id, params[:consumer_version_number]
47
47
  existing_pact = pact_repository.find_by_version_and_provider(consumer_version.id, provider.id)
48
48
 
49
49
  if existing_pact
50
- update_pact params, existing_pact
50
+ update_pact params, existing_pact, webhook_options
51
51
  else
52
- create_pact params, consumer_version, provider
52
+ create_pact params, consumer_version, provider, webhook_options
53
53
  end
54
54
  end
55
55
 
56
- def merge_pact params
56
+ def merge_pact params, webhook_options
57
57
  provider = pacticipant_repository.find_by_name_or_create params[:provider_name]
58
58
  consumer = pacticipant_repository.find_by_name_or_create params[:consumer_name]
59
59
  consumer_version = version_repository.find_by_pacticipant_id_and_number_or_create consumer.id, params[:consumer_version_number]
@@ -61,7 +61,7 @@ module PactBroker
61
61
 
62
62
  params.merge!(json_content: Merger.merge_pacts(existing_pact.json_content, params[:json_content]))
63
63
 
64
- update_pact params, existing_pact
64
+ update_pact params, existing_pact, webhook_options
65
65
  end
66
66
 
67
67
  def find_all_pact_versions_between consumer, options
@@ -113,7 +113,7 @@ module PactBroker
113
113
  private
114
114
 
115
115
  # Overwriting an existing pact with the same consumer/provider/consumer version number
116
- def update_pact params, existing_pact
116
+ def update_pact params, existing_pact, webhook_options
117
117
  logger.info "Updating existing pact publication with params #{params.reject{ |k, v| k == :json_content}}"
118
118
  logger.debug "Content #{params[:json_content]}"
119
119
  pact_version_sha = generate_sha(params[:json_content])
@@ -121,13 +121,13 @@ module PactBroker
121
121
  update_params = { pact_version_sha: pact_version_sha, json_content: json_content }
122
122
  updated_pact = pact_repository.update(existing_pact.id, update_params)
123
123
 
124
- webhook_trigger_service.trigger_webhooks_for_updated_pact(existing_pact, updated_pact)
124
+ webhook_trigger_service.trigger_webhooks_for_updated_pact(existing_pact, updated_pact, merge_consumer_version_info(webhook_options, updated_pact))
125
125
 
126
126
  updated_pact
127
127
  end
128
128
 
129
129
  # When no publication for the given consumer/provider/consumer version number exists
130
- def create_pact params, version, provider
130
+ def create_pact params, version, provider, webhook_options
131
131
  logger.info "Creating new pact publication with params #{params.reject{ |k, v| k == :json_content}}"
132
132
  logger.debug "Content #{params[:json_content]}"
133
133
  pact_version_sha = generate_sha(params[:json_content])
@@ -139,7 +139,7 @@ module PactBroker
139
139
  pact_version_sha: pact_version_sha,
140
140
  json_content: json_content
141
141
  )
142
- webhook_trigger_service.trigger_webhooks_for_new_pact pact
142
+ webhook_trigger_service.trigger_webhooks_for_new_pact(pact, merge_consumer_version_info(webhook_options, pact))
143
143
  pact
144
144
  end
145
145
 
@@ -150,6 +150,13 @@ module PactBroker
150
150
  def add_interaction_ids(json_content)
151
151
  Content.from_json(json_content).with_ids.to_json
152
152
  end
153
+
154
+ def merge_consumer_version_info(webhook_options, pact)
155
+ webhook_context = webhook_options.fetch(:webhook_context, {}).merge(
156
+ consumer_version_tags: pact.consumer_version_tag_names
157
+ )
158
+ webhook_options.merge(webhook_context: webhook_context)
159
+ end
153
160
  end
154
161
  end
155
162
  end
@@ -228,8 +228,8 @@ module PactBroker
228
228
  params[:events] || [{ name: PactBroker::Webhooks::WebhookEvent::DEFAULT_EVENT_NAME }]
229
229
  end
230
230
  events = event_params.collect{ |e| PactBroker::Webhooks::WebhookEvent.new(e) }
231
- default_params = { method: 'POST', url: 'http://example.org', headers: {'Content-Type' => 'application/json'}, username: params[:username], password: params[:password]}
232
- request = PactBroker::Webhooks::WebhookRequestTemplate.new(default_params.merge(params))
231
+ template_params = { method: 'POST', url: 'http://example.org', headers: {'Content-Type' => 'application/json'}, username: params[:username], password: params[:password]}
232
+ request = PactBroker::Webhooks::WebhookRequestTemplate.new(template_params.merge(params))
233
233
  @webhook = PactBroker::Webhooks::Repository.new.create uuid, PactBroker::Domain::Webhook.new(request: request, events: events), consumer, provider
234
234
  self
235
235
  end
@@ -242,6 +242,10 @@ module PactBroker
242
242
  create_webhook(parameters.merge(consumer: nil, provider: nil))
243
243
  end
244
244
 
245
+ def create_global_verification_webhook parameters = {}
246
+ create_webhook(parameters.merge(consumer: nil, provider: nil, event_names: [PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED]))
247
+ end
248
+
245
249
  def create_provider_webhook parameters = {}
246
250
  create_webhook(parameters.merge(consumer: nil))
247
251
  end
@@ -1,10 +1,8 @@
1
-
2
1
  require 'sequel'
3
2
 
4
3
  module PactBroker
5
4
  module Verifications
6
5
  class Sequence < Sequel::Model(:verification_sequence_number)
7
-
8
6
  dataset_module do
9
7
  # The easiest way to implement a cross database compatible sequence.
10
8
  # Sad, I know.
@@ -18,14 +18,22 @@ module PactBroker
18
18
  verification_repository.next_number
19
19
  end
20
20
 
21
- def create next_verification_number, params, pact
21
+ def create next_verification_number, params, pact, webhook_options
22
22
  logger.info "Creating verification #{next_verification_number} for pact_id=#{pact.id} from params #{params}"
23
23
  verification = PactBroker::Domain::Verification.new
24
24
  provider_version_number = params.fetch('providerApplicationVersion')
25
25
  PactBroker::Api::Decorators::VerificationDecorator.new(verification).from_hash(params)
26
26
  verification.number = next_verification_number
27
27
  verification = verification_repository.create(verification, provider_version_number, pact)
28
- webhook_service.trigger_webhooks pact, verification, PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED
28
+ webhook_context = webhook_options[:webhook_context].merge(
29
+ provider_version_tags: verification.provider_version_tag_names
30
+ )
31
+
32
+ webhook_service.trigger_webhooks(pact,
33
+ verification,
34
+ PactBroker::Webhooks::WebhookEvent::VERIFICATION_PUBLISHED,
35
+ webhook_options.merge(webhook_context: webhook_context)
36
+ )
29
37
  verification
30
38
  end
31
39
 
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.31.0'
2
+ VERSION = '2.32.0'
3
3
  end
@@ -0,0 +1,38 @@
1
+ require 'sequel'
2
+
3
+ module PactBroker
4
+ module Versions
5
+ class Sequence < Sequel::Model(:version_sequence_number)
6
+
7
+ dataset_module do
8
+ # The easiest way to implement a cross database compatible sequence.
9
+ # Sad, I know.
10
+ def next_val
11
+ db.transaction do
12
+ for_update.first
13
+ select_all.update(value: Sequel[:value]+1)
14
+ row = first
15
+ if row
16
+ row.value
17
+ else
18
+ # The first row should have been created in the migration, so this code
19
+ # should only ever be executed in a test context, after we've truncated all the
20
+ # tables after a test.
21
+ # There would be a risk of a race condition creating two rows if this
22
+ # code executed in prod, as I don't think you can lock an empty table
23
+ # to prevent another record being inserted.
24
+ max_version_order = PactBroker::Domain::Version.max(:order)
25
+ value = max_version_order ? max_version_order + 100 : 1
26
+ insert(value: value)
27
+ value
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ # Table: version_sequence_number
37
+ # Columns:
38
+ # value | integer | NOT NULL
@@ -18,14 +18,23 @@ module PactBroker
18
18
 
19
19
  private
20
20
 
21
- attr_reader :triggered_webhook, :error_count
21
+ attr_reader :triggered_webhook, :error_count, :data
22
22
 
23
23
  def perform_with_connection(data)
24
24
  @data = data
25
- @triggered_webhook = PactBroker::Webhooks::TriggeredWebhook.find(id: data[:triggered_webhook].id)
25
+ triggered_webhook_id = data[:triggered_webhook].id
26
+ @triggered_webhook = PactBroker::Webhooks::TriggeredWebhook.find(id: triggered_webhook_id)
27
+ if triggered_webhook
28
+ perform_with_triggered_webhook
29
+ else
30
+ logger.info "Could not find webhook with id: #{triggered_webhook_id}"
31
+ end
32
+ end
33
+
34
+ def perform_with_triggered_webhook
26
35
  @error_count = data[:error_count] || 0
27
36
  begin
28
- webhook_execution_result = PactBroker::Webhooks::Service.execute_triggered_webhook_now triggered_webhook, execution_options(data)
37
+ webhook_execution_result = PactBroker::Webhooks::Service.execute_triggered_webhook_now(triggered_webhook, webhook_options(data))
29
38
  if webhook_execution_result.success?
30
39
  handle_success
31
40
  else
@@ -36,11 +45,14 @@ module PactBroker
36
45
  end
37
46
  end
38
47
 
39
- def execution_options(data)
40
- {
48
+ def webhook_options(data)
49
+ execution_options = data[:execution_options].merge(
41
50
  success_log_message: "Successfully executed webhook",
42
- failure_log_message: failure_log_message,
43
- base_url: data.fetch(:base_url)
51
+ failure_log_message: failure_log_message
52
+ )
53
+ {
54
+ execution_options: execution_options,
55
+ webhook_context: data[:webhook_context]
44
56
  }
45
57
  end
46
58
 
@@ -4,15 +4,16 @@ module PactBroker
4
4
 
5
5
  TEMPLATE_PARAMETER_REGEXP = /\$\{pactbroker\.[^\}]+\}/
6
6
 
7
- def self.call(template, pact, trigger_verification, base_url, &escaper)
7
+ def self.call(template, pact, trigger_verification, webhook_context, &escaper)
8
+ base_url = webhook_context[:base_url]
8
9
  verification = trigger_verification || (pact && pact.latest_verification)
9
10
  params = {
10
- '${pactbroker.pactUrl}' => pact ? PactBroker::Api::PactBrokerUrls.pact_url(base_url, pact) : "",
11
+ '${pactbroker.pactUrl}' => pact ? PactBroker::Api::PactBrokerUrls.pact_version_url_with_metadata(pact, base_url) : "",
11
12
  '${pactbroker.verificationResultUrl}' => verification_url(verification, base_url),
12
- '${pactbroker.consumerVersionNumber}' => pact ? pact.consumer_version_number : "",
13
+ '${pactbroker.consumerVersionNumber}' => consumer_version_number(pact, webhook_context),
13
14
  '${pactbroker.providerVersionNumber}' => verification ? verification.provider_version_number : "",
14
- '${pactbroker.providerVersionTags}' => provider_version_tags(verification),
15
- '${pactbroker.consumerVersionTags}' => consumer_version_tags(pact),
15
+ '${pactbroker.providerVersionTags}' => provider_version_tags(verification, webhook_context),
16
+ '${pactbroker.consumerVersionTags}' => consumer_version_tags(pact, webhook_context),
16
17
  '${pactbroker.consumerName}' => pact ? pact.consumer_name : "",
17
18
  '${pactbroker.providerName}' => pact ? pact.provider_name : "",
18
19
  '${pactbroker.githubVerificationStatus}' => github_verification_status(verification),
@@ -47,19 +48,35 @@ module PactBroker
47
48
  end
48
49
  end
49
50
 
50
- def self.consumer_version_tags pact
51
- if pact
52
- pact.consumer_version.tags.collect(&:name).join(", ")
51
+ def self.consumer_version_number(pact, webhook_context)
52
+ if webhook_context[:consumer_version_number]
53
+ webhook_context[:consumer_version_number]
53
54
  else
54
- ""
55
+ pact ? pact.consumer_version_number : ""
55
56
  end
56
57
  end
57
58
 
58
- def self.provider_version_tags verification
59
- if verification
60
- verification.provider_version.tags.collect(&:name).join(", ")
59
+ def self.consumer_version_tags pact, webhook_context
60
+ if webhook_context[:consumer_version_tags]
61
+ webhook_context[:consumer_version_tags].join(", ")
61
62
  else
62
- ""
63
+ if pact
64
+ pact.consumer_version.tags.collect(&:name).join(", ")
65
+ else
66
+ ""
67
+ end
68
+ end
69
+ end
70
+
71
+ def self.provider_version_tags verification, webhook_context
72
+ if webhook_context[:provider_version_tags]
73
+ webhook_context[:provider_version_tags].join(", ")
74
+ else
75
+ if verification
76
+ verification.provider_version.tags.collect(&:name).join(", ")
77
+ else
78
+ ""
79
+ end
63
80
  end
64
81
  end
65
82
 
@@ -34,6 +34,8 @@ module PactBroker
34
34
 
35
35
  def update_by_uuid uuid, webhook
36
36
  existing_webhook = Webhook.find(uuid: uuid)
37
+ existing_webhook.consumer_id = pacticipant_repository.find_by_name(webhook.consumer.name).id if webhook.consumer
38
+ existing_webhook.provider_id = pacticipant_repository.find_by_name(webhook.provider.name).id if webhook.provider
37
39
  existing_webhook.update_from_domain(webhook).save
38
40
  existing_webhook.headers.collect(&:delete)
39
41
  existing_webhook.events.collect(&:delete)
@@ -82,10 +84,6 @@ module PactBroker
82
84
  Webhook.where(consumer: consumer, provider: provider).destroy
83
85
  end
84
86
 
85
- def find_for_pact_and_event_name pact, event_name
86
- find_by_consumer_and_or_provider_and_event_name(pact.consumer, pact.provider, event_name)
87
- end
88
-
89
87
  def find_by_consumer_and_or_provider_and_event_name consumer, provider, event_name
90
88
  find_by_consumer_and_provider_and_event_name(consumer, provider, event_name) +
91
89
  find_by_consumer_and_provider_and_event_name(nil, provider, event_name) +
@@ -100,6 +98,7 @@ module PactBroker
100
98
  }
101
99
  Webhook
102
100
  .select_all_qualified
101
+ .enabled
103
102
  .where(criteria)
104
103
  .join(:webhook_events, { webhook_id: :id })
105
104
  .where(Sequel[:webhook_events][:name] => event_name)
@@ -9,6 +9,7 @@ require 'pact_broker/webhooks/status'
9
9
  require 'pact_broker/webhooks/webhook_event'
10
10
  require 'pact_broker/verifications/placeholder_verification'
11
11
  require 'pact_broker/pacts/placeholder_pact'
12
+ require 'pact_broker/api/decorators/webhook_decorator'
12
13
 
13
14
  module PactBroker
14
15
 
@@ -40,7 +41,10 @@ module PactBroker
40
41
  webhook_repository.find_by_uuid uuid
41
42
  end
42
43
 
43
- def self.update_by_uuid uuid, webhook
44
+ def self.update_by_uuid uuid, params
45
+ webhook = webhook_repository.find_by_uuid(uuid)
46
+ maintain_redacted_params(webhook, params)
47
+ PactBroker::Api::Decorators::WebhookDecorator.new(webhook).from_hash(params)
44
48
  webhook_repository.update_by_uuid uuid, webhook
45
49
  end
46
50
 
@@ -63,31 +67,35 @@ module PactBroker
63
67
  webhook_repository.find_all
64
68
  end
65
69
 
66
- def self.test_execution webhook
67
- options = { failure_log_message: "Webhook execution failed", show_response: PactBroker.configuration.show_webhook_response?, base_url: base_url}
70
+ def self.test_execution webhook, options
71
+ execution_options = options[:execution_options].merge(
72
+ failure_log_message: "Webhook execution failed",
73
+ )
74
+ merged_options = options.merge(execution_options: execution_options)
68
75
  verification = nil
69
76
  if webhook.trigger_on_provider_verification_published?
70
77
  verification = verification_service.search_for_latest(webhook.consumer_name, webhook.provider_name) || PactBroker::Verifications::PlaceholderVerification.new
71
78
  end
72
79
 
73
80
  pact = pact_service.search_for_latest_pact(consumer_name: webhook.consumer_name, provider_name: webhook.provider_name) || PactBroker::Pacts::PlaceholderPact.new
74
- webhook.execute(pact, verification, options)
75
- end
76
-
77
- def self.execute_webhook_now webhook, pact, verification = nil
78
- triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, verification, USER)
79
- options = { failure_log_message: "Webhook execution failed"}
80
- webhook_execution_result = execute_triggered_webhook_now triggered_webhook, options
81
- if webhook_execution_result.success?
82
- webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_SUCCESS
83
- else
84
- webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_FAILURE
85
- end
86
- webhook_execution_result
87
- end
88
-
89
- def self.execute_triggered_webhook_now triggered_webhook, options
90
- webhook_execution_result = triggered_webhook.execute options.merge(show_response: PactBroker.configuration.show_webhook_response?)
81
+ webhook.execute(pact, verification, merged_options)
82
+ end
83
+
84
+ # # TODO delete?
85
+ # def self.execute_webhook_now webhook, pact, verification = nil
86
+ # triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, verification, USER)
87
+ # execution_options = { failure_log_message: "Webhook execution failed"}
88
+ # webhook_execution_result = execute_triggered_webhook_now triggered_webhook, execution_options
89
+ # if webhook_execution_result.success?
90
+ # webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_SUCCESS
91
+ # else
92
+ # webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_FAILURE
93
+ # end
94
+ # webhook_execution_result
95
+ # end
96
+
97
+ def self.execute_triggered_webhook_now triggered_webhook, webhook_options
98
+ webhook_execution_result = triggered_webhook.execute webhook_options
91
99
  webhook_repository.create_execution triggered_webhook, webhook_execution_result
92
100
  webhook_execution_result
93
101
  end
@@ -108,17 +116,17 @@ module PactBroker
108
116
  webhook_repository.find_by_consumer_and_provider consumer, provider
109
117
  end
110
118
 
111
- def self.trigger_webhooks pact, verification, event_name
119
+ def self.trigger_webhooks pact, verification, event_name, options
112
120
  webhooks = webhook_repository.find_by_consumer_and_or_provider_and_event_name pact.consumer, pact.provider, event_name
113
121
 
114
122
  if webhooks.any?
115
- run_later(webhooks, pact, verification, event_name)
123
+ run_later(webhooks, pact, verification, event_name, options)
116
124
  else
117
- logger.debug "No webhook found for consumer \"#{pact.consumer.name}\" and provider \"#{pact.provider.name}\""
125
+ logger.debug "No enabled webhooks found for consumer \"#{pact.consumer.name}\" and provider \"#{pact.provider.name}\" and event #{event_name}"
118
126
  end
119
127
  end
120
128
 
121
- def self.run_later webhooks, pact, verification, event_name
129
+ def self.run_later webhooks, pact, verification, event_name, options
122
130
  trigger_uuid = next_uuid
123
131
  webhooks.each do | webhook |
124
132
  begin
@@ -126,8 +134,9 @@ module PactBroker
126
134
  logger.info "Scheduling job for #{webhook.description} with uuid #{webhook.uuid}"
127
135
  job_data = {
128
136
  triggered_webhook: triggered_webhook,
129
- database_connector: job_database_connector,
130
- base_url: base_url
137
+ webhook_context: options.fetch(:webhook_context),
138
+ execution_options: options.fetch(:execution_options),
139
+ database_connector: options.fetch(:database_connector)
131
140
  }
132
141
  # Delay slightly to make sure the request transaction has finished before we execute the webhook
133
142
  Job.perform_in(5, job_data)
@@ -137,14 +146,6 @@ module PactBroker
137
146
  end
138
147
  end
139
148
 
140
- def self.job_database_connector
141
- Thread.current[:pact_broker_thread_data].database_connector
142
- end
143
-
144
- def self.base_url
145
- Thread.current[:pact_broker_thread_data].base_url
146
- end
147
-
148
149
  def self.find_latest_triggered_webhooks_for_pact pact
149
150
  webhook_repository.find_latest_triggered_webhooks_for_pact pact
150
151
  end
@@ -164,6 +165,31 @@ module PactBroker
164
165
  def self.find_triggered_webhooks_for_verification verification
165
166
  webhook_repository.find_triggered_webhooks_for_verification(verification)
166
167
  end
168
+
169
+ private
170
+
171
+ # Dirty hack to maintain existing password or Authorization header if it is submitted with value ****
172
+ # This is required because the password and Authorization header is **** out in the API response
173
+ # for security purposes, so it would need to be re-entered with every response.
174
+ # TODO implement proper 'secrets' management.
175
+ def self.maintain_redacted_params(webhook, params)
176
+ if webhook.request.password && password_key_does_not_exist_or_is_starred?(params)
177
+ params['request']['password'] = webhook.request.password
178
+ end
179
+
180
+ new_headers = params['request']['headers'] ||= {}
181
+ existing_headers = webhook.request.headers
182
+ starred_new_headers = new_headers.select { |key, value| value =~ /^\**$/ }
183
+ starred_new_headers.each do | (key, value) |
184
+ new_headers[key] = existing_headers[key]
185
+ end
186
+ params['request']['headers'] = new_headers
187
+ params
188
+ end
189
+
190
+ def self.password_key_does_not_exist_or_is_starred?(params)
191
+ !params['request'].key?('password') || params.dig('request','password') =~ /^\**$/
192
+ end
167
193
  end
168
194
  end
169
195
  end