pact_broker 2.46.0 → 2.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4afa0b6a3444f1fdf6ea852bed8016f63cccd1a5373aa0903be0d206f528bc6
4
- data.tar.gz: cd86a0d032bb39ffe2f4318116cc9e51886fc3d3e84bf8c537d6bae5ee0f9d52
3
+ metadata.gz: c54fb8ff19038af9dd5d0a09105244459a18febe23333bf5203ef77424eb97a3
4
+ data.tar.gz: f380f9f5e9939d9844e88f09b5807a5e1f094cc368adfa22c104ee8ad55d9f97
5
5
  SHA512:
6
- metadata.gz: 217a6da9c4b27d6959695eefa1c6b8c13526c827222d114cb6527d20fc9fceec194498ffa84ee6f10e574f96a353429bfdacf18f6b6494826e12aea5d58fc7ba
7
- data.tar.gz: 1632aac1111449205a45ca9367093d2e4557e75a163a8c516248486f7a7e884cbf9205302ba8c5e1f4c01c8ff604bbebdaeef6729a1bc03e9dd97ec7f0afa850
6
+ metadata.gz: ebfc52497d4d877c0adb715bd1bf39c1be9513893ffaf032e142d160b4aa12be04c5b01d0c698f7b94ea6aadca1a416cddd53e3a0f304bb9c2821d128cf894a2
7
+ data.tar.gz: 41184e2249f9bde504a6f9258bf44ff2f33a8374d6a2d05ebe59536c8113461c79f41319a9b337f1db58c83e31ff652951ffca7466988626150dab7e09002abb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ <a name="v2.47.0"></a>
2
+ ### v2.47.0 (2020-01-31)
3
+
4
+
5
+ #### Features
6
+
7
+ * include the webhook context in the execution logs ([29f65bef](/../../commit/29f65bef))
8
+
9
+
1
10
  <a name="v2.46.0"></a>
2
11
  ### v2.46.0 (2020-01-30)
3
12
 
data/Dockerfile CHANGED
@@ -12,6 +12,7 @@ RUN apk update \
12
12
  "sqlite>=3.28" \
13
13
  "tzdata>=2019" \
14
14
  "mariadb-dev>=10.3" \
15
+ "git" \
15
16
  && rm -rf /var/cache/apk/*
16
17
 
17
18
  WORKDIR /app
@@ -40,7 +40,10 @@ module PactBroker
40
40
  provider_name,
41
41
  parsed_query_params.provider_version_tags,
42
42
  parsed_query_params.consumer_version_selectors,
43
- { include_wip_pacts_since: parsed_query_params.include_wip_pacts_since }
43
+ {
44
+ include_wip_pacts_since: parsed_query_params.include_wip_pacts_since,
45
+ include_pending_status: parsed_query_params.include_pending_status
46
+ }
44
47
  )
45
48
  end
46
49
 
@@ -33,11 +33,11 @@ module PactBroker
33
33
  end
34
34
 
35
35
  def to_json
36
- decorator_for(verification).to_json(user_options: {base_url: base_url})
36
+ decorator_for(verification).to_json(user_options: { base_url: base_url })
37
37
  end
38
38
 
39
39
  def to_extended_json
40
- extended_decorator_for(verification).to_json(user_options: {base_url: base_url})
40
+ extended_decorator_for(verification).to_json(user_options: { base_url: base_url })
41
41
  end
42
42
 
43
43
  private
@@ -49,7 +49,7 @@ module PactBroker
49
49
 
50
50
  def from_json
51
51
  verification = verification_service.create(next_verification_number, params_with_string_keys, pact, webhook_options)
52
- response.body = decorator_for(verification).to_json(user_options: {base_url: base_url})
52
+ response.body = decorator_for(verification).to_json(user_options: { base_url: base_url })
53
53
  true
54
54
  end
55
55
 
@@ -53,14 +53,14 @@ module PactBroker
53
53
  end
54
54
 
55
55
  def execute pact, verification, options
56
- logger.info "Executing #{self}"
56
+ logger.info "Executing #{self} webhook_context=#{options.fetch(:webhook_context)}"
57
57
  webhook_request = request.build(template_parameters(pact, verification, options))
58
58
  http_response, error = execute_request(webhook_request)
59
59
 
60
60
  PactBroker::Webhooks::WebhookExecutionResult.new(
61
61
  webhook_request.http_request,
62
62
  http_response,
63
- generate_logs(webhook_request, http_response, error, options.fetch(:logging_options)),
63
+ generate_logs(webhook_request, http_response, error, options[:webhook_context], options.fetch(:logging_options)),
64
64
  error
65
65
  )
66
66
  end
@@ -110,13 +110,14 @@ module PactBroker
110
110
  PactBroker::Webhooks::PactAndVerificationParameters.new(pact, verification, options.fetch(:webhook_context)).to_hash
111
111
  end
112
112
 
113
- def generate_logs(webhook_request, http_response, error, logging_options)
113
+ def generate_logs(webhook_request, http_response, error, webhook_context, logging_options)
114
114
  webhook_request_logger = PactBroker::Webhooks::WebhookRequestLogger.new(logging_options)
115
115
  webhook_request_logger.log(
116
116
  uuid,
117
117
  webhook_request,
118
118
  http_response,
119
- error
119
+ error,
120
+ webhook_context
120
121
  )
121
122
  end
122
123
  end
@@ -14,6 +14,8 @@ require 'pact_broker/matrix/head_row'
14
14
  require 'pact_broker/pacts/latest_pact_publication_id_for_consumer_version'
15
15
  require 'pact_broker/pacts/verifiable_pact'
16
16
  require 'pact_broker/repositories/helpers'
17
+ require 'pact_broker/pacts/selected_pact'
18
+ require 'pact_broker/pacts/selector'
17
19
 
18
20
  module PactBroker
19
21
  module Pacts
@@ -127,12 +129,13 @@ module PactBroker
127
129
  end
128
130
  end
129
131
 
130
- def find_all_pact_versions_for_provider_with_tags provider_name, consumer_version_tag_names
132
+ def find_all_pact_versions_for_provider_with_consumer_version_tags provider_name, consumer_version_tag_names
131
133
  provider = pacticipant_repository.find_by_name(provider_name)
132
134
 
133
135
  PactPublication
134
136
  .select_all_qualified
135
137
  .select_append(Sequel[:cv][:order].as(:consumer_version_order))
138
+ .select_append(Sequel[:ct][:name].as(:consumer_version_tag_name))
136
139
  .remove_overridden_revisions
137
140
  .join_consumer_versions(:cv)
138
141
  .join_consumer_version_tags_with_names(consumer_version_tag_names)
@@ -144,8 +147,12 @@ module PactBroker
144
147
  .all
145
148
  .group_by(&:pact_version_id)
146
149
  .values
147
- .collect{ | pacts| pacts.sort_by{|pact| pact.values.fetch(:consumer_version_order) }.last }
148
- .collect(&:to_domain)
150
+ .collect do | pact_publications |
151
+ selector_tag_names = pact_publications.collect{ | p| p.values.fetch(:consumer_version_tag_name) }
152
+ latest_pact_publication = pact_publications.sort_by{ |p| p.values.fetch(:consumer_version_order) }.last
153
+ selectors = selector_tag_names.collect{ | tag_name | Selector.one_of_tag(tag_name) }
154
+ SelectedPact.new(latest_pact_publication.to_domain, selectors)
155
+ end
149
156
  end
150
157
 
151
158
  # To find the work in progress pacts for this verification execution:
@@ -339,29 +346,67 @@ module PactBroker
339
346
 
340
347
  # Returns a list of Domain::Pact objects the represent pact publications
341
348
  def find_for_verification(provider_name, consumer_version_selectors)
342
- find_pacts_for_which_the_latest_version_or_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors) +
349
+ selected_pacts = find_pacts_for_which_the_latest_version_is_required(provider_name, consumer_version_selectors) +
350
+ find_pacts_for_which_the_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors) +
343
351
  find_pacts_for_which_all_versions_for_the_tag_are_required(provider_name, consumer_version_selectors)
352
+ selected_pacts
353
+ .group_by(&:pact_version_sha)
354
+ .values
355
+ .collect do | selected_pacts_for_pact_version_id |
356
+ SelectedPact.merge(selected_pacts_for_pact_version_id)
357
+ end
344
358
  end
345
359
 
346
360
  private
347
361
 
348
- def find_pacts_for_which_the_latest_version_or_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors)
362
+ def find_pacts_for_which_the_latest_version_is_required(provider_name, consumer_version_selectors)
363
+ if consumer_version_selectors.empty?
364
+ LatestPactPublications
365
+ .provider(provider_name)
366
+ .order_ignore_case(:consumer_name)
367
+ .collect do | latest_pact_publication |
368
+ pact_publication = PactPublication.find(id: latest_pact_publication.id)
369
+ SelectedPact.new(pact_publication.to_domain, [Selector.overall_latest])
370
+ end
371
+ else
372
+ []
373
+ end
374
+ end
375
+
376
+ def find_pacts_for_which_the_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors)
349
377
  # The tags for which only the latest version is specified
350
- latest_tags = consumer_version_selectors.any? ?
351
- consumer_version_selectors.select(&:latest).collect(&:tag) :
352
- nil
378
+ latest_tags = consumer_version_selectors.select(&:latest).collect(&:tag).compact.uniq
353
379
 
354
- find_latest_pact_versions_for_provider(provider_name, latest_tags)
380
+ # TODO make this an efficient query!
381
+ # These are not yet de-duped. Should make the behaviour consistent between this and find_pacts_for_which_all_versions_for_the_tag_are_required ?
382
+ if latest_tags.any?
383
+ LatestTaggedPactPublications
384
+ .provider(provider_name)
385
+ .order_ignore_case(:consumer_name)
386
+ .where(tag_name: latest_tags)
387
+ .all
388
+ .group_by(&:pact_version_id)
389
+ .values
390
+ .collect do | pact_publications |
391
+ selector_tag_names = pact_publications.collect(&:tag_name)
392
+ last_pact_publication = pact_publications.sort_by(&:consumer_version_order).last
393
+ pact_publication = PactPublication.find(id: last_pact_publication.id)
394
+ SelectedPact.new(
395
+ pact_publication.to_domain,
396
+ selector_tag_names.collect{ | tag_name| Selector.latest_for_tag(tag_name) }
397
+ )
398
+ end
399
+ else
400
+ []
401
+ end
355
402
  end
356
403
 
357
404
  def find_pacts_for_which_all_versions_for_the_tag_are_required(provider_name, consumer_version_selectors)
358
405
  # The tags for which all versions are specified
359
- all_tags = consumer_version_selectors.any? ?
360
- consumer_version_selectors.reject(&:latest).collect(&:tag) :
361
- nil
406
+ all_tags = consumer_version_selectors.reject(&:latest).collect(&:tag)
362
407
 
363
- if all_tags
364
- find_all_pact_versions_for_provider_with_tags(provider_name, all_tags)
408
+ if all_tags.any?
409
+ find_all_pact_versions_for_provider_with_consumer_version_tags(provider_name, all_tags)
365
410
  else
366
411
  []
367
412
  end
@@ -0,0 +1,46 @@
1
+ require 'delegate'
2
+
3
+ module PactBroker
4
+ module Pacts
5
+ class SelectedPact < SimpleDelegator
6
+ attr_reader :pact, :selectors
7
+
8
+ def initialize(pact, selectors)
9
+ super(pact)
10
+ @pact = pact
11
+ @selectors = selectors
12
+ end
13
+
14
+ # might actually be, but this code doesn't know it.
15
+ def overall_latest?
16
+ selectors.any?(&:overall_latest?)
17
+ end
18
+
19
+ def latest_for_tag?
20
+ selectors.any?(&:latest_for_tag?)
21
+ end
22
+
23
+ def self.merge(selected_pacts)
24
+ latest_selected_pact = selected_pacts.sort_by(&:consumer_version_order).last
25
+ selectors = selected_pacts.collect(&:selectors).flatten.uniq
26
+ SelectedPact.new(latest_selected_pact.pact, selectors)
27
+ end
28
+
29
+ def merge(other)
30
+ if pact_version_sha != other.pact_version_sha
31
+ raise "These two pacts do not have the same pact_version_sha. They cannot be merged. #{pact_version_sha} and #{other.pact_version_sha}"
32
+ else
33
+ SelectedPact.new()
34
+ end
35
+ end
36
+
37
+ def tag_names_for_selectors_for_latest_pacts
38
+ selectors.select(&:latest_for_tag?).collect(&:tag)
39
+ end
40
+
41
+ def consumer_version_order
42
+ pact.consumer_version.order
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ module PactBroker
2
+ module Pacts
3
+ class Selector < Hash
4
+ def initialize(options)
5
+ merge!(options)
6
+ end
7
+
8
+ def self.overall_latest
9
+ Selector.new(latest: true)
10
+ end
11
+
12
+ def self.latest_for_tag(tag)
13
+ Selector.new(latest: true, tag: tag)
14
+ end
15
+
16
+ def self.one_of_tag(tag)
17
+ Selector.new(tag: tag)
18
+ end
19
+
20
+ def tag
21
+ self[:tag]
22
+ end
23
+
24
+ def overall_latest?
25
+ !!(latest && !tag)
26
+ end
27
+
28
+ def latest_for_tag?
29
+ !!(latest && tag)
30
+ end
31
+
32
+ private
33
+
34
+ def latest?
35
+ self[:latest]
36
+ end
37
+
38
+ def latest
39
+ self[:latest]
40
+ end
41
+ end
42
+ end
43
+ end
@@ -118,10 +118,8 @@ module PactBroker
118
118
  def find_for_verification(provider_name, provider_version_tags, consumer_version_selectors, options)
119
119
  verifiable_pacts_specified_in_request = pact_repository
120
120
  .find_for_verification(provider_name, consumer_version_selectors)
121
- .group_by(&:pact_version_sha)
122
- .values
123
- .collect do | head_pacts |
124
- squash_pacts_for_verification(provider_version_tags, head_pacts)
121
+ .collect do | selected_pact |
122
+ squash_pacts_for_verification(provider_version_tags, selected_pact, options[:include_pending_status])
125
123
  end
126
124
 
127
125
  verifiable_wip_pacts = if options[:include_wip_pacts_since]
@@ -5,28 +5,38 @@
5
5
  module PactBroker
6
6
  module Pacts
7
7
  module SquashPactsForVerification
8
- def self.call(provider_version_tags, head_pacts)
9
- domain_pact = head_pacts.first.pact
10
- pending_provider_tags = []
11
- pending = nil
12
- if provider_version_tags.any?
13
- pending_provider_tags = domain_pact.select_pending_provider_version_tags(provider_version_tags)
14
- pending = pending_provider_tags.any?
8
+ def self.call(provider_version_tags, selected_pact, include_pending_status = false)
9
+ domain_pact = selected_pact.pact
10
+
11
+ if include_pending_status
12
+ pending_provider_tags = []
13
+ non_pending_provider_tags = []
14
+ pending = nil
15
+ if provider_version_tags.any?
16
+ pending_provider_tags = domain_pact.select_pending_provider_version_tags(provider_version_tags)
17
+ pending = pending_provider_tags.any?
18
+ else
19
+ pending = domain_pact.pending?
20
+ end
21
+ non_pending_provider_tags = provider_version_tags - pending_provider_tags
22
+ VerifiablePact.new(
23
+ domain_pact,
24
+ pending,
25
+ pending_provider_tags,
26
+ non_pending_provider_tags,
27
+ selected_pact.tag_names_for_selectors_for_latest_pacts,
28
+ selected_pact.overall_latest?
29
+ )
15
30
  else
16
- pending = domain_pact.pending?
31
+ VerifiablePact.new(
32
+ domain_pact,
33
+ nil,
34
+ [],
35
+ [],
36
+ selected_pact.tag_names_for_selectors_for_latest_pacts,
37
+ selected_pact.overall_latest?
38
+ )
17
39
  end
18
-
19
- non_pending_provider_tags = provider_version_tags - pending_provider_tags
20
-
21
- head_consumer_tags = head_pacts.collect(&:tag)
22
- overall_latest = head_consumer_tags.include?(nil)
23
- VerifiablePact.new(domain_pact,
24
- pending,
25
- pending_provider_tags,
26
- non_pending_provider_tags,
27
- head_consumer_tags.compact,
28
- overall_latest
29
- )
30
40
  end
31
41
 
32
42
  def squash_pacts_for_verification(*args)
@@ -6,13 +6,23 @@ module PactBroker
6
6
  READ_MORE_PENDING = "Read more at https://pact.io/pending"
7
7
  READ_MORE_WIP = "Read more at https://pact.io/wip"
8
8
 
9
- delegate [:consumer_name, :provider_name, :head_consumer_tags, :pending_provider_tags, :non_pending_provider_tags, :pending?, :wip?] => :verifiable_pact
9
+ delegate [:consumer_name, :provider_name, :consumer_version_number, :head_consumer_tags, :pending_provider_tags, :non_pending_provider_tags, :pending?, :wip?] => :verifiable_pact
10
10
 
11
11
  def initialize(verifiable_pact, pact_version_url)
12
12
  @verifiable_pact = verifiable_pact
13
13
  @pact_version_url = pact_version_url
14
14
  end
15
15
 
16
+ def pact_description
17
+ position_descs = if head_consumer_tags.empty?
18
+ ["latest"]
19
+ else
20
+ head_consumer_tags.collect { | tag | "latest #{tag}"}
21
+ end
22
+
23
+ "Pact between #{consumer_name} and #{provider_name}, consumer version #{consumer_version_number}, #{position_descs.join(",")}"
24
+ end
25
+
16
26
  def inclusion_reason
17
27
  version_text = head_consumer_tags.size == 1 ? "version" : "versions"
18
28
  if wip?
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.46.0'
2
+ VERSION = '2.47.0'
3
3
  end
@@ -110,7 +110,8 @@ module PactBroker
110
110
  webhooks = webhook_repository.find_by_consumer_and_or_provider_and_event_name pact.consumer, pact.provider, event_name
111
111
 
112
112
  if webhooks.any?
113
- run_later(webhooks, pact, verification, event_name, options)
113
+ webhook_execution_configuration = options.fetch(:webhook_execution_configuration).with_webhook_context(event_name: event_name)
114
+ run_later(webhooks, pact, verification, event_name, options.merge(webhook_execution_configuration: webhook_execution_configuration))
114
115
  else
115
116
  logger.info "No enabled webhooks found for consumer \"#{pact.consumer.name}\" and provider \"#{pact.provider.name}\" and event #{event_name}"
116
117
  end
@@ -122,6 +123,7 @@ module PactBroker
122
123
  begin
123
124
  triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, verification, RESOURCE_CREATION)
124
125
  logger.info "Scheduling job for webhook with uuid #{webhook.uuid}"
126
+ logger.debug "Schedule webhook with options #{options}"
125
127
  job_data = { triggered_webhook: triggered_webhook }.deep_merge(options)
126
128
  # Delay slightly to make sure the request transaction has finished before we execute the webhook
127
129
  Job.perform_in(5, job_data)
@@ -29,8 +29,9 @@ module PactBroker
29
29
  @options = options
30
30
  end
31
31
 
32
- def log(uuid, webhook_request, http_response, error)
32
+ def log(uuid, webhook_request, http_response, error, webhook_context)
33
33
  safe_response = http_response ? HttpResponseWithUtf8SafeBody.new(http_response) : nil
34
+ log_webhook_context(webhook_context)
34
35
  log_request(webhook_request)
35
36
  log_response(uuid, safe_response) if safe_response
36
37
  log_error(uuid, error) if error
@@ -42,6 +43,11 @@ module PactBroker
42
43
 
43
44
  attr_reader :log_stream
44
45
 
46
+ def log_webhook_context(webhook_context)
47
+ execution_logger.debug "Webhook context #{webhook_context.to_json}"
48
+ logger.debug("Webhook context #{webhook_context.to_json}")
49
+ end
50
+
45
51
  def log_request(webhook_request)
46
52
  http_request = HttpRequestWithRedactedHeaders.new(webhook_request.http_request)
47
53
  logger.info "Making webhook #{webhook_request.uuid} request #{http_request.method.upcase} URI=#{webhook_request.url} (headers and body in debug logs)"
data/pact_broker.gemspec CHANGED
@@ -74,7 +74,7 @@ Gem::Specification.new do |gem|
74
74
  gem.add_development_dependency 'webmock', '~>2.3'
75
75
  gem.add_development_dependency 'rspec', '~>3.0'
76
76
  gem.add_development_dependency 'rspec-its', '~>1.2'
77
- gem.add_development_dependency 'database_cleaner', '~>1.6'
77
+ gem.add_development_dependency 'database_cleaner', '~>1.8', '>= 1.8.1'
78
78
  gem.add_development_dependency 'pg', '~>0.21'
79
79
  gem.add_development_dependency 'conventional-changelog', '~>1.3'
80
80
  gem.add_development_dependency 'bump', '~> 0.5'
@@ -18,7 +18,7 @@ module PactBroker
18
18
  {
19
19
  provider_version_tags: ['master'],
20
20
  consumer_version_selectors: [ { tag: 'dev', latest: 'true' }],
21
- include_pending_status: false,
21
+ include_pending_status: 'true',
22
22
  include_wip_pacts_since: '2018-01-01'
23
23
  }
24
24
  end
@@ -32,7 +32,11 @@ module PactBroker
32
32
  "Bar",
33
33
  ["master"],
34
34
  [OpenStruct.new(tag: "dev", latest: true)],
35
- { include_wip_pacts_since: DateTime.parse('2018-01-01') })
35
+ {
36
+ include_wip_pacts_since: DateTime.parse('2018-01-01'),
37
+ include_pending_status: true
38
+ }
39
+ )
36
40
  subject
37
41
  end
38
42
 
@@ -54,8 +58,8 @@ module PactBroker
54
58
  {
55
59
  providerVersionTags: ['master'],
56
60
  consumerVersionSelectors: [ { tag: 'dev', latest: true }],
57
- includePendingStatus: false,
58
- includeWipPactsSince: '2018-01-01'
61
+ includePendingStatus: true,
62
+ includeWipPactsSince: '2018-01-01',
59
63
  }
60
64
  end
61
65
 
@@ -74,7 +78,11 @@ module PactBroker
74
78
  "Bar",
75
79
  ["master"],
76
80
  [OpenStruct.new(tag: "dev", latest: true)],
77
- { include_wip_pacts_since: DateTime.parse('2018-01-01') })
81
+ {
82
+ include_wip_pacts_since: DateTime.parse('2018-01-01'),
83
+ include_pending_status: true
84
+ }
85
+ )
78
86
  subject
79
87
  end
80
88
 
@@ -94,7 +102,7 @@ module PactBroker
94
102
  it "uses the correct options for the decorator" do
95
103
  expect(decorator).to receive(:to_json) do | options |
96
104
  expect(options[:user_options][:title]).to eq "Pacts to be verified by provider Bar"
97
- expect(options[:user_options][:include_pending_status]).to eq false
105
+ expect(options[:user_options][:include_pending_status]).to eq true
98
106
  end
99
107
  subject
100
108
  end
@@ -81,7 +81,7 @@ module PactBroker
81
81
  end
82
82
 
83
83
  it "generates the execution logs" do
84
- expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, http_response, nil)
84
+ expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, http_response, nil, webhook_context)
85
85
  execute
86
86
  end
87
87
 
@@ -106,7 +106,7 @@ module PactBroker
106
106
  end
107
107
 
108
108
  it "generates the execution logs" do
109
- expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, nil, instance_of(error_class))
109
+ expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, nil, instance_of(error_class), webhook_context)
110
110
  execute
111
111
  end
112
112
 
@@ -15,32 +15,48 @@ module PactBroker
15
15
  subject.find{ |pact| pact.consumer_name == consumer_name && pact.consumer_version_number == consumer_version_number }
16
16
  end
17
17
 
18
- before do
19
- td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
20
- .create_consumer_version_tag("prod")
21
- .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
22
- .comment("next pact not selected")
23
- .create_pact
24
- .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
25
- .create_pact
26
- .create_consumer("Baz")
27
- .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
28
- .create_pact
29
- end
30
18
 
31
19
  subject { Repository.new.find_for_verification("Bar", consumer_version_selectors) }
32
20
 
33
21
  context "when there are no selectors" do
22
+ before do
23
+ td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
24
+ .create_consumer_version_tag("prod")
25
+ .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
26
+ .comment("next pact not selected")
27
+ .create_pact
28
+ .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
29
+ .create_pact
30
+ .create_consumer("Baz")
31
+ .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
32
+ .create_pact
33
+ end
34
+
34
35
  let(:consumer_version_selectors) { [] }
35
36
 
36
37
  it "returns the latest pact for each consumer" do
37
38
  expect(subject.size).to eq 2
38
39
  expect(find_by_consumer_name_and_consumer_version_number("Foo", "foo-latest-dev-version")).to_not be nil
39
40
  expect(find_by_consumer_name_and_consumer_version_number("Baz", "baz-latest-dev-version")).to_not be nil
41
+ expect(subject.all?(&:overall_latest?)).to be true
40
42
  end
41
43
  end
42
44
 
43
- context "when consumer tag names are specified" do
45
+ context "when the latest consumer tag names are specified" do
46
+ before do
47
+ td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
48
+ .create_consumer_version_tag("prod")
49
+ .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
50
+ .comment("next pact not selected")
51
+ .create_pact
52
+ .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
53
+ .create_pact
54
+ .create_consumer("Baz")
55
+ .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
56
+ .create_consumer_version_tag("prod")
57
+ .create_pact
58
+ end
59
+
44
60
  let(:pact_selector_1) { double('selector', tag: 'dev', latest: true) }
45
61
  let(:pact_selector_2) { double('selector', tag: 'prod', latest: true) }
46
62
  let(:consumer_version_selectors) do
@@ -48,47 +64,78 @@ module PactBroker
48
64
  end
49
65
 
50
66
  it "returns the latest pact with the specified tags for each consumer" do
51
- expect(find_by_consumer_version_number("foo-latest-prod-version")).to_not be nil
52
- expect(find_by_consumer_version_number("foo-latest-dev-version")).to_not be nil
53
- expect(find_by_consumer_version_number("baz-latest-dev-version")).to_not be nil
67
+ expect(find_by_consumer_version_number("foo-latest-prod-version").selectors).to eq [Selector.latest_for_tag('prod')]
68
+ expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [Selector.latest_for_tag('dev')]
69
+ expect(find_by_consumer_version_number("baz-latest-dev-version").selectors.sort_by{ |s| s[:tag] }).to eq [{ tag: 'dev', latest: true }, { tag: 'prod', latest: true }]
54
70
  expect(subject.size).to eq 3
55
71
  end
56
72
 
57
73
  it "sets the latest_consumer_version_tag_names" do
58
- expect(find_by_consumer_version_number("foo-latest-prod-version").tag).to eq 'prod'
74
+ expect(find_by_consumer_version_number("foo-latest-prod-version").selectors.collect(&:tag)).to eq ['prod']
75
+ end
76
+ end
77
+
78
+ context "when all versions with a given tag are requested" do
79
+ before do
80
+ td.create_pact_with_hierarchy("Foo2", "prod-version-1", "Bar2")
81
+ .create_consumer_version_tag("prod")
82
+ .create_consumer_version("not-prod-version", tag_names: %w[master])
83
+ .create_pact
84
+ .create_consumer_version("prod-version-2", tag_names: %w[prod])
85
+ .create_pact
86
+ end
87
+
88
+ let(:consumer_version_selectors) { [pact_selector_1] }
89
+ let(:pact_selector_1) { double('selector', tag: 'prod', latest: nil) }
90
+
91
+ subject { Repository.new.find_for_verification("Bar2", consumer_version_selectors) }
92
+
93
+ it "returns all the versions with the specified tag" do
94
+ expect(subject.size).to be 2
95
+ expect(find_by_consumer_version_number("prod-version-1").selectors.collect(&:tag)).to eq %w[prod]
96
+ expect(find_by_consumer_version_number("prod-version-2").selectors.collect(&:tag)).to eq %w[prod]
59
97
  end
60
98
 
61
- context "when all versions with a given tag are requested" do
62
- before do
63
- td.create_pact_with_hierarchy("Foo2", "prod-version-1", "Bar2")
64
- .create_consumer_version_tag("prod")
65
- .create_consumer_version("not-prod-version", tag_names: %w[master])
66
- .create_pact
67
- .create_consumer_version("prod-version-2", tag_names: %w[prod])
68
- .create_pact
69
- end
70
-
71
- let(:consumer_version_selectors) { [pact_selector_1] }
72
- let(:pact_selector_1) { double('selector', tag: 'prod', latest: nil) }
73
-
74
- subject { Repository.new.find_for_verification("Bar2", consumer_version_selectors) }
75
-
76
- it "returns all the versions with the specified tag" do
77
- expect(subject.size).to be 2
78
- expect(find_by_consumer_version_number("prod-version-1")).to_not be nil
79
- expect(find_by_consumer_version_number("prod-version-2")).to_not be nil
80
- end
81
-
82
- it "dedupes them to ensure that each pact version is only verified once" do
83
- td.create_consumer_version("prod-version-3", tag_names: %w[prod])
84
- .republish_same_pact
85
- expect(subject.size).to be 2
86
- expect(subject.collect(&:consumer_version_number)).to eq %w[prod-version-1 prod-version-3]
87
- end
99
+ it "dedupes them to ensure that each pact version is only verified once" do
100
+ td.create_consumer_version("prod-version-3", tag_names: %w[prod])
101
+ .republish_same_pact
102
+ expect(subject.size).to be 2
103
+ expect(subject.collect(&:consumer_version_number)).to eq %w[prod-version-1 prod-version-3]
104
+ end
105
+ end
106
+
107
+ context "when a pact version has been selected by two different selectors" do
108
+ before do
109
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
110
+ .create_consumer_version_tag("dev")
111
+ .create_consumer_version_tag("prod")
112
+ end
113
+
114
+ let(:consumer_version_selectors) { [pact_selector_1, pact_selector_2] }
115
+ let(:pact_selector_1) { double('selector1', tag: 'prod', latest: nil) }
116
+ let(:pact_selector_2) { double('selector2', tag: 'dev', latest: true) }
117
+ let(:consumer_version_selectors) { [pact_selector_1, pact_selector_2] }
118
+
119
+ it "returns a single selected pact with multiple selectors" do
120
+ expect(subject.size).to eq 1
121
+ expect(subject.first.selectors.size).to eq 2
88
122
  end
89
123
  end
90
124
 
91
125
  context "when no selectors are specified" do
126
+ before do
127
+ td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
128
+ .create_consumer_version_tag("prod")
129
+ .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
130
+ .comment("next pact not selected")
131
+ .create_pact
132
+ .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
133
+ .create_pact
134
+ .create_consumer("Baz")
135
+ .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
136
+ .create_pact
137
+ end
138
+
92
139
  let(:consumer_version_selectors) { [] }
93
140
 
94
141
  it "returns the latest pact for each provider" do
@@ -98,7 +145,7 @@ module PactBroker
98
145
  end
99
146
 
100
147
  it "does not set the tag name" do
101
- expect(find_by_consumer_version_number("foo-latest-dev-version").tag).to be nil
148
+ expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [{ latest: true }]
102
149
  expect(find_by_consumer_version_number("foo-latest-dev-version").overall_latest?).to be true
103
150
  end
104
151
  end
@@ -4,7 +4,7 @@ module PactBroker
4
4
  module Pacts
5
5
  module SquashPactsForVerification
6
6
  describe ".call" do
7
- let(:head_pacts) { [pact_1, pact_2] }
7
+ let(:selected_pact) { pact_1 }
8
8
  let(:head_tag_1) { "dev" }
9
9
  let(:head_tag_2) { "feat-x" }
10
10
  let(:pact_version_sha_1) { "1" }
@@ -15,42 +15,23 @@ module PactBroker
15
15
  select_pending_provider_version_tags: pending_provider_version_tags
16
16
  )
17
17
  end
18
- let(:domain_pact_2) { double('pact2', pending?: pending_2) }
19
18
  let(:pending_1) { false }
20
- let(:pending_2) { false }
21
19
  let(:pending_provider_version_tags) { [] }
22
20
 
23
21
  let(:pact_1) do
24
- double("HeadPact",
25
- tag: head_tag_1,
26
- pact_version_sha: pact_version_sha_1,
27
- pact: domain_pact_1
22
+ double("SelectedPact",
23
+ tag_names_for_selectors_for_latest_pacts: %w[dev feat-x],
24
+ pact: domain_pact_1,
25
+ overall_latest?: false
28
26
  )
29
27
  end
30
28
 
31
- let(:pact_2) do
32
- double("HeadPact",
33
- tag: head_tag_2,
34
- pact_version_sha: pact_version_sha_2,
35
- pact: domain_pact_2
36
- )
37
- end
38
-
39
- let(:provider_name) { "Bar" }
40
29
  let(:provider_version_tags) { [] }
41
30
 
42
- subject { SquashPactsForVerification.call(provider_version_tags, head_pacts) }
31
+ subject { SquashPactsForVerification.call(provider_version_tags, selected_pact, true) }
43
32
 
44
- context "when all of the consumer tags are not nil" do
45
- its(:head_consumer_tags) { is_expected.to eq %w[dev feat-x] }
46
- its(:overall_latest?) { is_expected.to be false }
47
- end
48
-
49
- context "when one of the consumer tags is nil" do
50
- let(:head_tag_2) { nil }
51
- its(:head_consumer_tags) { is_expected.to eq %w[dev] }
52
- its(:overall_latest?) { is_expected.to be true }
53
- end
33
+ its(:head_consumer_tags) { is_expected.to eq %w[dev feat-x] }
34
+ its(:overall_latest?) { is_expected.to be false }
54
35
 
55
36
  context "when there are no provider tags" do
56
37
  context "when the pact version is not pending" do
@@ -13,6 +13,7 @@ module PactBroker
13
13
  double(VerifiablePact,
14
14
  head_consumer_tags: head_consumer_tags,
15
15
  consumer_name: "Foo",
16
+ consumer_version_number: "123",
16
17
  provider_name: "Bar",
17
18
  pending_provider_tags: pending_provider_tags,
18
19
  non_pending_provider_tags: non_pending_provider_tags,
@@ -32,6 +33,7 @@ module PactBroker
32
33
  context "when there is 1 head consumer tags" do
33
34
  let(:head_consumer_tags) { %w[dev] }
34
35
  its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it is the pact for the latest version of Foo tagged with 'dev'" }
36
+ its(:pact_description) { is_expected.to eq "Pact between Foo and Bar, consumer version 123, latest dev"}
35
37
  end
36
38
 
37
39
  context "when there are 2 head consumer tags" do
@@ -152,14 +152,16 @@ module PactBroker
152
152
  let(:provider) { PactBroker::Domain::Pacticipant.new(name: 'Provider') }
153
153
  let(:webhooks) { [instance_double(PactBroker::Domain::Webhook, description: 'description', uuid: '1244')]}
154
154
  let(:triggered_webhook) { instance_double(PactBroker::Webhooks::TriggeredWebhook) }
155
+ let(:webhook_execution_configuration) { double('webhook_execution_configuration') }
155
156
  let(:options) do
156
157
  { database_connector: double('database_connector'),
157
- webhook_context: {},
158
+ webhook_execution_configuration: webhook_execution_configuration,
158
159
  logging_options: {}
159
160
  }
160
161
  end
161
162
 
162
163
  before do
164
+ allow(webhook_execution_configuration).to receive(:with_webhook_context).and_return(webhook_execution_configuration)
163
165
  allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_by_consumer_and_or_provider_and_event_name).and_return(webhooks)
164
166
  allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:create_triggered_webhook).and_return(triggered_webhook)
165
167
  allow(Job).to receive(:perform_in)
@@ -177,6 +179,11 @@ module PactBroker
177
179
  expect(Service).to receive(:run_later).with(webhooks, pact, verification, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED, options)
178
180
  subject
179
181
  end
182
+
183
+ it "merges the event name in the options" do
184
+ expect(webhook_execution_configuration).to receive(:with_webhook_context).with(event_name: PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED)
185
+ subject
186
+ end
180
187
  end
181
188
 
182
189
  context "when no webhooks are found" do
@@ -50,9 +50,11 @@ module PactBroker
50
50
  }
51
51
  end
52
52
 
53
+ let(:webhook_context) { { consumer_version_number: "123" } }
54
+
53
55
  let(:webhook_request_logger) { WebhookRequestLogger.new(options) }
54
56
 
55
- subject(:logs) { webhook_request_logger.log(uuid, webhook_request, response, error) }
57
+ subject(:logs) { webhook_request_logger.log(uuid, webhook_request, response, error, webhook_context) }
56
58
 
57
59
  describe "application logs" do
58
60
  it "logs the request" do
@@ -69,6 +71,9 @@ module PactBroker
69
71
  end
70
72
 
71
73
  describe "execution logs" do
74
+ it "logs the application context" do
75
+ expect(logs).to include webhook_context.to_json
76
+ end
72
77
 
73
78
  it "logs the request method and path" do
74
79
  expect(logs).to include "POST http://example.org/hook"
@@ -11,10 +11,12 @@ if __FILE__ == $0
11
11
  def webrick_opts port
12
12
  certificate = OpenSSL::X509::Certificate.new(File.read(SSL_CERT))
13
13
  cert_name = certificate.subject.to_a.collect{|a| a[0..1] }
14
+ logger_stream = ENV['DEBUG'] ? $stderr : StringIO.new
14
15
  {
15
16
  Port: port,
16
17
  Host: "0.0.0.0",
17
18
  AccessLog: [],
19
+ Logger: WEBrick::Log.new(logger_stream,WEBrick::Log::INFO),
18
20
  SSLCertificate: certificate,
19
21
  SSLPrivateKey: OpenSSL::PKey::RSA.new(File.read(SSL_KEY)),
20
22
  SSLEnable: true,
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.46.0
4
+ version: 2.47.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: 2020-01-30 00:00:00.000000000 Z
13
+ date: 2020-02-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -480,14 +480,20 @@ dependencies:
480
480
  requirements:
481
481
  - - "~>"
482
482
  - !ruby/object:Gem::Version
483
- version: '1.6'
483
+ version: '1.8'
484
+ - - ">="
485
+ - !ruby/object:Gem::Version
486
+ version: 1.8.1
484
487
  type: :development
485
488
  prerelease: false
486
489
  version_requirements: !ruby/object:Gem::Requirement
487
490
  requirements:
488
491
  - - "~>"
489
492
  - !ruby/object:Gem::Version
490
- version: '1.6'
493
+ version: '1.8'
494
+ - - ">="
495
+ - !ruby/object:Gem::Version
496
+ version: 1.8.1
491
497
  - !ruby/object:Gem::Dependency
492
498
  name: pg
493
499
  requirement: !ruby/object:Gem::Requirement
@@ -1051,6 +1057,8 @@ files:
1051
1057
  - lib/pact_broker/pacts/parse.rb
1052
1058
  - lib/pact_broker/pacts/placeholder_pact.rb
1053
1059
  - lib/pact_broker/pacts/repository.rb
1060
+ - lib/pact_broker/pacts/selected_pact.rb
1061
+ - lib/pact_broker/pacts/selector.rb
1054
1062
  - lib/pact_broker/pacts/service.rb
1055
1063
  - lib/pact_broker/pacts/sort_content.rb
1056
1064
  - lib/pact_broker/pacts/squash_pacts_for_verification.rb
@@ -1604,7 +1612,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1604
1612
  - !ruby/object:Gem::Version
1605
1613
  version: '0'
1606
1614
  requirements: []
1607
- rubygems_version: 3.1.2
1615
+ rubyforge_project:
1616
+ rubygems_version: 2.7.6
1608
1617
  signing_key:
1609
1618
  specification_version: 4
1610
1619
  summary: See description