pact_broker 2.121.2 → 2.123.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +116 -0
  3. data/Gemfile +1 -4
  4. data/lib/pact_broker/api/contracts/consumer_version_selector_contract.rb +17 -1
  5. data/lib/pact_broker/api/contracts/utf_8_validation.rb +5 -1
  6. data/lib/pact_broker/api/decorators/pacts_for_verification_query_decorator.rb +1 -0
  7. data/lib/pact_broker/api/decorators/publish_contract_decorator.rb +4 -3
  8. data/lib/pact_broker/api/decorators/verification_decorator.rb +12 -2
  9. data/lib/pact_broker/api/decorators/verification_summary_decorator.rb +6 -0
  10. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +6 -69
  11. data/lib/pact_broker/api/renderers/markdown/consumer_contract_renderer.rb +87 -0
  12. data/lib/pact_broker/api/renderers/markdown/interaction_renderer.rb +52 -0
  13. data/lib/pact_broker/api/renderers/markdown/interaction_view_model.rb +162 -0
  14. data/lib/pact_broker/api/renderers/markdown.rb +9 -0
  15. data/lib/pact_broker/api/resources/publish_contracts.rb +10 -6
  16. data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +10 -2
  17. data/lib/pact_broker/hash_refinements.rb +2 -2
  18. data/lib/pact_broker/locale/en.yml +3 -0
  19. data/lib/pact_broker/pacts/create_formatted_diff.rb +2 -7
  20. data/lib/pact_broker/pacts/generate_sha.rb +7 -4
  21. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +41 -21
  22. data/lib/pact_broker/pacts/repository.rb +2 -2
  23. data/lib/pact_broker/pacts/selected_pact.rb +4 -0
  24. data/lib/pact_broker/pacts/selector.rb +20 -2
  25. data/lib/pact_broker/pacts/selectors.rb +4 -0
  26. data/lib/pact_broker/unified_diff.rb +26 -0
  27. data/lib/pact_broker/verifications/repository.rb +1 -1
  28. data/lib/pact_broker/verifications/summary_for_consumer_version.rb +4 -0
  29. data/lib/pact_broker/version.rb +1 -1
  30. data/pact_broker.gemspec +2 -2
  31. metadata +25 -30
  32. data/lib/pact/doc/doc_file.rb +0 -38
  33. data/lib/pact/doc/generate.rb +0 -11
  34. data/lib/pact/doc/generator.rb +0 -82
  35. data/lib/pact/doc/interaction_view_model.rb +0 -157
  36. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +0 -74
  37. data/lib/pact/doc/markdown/generator.rb +0 -24
  38. data/lib/pact/doc/markdown/index_renderer.rb +0 -40
  39. data/lib/pact/doc/markdown/interaction_renderer.rb +0 -51
  40. data/lib/pact/doc/sort_interactions.rb +0 -14
  41. data/lib/pact/doc.rb +0 -5
  42. /data/lib/{pact/doc → pact_broker/api/renderers}/markdown/interaction.erb +0 -0
  43. /data/lib/{pact/doc → pact_broker/api/renderers}/markdown/interaction_async.erb +0 -0
@@ -0,0 +1,9 @@
1
+ module PactBroker
2
+ module Api
3
+ module Renderers
4
+ module Markdown
5
+ MARKDOWN_SPECIAL_CHARS_REGEXP = /[\\`*_\[\](){}#+\-.!|]/
6
+ end
7
+ end
8
+ end
9
+ end
@@ -56,16 +56,20 @@ module PactBroker
56
56
  @parsed_contracts ||= decorator_class(:publish_contracts_decorator).new(PactBroker::Contracts::ContractsToPublish.new).from_hash(params, { user_options: { sha_generator: PactBroker.configuration.sha_generator } } )
57
57
  end
58
58
 
59
+ # Memoized because decoding and parsing very large contracts is expensive,
60
+ # and this is called multiple times per request (validation, authorization and publication)
59
61
  def params
60
- p = super(default: {}, symbolize_names: false)
61
- if p["contracts"].is_a?(Array)
62
- p["contracts"].each do | contract |
63
- if contract.is_a?(Hash)
64
- decode_and_parse_content(contract)
62
+ @params_with_decoded_contracts ||= begin
63
+ p = super(default: {}, symbolize_names: false)
64
+ if p["contracts"].is_a?(Array)
65
+ p["contracts"].each do | contract |
66
+ if contract.is_a?(Hash)
67
+ decode_and_parse_content(contract)
68
+ end
65
69
  end
66
70
  end
71
+ p
67
72
  end
68
- p
69
73
  end
70
74
 
71
75
  def schema
@@ -32,7 +32,15 @@ Example: This data structure represents the way a user might specify "I want to
32
32
 
33
33
  `consumerVersionSelectors.matchingBranch`: if the key is specified, can only be set to `true`. When true, returns the latest pact for any branch with the same name as the specified `providerVersionBranch`.
34
34
 
35
- `consumerVersionSelectors.fallbackBranch`: the name of the branch to fallback to if the specified `branch` does not exist. Use of this property is discouraged as it may allow a pact to pass on a feature branch while breaking backwards compatibility with the main branch, which is generally not desired. It is better to use two separate consumer version selectors, one with the main branch name, and one with the feature branch name, rather than use this property.
35
+ `consumerVersionSelectors.fallbackBranch`: the name of the branch to fall back to for each consumer that has no pact from the specified `branch`. Use of this property is discouraged as it may allow a pact to pass on a feature branch while breaking backwards compatibility with the main branch, which is generally not desired. It is better to use two separate consumer version selectors, one with the main branch name, and one with the feature branch name, rather than use this property.
36
+
37
+ `consumerVersionSelectors.fallbackToMainBranch`: if the key is specified, can only be set to `true`. Used with `branch`. For each consumer that has no pact for the specified `branch`, returns the latest pact from that consumer's configured `mainBranch` instead. Consumers without a configured `mainBranch` have no fallback. Use this instead of `fallbackBranch` when the consumers have different main branch names, or when a main branch may be renamed, so the provider does not need to know the name. It cannot be used together with `fallbackBranch`. The same caveat as `fallbackBranch` applies.
38
+
39
+ {
40
+ "consumerVersionSelectors": [
41
+ { "branch": "feat-x", "fallbackToMainBranch": true }
42
+ ]
43
+ }
36
44
 
37
45
  `consumerVersionSelectors.deployed`: if the key is specified, can only be set to `true`. Returns the pacts for all versions of the consumer that are currently deployed to any environment. Use of this selector requires that the deployment of the consumer application is recorded in the Pact Broker using the `pact-broker record-deployment` CLI.
38
46
 
@@ -48,7 +56,7 @@ Example: This data structure represents the way a user might specify "I want to
48
56
 
49
57
  `consumerVersionSelectors.tag`: the tag name(s) of the consumer versions to get the pacts for. *This field is still supported but it is recommended to use the `branch` in preference now.*
50
58
 
51
- `consumerVersionSelectors.fallbackTag`: the name of the tag to fallback to if the specified `tag` does not exist. This is useful when the consumer and provider use matching branch names to coordinate the development of new features. *This field is still supported but it is recommended to use two separate selectors - one with the main branch name and one with the feature branch name.*
59
+ `consumerVersionSelectors.fallbackTag`: the name of the tag to fall back to for each consumer that has no pact with the specified `tag`. This is useful when the consumer and provider use matching branch names to coordinate the development of new features. *This field is still supported but it is recommended to use two separate selectors - one with the main branch name and one with the feature branch name.*
52
60
 
53
61
  `providerVersionBranch`: the repository branch name for the provider application version that will be published with the verification results. This is used by the Broker to determine whether or not a particular pact is in pending state or not.
54
62
 
@@ -80,8 +80,8 @@ module PactBroker
80
80
  def symbolize_keys_private(params)
81
81
  case params
82
82
  when Hash
83
- params.inject({}) do |result, (key, value)|
84
- result.merge(key.to_sym => symbolize_keys_private(value))
83
+ params.each_with_object({}) do |(key, value), result|
84
+ result[key.to_sym] = symbolize_keys_private(value)
85
85
  end
86
86
  when Array
87
87
  params.collect { |value| symbolize_keys_private(value) }
@@ -105,6 +105,9 @@ en:
105
105
  pacts_for_verification_selector_branch_and_latest_false_disallowed: cannot specify a branch with latest=false
106
106
  pacts_for_verification_selector_main_branch_and_latest_false_disallowed: cannot specify mainBranch=true with latest=false
107
107
  pacts_for_verification_selector_fallback_branch_and_latest_false_disallowed: fallbackBranch can only be set if latest is true
108
+ pacts_for_verification_selector_fallback_to_main_branch_requires_branch: "a branch must be specified when fallbackToMainBranch=true is specified"
109
+ pacts_for_verification_selector_fallback_to_main_branch_and_fallback_branch_disallowed: cannot specify both fallbackBranch and fallbackToMainBranch=true
110
+ pacts_for_verification_selector_fallback_to_main_branch_and_latest_false_disallowed: fallbackToMainBranch can only be set if latest is true
108
111
  pacts_for_verification_selector_required_params_missing: must specify a value for environment or tag or branch, or specify mainBranch=true, matchingBranch=true, latest=true, deployed=true, released=true or deployedOrReleased=true
109
112
  pacts_for_verification_selector_main_branch_with_other_param_disallowed: cannot specify mainBranch=true with any other criteria apart from consumer
110
113
  pacts_for_verification_selector_matching_branch_with_other_param_disallowed: cannot specify matchingBranch=true with any other criteria apart from consumer
@@ -1,14 +1,11 @@
1
- require "pact/support/matchers"
2
1
  require "pact_broker/json"
3
- require "pact/matchers/unix_diff_formatter"
2
+ require "pact_broker/unified_diff"
4
3
  require "pact_broker/pacts/sort_content"
5
4
  require "pact_broker/pacts/content"
6
5
 
7
6
  module PactBroker
8
7
  module Pacts
9
8
  class CreateFormattedDiff
10
- extend Pact::Matchers
11
-
12
9
  def self.call pact_json_content, previous_pact_json_content, raw: false
13
10
  pact_hash = JSON.parse(pact_json_content, **PactBroker::PACT_PARSING_OPTIONS)
14
11
  previous_pact_hash = JSON.parse(previous_pact_json_content, **PactBroker::PACT_PARSING_OPTIONS)
@@ -18,9 +15,7 @@ module PactBroker
18
15
  previous_pact_hash = SortContent.call(PactBroker::Pacts::Content.from_hash(previous_pact_hash).without_ids.to_hash)
19
16
  end
20
17
 
21
- difference = diff(previous_pact_hash, pact_hash)
22
-
23
- Pact::Matchers::UnixDiffFormatter.call(difference, colour: false, include_explanation: false)
18
+ PactBroker::UnifiedDiff.call(JSON.pretty_generate(previous_pact_hash), JSON.pretty_generate(pact_hash))
24
19
  end
25
20
  end
26
21
  end
@@ -11,17 +11,20 @@ module PactBroker
11
11
  include PactBroker::Logging
12
12
 
13
13
  # @param [String] json_content
14
- def self.call(json_content, _options = {})
14
+ # @param [Hash] options
15
+ # @option options [Hash] :parsed_content the result of parsing the json_content, if it has already been parsed.
16
+ # Used to avoid parsing very large pacts again. Must be the parsed json_content, or the sha will be wrong.
17
+ def self.call(json_content, options = {})
15
18
  content_for_sha = if PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results
16
- extract_verifiable_content_for_sha(json_content)
19
+ extract_verifiable_content_for_sha(json_content, options[:parsed_content])
17
20
  else
18
21
  json_content
19
22
  end
20
23
  measure_info("Generating SHA1 hexdigest for pact", payload: { length: content_for_sha.length } ){ Digest::SHA1.hexdigest(content_for_sha) }
21
24
  end
22
25
 
23
- def self.extract_verifiable_content_for_sha(json_content)
24
- objects = Content.from_json(json_content)
26
+ def self.extract_verifiable_content_for_sha(json_content, parsed_content = nil)
27
+ objects = parsed_content.is_a?(Hash) ? Content.from_hash(parsed_content) : Content.from_json(json_content)
25
28
  sorted_content = measure_info("Sorting content", payload: { length: json_content.length }){ objects.sort }
26
29
  sorted_content.content_that_affects_verification_results.to_json
27
30
  end
@@ -28,7 +28,9 @@ module PactBroker
28
28
  # @return [VerifiablePact] an array of VerifiablePact objects
29
29
  def find(provider_name, consumer_version_selectors)
30
30
  selected_pacts = find_pacts_by_selector(provider_name, consumer_version_selectors)
31
- selected_pacts = selected_pacts + find_pacts_for_fallback_tags(selected_pacts, provider_name, consumer_version_selectors)
31
+ selected_pacts = selected_pacts +
32
+ find_pacts_for_fallback_tags(selected_pacts, provider_name, consumer_version_selectors) +
33
+ find_pacts_for_fallback_branches(selected_pacts, provider_name, consumer_version_selectors)
32
34
  merge_selected_pacts(selected_pacts)
33
35
  end
34
36
 
@@ -178,22 +180,32 @@ module PactBroker
178
180
  end
179
181
  end
180
182
 
183
+ # The fallback is applied per consumer, so a consumer without a pact for the tag
184
+ # still gets its fallback pact when another consumer does have a pact for the tag.
181
185
  def find_pacts_for_fallback_tags(selected_pacts, provider_name, consumer_version_selectors)
182
- # TODO at the moment, the validation doesn't stop fallback being used with 'all' selectors
183
- selectors_with_fallback_tags = consumer_version_selectors.select(&:fallback_tag?)
184
- selectors_missing_a_pact = selectors_with_fallback_tags.reject do | selector |
185
- selected_pacts.any? do | selected_pact |
186
- selected_pact.latest_for_tag?(selector.tag)
186
+ consumer_version_selectors.select(&:fallback_tag?).flat_map do | selector |
187
+ consumer_ids_with_a_pact_for_the_tag = consumer_ids_of(selected_pacts.select { | selected_pact | selected_pact.latest_for_tag?(selector.tag) })
188
+ find_fallback_pacts(provider_name, selector, consumer_ids_with_a_pact_for_the_tag) do | query |
189
+ query.for_latest_consumer_versions_with_tag(selector.fallback_tag)
187
190
  end
188
191
  end
192
+ end
189
193
 
190
- if selectors_missing_a_pact.any?
191
- find_pacts_for_which_the_latest_version_for_the_fallback_tag_is_required(provider_name, selectors_missing_a_pact)
192
- else
193
- []
194
+ # The fallback is applied per consumer, so a consumer without a pact for the branch
195
+ # still gets its fallback pact when another consumer does have a pact for the branch.
196
+ def find_pacts_for_fallback_branches(selected_pacts, provider_name, consumer_version_selectors)
197
+ consumer_version_selectors.select { | selector | selector.fallback_branch? || selector.fallback_to_main_branch? }.flat_map do | selector |
198
+ consumer_ids_with_a_pact_for_the_branch = consumer_ids_of(selected_pacts.select { | selected_pact | selected_pact.latest_for_branch?(selector.branch) })
199
+ find_fallback_pacts(provider_name, selector, consumer_ids_with_a_pact_for_the_branch) do | query |
200
+ selector.fallback_to_main_branch? ? query.latest_for_main_branches : query.latest_for_consumer_branch(selector.fallback_branch)
201
+ end
194
202
  end
195
203
  end
196
204
 
205
+ def consumer_ids_of(selected_pacts)
206
+ selected_pacts.collect { | selected_pact | selected_pact.pact.consumer.id }.uniq
207
+ end
208
+
197
209
  def find_pacts_by_selector(provider_name, consumer_version_selectors)
198
210
  provider = pacticipant_repository.find_by_name(provider_name)
199
211
 
@@ -248,17 +260,25 @@ module PactBroker
248
260
  selectors
249
261
  end
250
262
 
251
- def find_pacts_for_which_the_latest_version_for_the_fallback_tag_is_required(provider_name, selectors)
252
- selectors.collect do | selector |
253
- query = scope_for(PactPublication).eager_for_domain_with_content.for_provider_name(provider_name).for_latest_consumer_versions_with_tag(selector.fallback_tag)
254
- query = query.for_consumer_name(selector.consumer) if selector.consumer
255
- query.all.collect do | pact_publication |
256
- SelectedPact.new(
257
- pact_publication.to_domain,
258
- Selectors.new(selector.resolve_for_fallback(pact_publication.consumer_version))
259
- )
260
- end
261
- end.flatten
263
+ # Yields the provider's pacts so the caller can narrow them to the latest for the fallback.
264
+ def find_fallback_pacts(provider_name, selector, consumer_ids_to_exclude)
265
+ query = yield scope_for(PactPublication).eager_for_domain_with_content.for_provider_name(provider_name)
266
+ query = query.for_consumer_name(selector.consumer) if selector.consumer
267
+ query = query.exclude(Sequel[:pact_publications][:consumer_id] => consumer_ids_to_exclude) if consumer_ids_to_exclude.any?
268
+ query.all.collect do | pact_publication |
269
+ SelectedPact.new(
270
+ pact_publication.to_domain,
271
+ Selectors.new(resolve_selector_for_fallback(selector, pact_publication))
272
+ )
273
+ end
274
+ end
275
+
276
+ def resolve_selector_for_fallback(selector, pact_publication)
277
+ if selector.fallback_to_main_branch?
278
+ selector.resolve_for_fallback_to_main_branch(pact_publication.consumer_version, pact_publication.consumer.main_branch)
279
+ else
280
+ selector.resolve_for_fallback(pact_publication.consumer_version)
281
+ end
262
282
  end
263
283
 
264
284
  def find_provider_tags_for_which_pact_publication_id_is_pending(pact_publication, successfully_verified_head_pact_publication_ids_for_each_provider_tag)
@@ -3,7 +3,6 @@ require "pact_broker/logging"
3
3
  require "pact_broker/pacts/generate_sha"
4
4
  require "pact_broker/pacts/pact_publication"
5
5
  require "pact_broker/pacts/pact_version"
6
- require "pact/shared/json_differ"
7
6
  require "pact_broker/domain"
8
7
  require "pact_broker/pacts/parse"
9
8
  require "pact_broker/pacts/latest_pact_publication_id_for_consumer_version"
@@ -203,6 +202,7 @@ module PactBroker
203
202
  def find_by_consumer_version consumer_name, consumer_version_number
204
203
  scope_for(PactPublication)
205
204
  .eager_for_domain_with_content
205
+ .eager(pact_version: :latest_verification)
206
206
  .for_consumer_name_and_maybe_version_number(consumer_name, consumer_version_number)
207
207
  .remove_overridden_revisions_from_complete_query
208
208
  .all
@@ -386,7 +386,7 @@ module PactBroker
386
386
  end
387
387
 
388
388
  def different? pact, other_pact
389
- Pact::JsonDiffer.(pact.content_hash, other_pact.content_hash, allow_unexpected_keys: false).any?
389
+ pact.content_hash != other_pact.content_hash
390
390
  end
391
391
 
392
392
  def find_or_create_pact_version consumer_id, provider_id, pact_version_sha, json_content
@@ -57,6 +57,10 @@ module PactBroker
57
57
  selectors.latest_for_tag?(potential_tag)
58
58
  end
59
59
 
60
+ def latest_for_branch? potential_branch = nil
61
+ selectors.latest_for_branch?(potential_branch)
62
+ end
63
+
60
64
  def consumer_version_order
61
65
  pact.consumer_version.order
62
66
  end
@@ -6,7 +6,7 @@ module PactBroker
6
6
  class Selector < Hash
7
7
  using PactBroker::HashRefinements
8
8
 
9
- PROPERTY_NAMES = [:latest, :tag, :branch, :consumer, :consumer_version, :environment_name, :fallback_tag, :fallback_branch, :main_branch, :matching_branch, :currently_supported, :currently_deployed]
9
+ PROPERTY_NAMES = [:latest, :tag, :branch, :consumer, :consumer_version, :environment_name, :fallback_tag, :fallback_branch, :fallback_to_main_branch, :main_branch, :matching_branch, :currently_supported, :currently_deployed]
10
10
 
11
11
  def initialize(properties = {})
12
12
  properties.without(*PROPERTY_NAMES).tap { |it| warn("WARN: Unsupported property for #{self.class.name}: #{it.keys.join(", ")} at #{caller[0..3]}") if it.any? }
@@ -14,13 +14,19 @@ module PactBroker
14
14
  end
15
15
 
16
16
  def resolve(consumer_version)
17
- ResolvedSelector.new(self.to_h.without(:fallback_tag, :fallback_branch), consumer_version)
17
+ ResolvedSelector.new(self.to_h.without(:fallback_tag, :fallback_branch, :fallback_to_main_branch), consumer_version)
18
18
  end
19
19
 
20
20
  def resolve_for_fallback(consumer_version)
21
21
  ResolvedSelector.new(self.to_h, consumer_version)
22
22
  end
23
23
 
24
+ # Records the consumer's main branch as the fallback branch, so the verification
25
+ # notice names the branch that was actually used.
26
+ def resolve_for_fallback_to_main_branch(consumer_version, main_branch)
27
+ ResolvedSelector.new(self.to_h.without(:fallback_to_main_branch).merge(fallback_branch: main_branch), consumer_version)
28
+ end
29
+
24
30
  def resolve_for_environment(consumer_version, environment, target = nil)
25
31
  ResolvedSelector.new(self.to_h.merge({ environment: environment, target: target }.compact), consumer_version)
26
32
  end
@@ -87,6 +93,10 @@ module PactBroker
87
93
  self[:fallback_branch] = fallback_branch
88
94
  end
89
95
 
96
+ def fallback_to_main_branch= fallback_to_main_branch
97
+ self[:fallback_to_main_branch] = fallback_to_main_branch
98
+ end
99
+
90
100
  def fallback_tag
91
101
  self[:fallback_tag]
92
102
  end
@@ -95,6 +105,10 @@ module PactBroker
95
105
  self[:fallback_branch]
96
106
  end
97
107
 
108
+ def fallback_to_main_branch
109
+ self[:fallback_to_main_branch]
110
+ end
111
+
98
112
  def consumer= consumer
99
113
  self[:consumer] = consumer
100
114
  end
@@ -235,6 +249,10 @@ module PactBroker
235
249
  !!fallback_branch
236
250
  end
237
251
 
252
+ def fallback_to_main_branch?
253
+ !!fallback_to_main_branch
254
+ end
255
+
238
256
  def main_branch
239
257
  self[:main_branch]
240
258
  end
@@ -59,6 +59,10 @@ module PactBroker
59
59
  any? { | selector | selector.latest_for_tag?(potential_tag) }
60
60
  end
61
61
 
62
+ def latest_for_branch? potential_branch = nil
63
+ any? { | selector | selector.latest_for_branch?(potential_branch) }
64
+ end
65
+
62
66
  def tag_names_of_selectors_for_all_pacts
63
67
  select(&:all_for_tag?).collect(&:tag).uniq
64
68
  end
@@ -0,0 +1,26 @@
1
+ require "diff/lcs"
2
+ require "diff/lcs/hunk"
3
+
4
+ module PactBroker
5
+ # Renders the difference between two texts in unified diff format,
6
+ # without the file header lines.
7
+ class UnifiedDiff
8
+ def self.call(old_text, new_text, context: 3)
9
+ old_lines = old_text.split("\n")
10
+ new_lines = new_text.split("\n")
11
+ pieces = Diff::LCS.diff(old_lines, new_lines)
12
+ return "" if pieces.empty?
13
+
14
+ hunks = []
15
+ length_difference = 0
16
+ pieces.each do |piece|
17
+ hunk = Diff::LCS::Hunk.new(old_lines, new_lines, piece, context, length_difference)
18
+ length_difference = hunk.file_length_difference
19
+ hunk.merge(hunks.pop) if context.positive? && hunks.last && hunk.overlaps?(hunks.last)
20
+ hunks << hunk
21
+ end
22
+
23
+ hunks.collect { |hunk| hunk.diff(:unified) }.join("\n")
24
+ end
25
+ end
26
+ end
@@ -99,7 +99,7 @@ module PactBroker
99
99
  .remove_verifications_for_overridden_consumer_versions
100
100
  .for_consumer_name_and_consumer_version_number(consumer_name, consumer_version_number)
101
101
  .latest_by_pact_version
102
- .eager(:provider)
102
+ .eager(:provider, :consumer, :provider_version, :pact_version)
103
103
  .all
104
104
  .sort_by { | v | v.provider_name.downcase }
105
105
  end
@@ -13,6 +13,10 @@ module PactBroker
13
13
  successful.count == pacts.count
14
14
  end
15
15
 
16
+ def pacts_by_pact_version_sha
17
+ pacts.to_h { | pact | [pact.pact_version_sha, pact] }
18
+ end
19
+
16
20
  def provider_summary
17
21
  OpenStruct.new(
18
22
  successful: successful,
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = "2.121.2"
2
+ VERSION = "2.123.0"
3
3
  end
data/pact_broker.gemspec CHANGED
@@ -59,17 +59,17 @@ Gem::Specification.new do |gem|
59
59
  gem.add_runtime_dependency "semver2", "~> 3.4.2"
60
60
  gem.add_runtime_dependency "rack", "~> 3.2"
61
61
  gem.add_runtime_dependency "redcarpet", "~> 3.5"
62
- gem.add_runtime_dependency "pact-support", "~> 2.0"
63
62
  gem.add_runtime_dependency "haml", "~>5.0"
64
63
  gem.add_runtime_dependency "sucker_punch", "~>3.0"
65
64
  gem.add_runtime_dependency "rack-protection", "~> 4.1"
66
65
  gem.add_runtime_dependency "table_print", "~> 1.5"
67
66
  gem.add_runtime_dependency "semantic_logger", "~> 5.0"
68
- gem.add_runtime_dependency "sanitize", "~> 6.0"
67
+ gem.add_runtime_dependency "sanitize", "~> 7.0"
69
68
  gem.add_runtime_dependency "wisper", "~> 2.0"
70
69
  gem.add_runtime_dependency "anyway_config", "~> 2.1"
71
70
  gem.add_runtime_dependency "request_store", "~> 1.5"
72
71
  gem.add_runtime_dependency "moments", "~> 0.2"
72
+ gem.add_runtime_dependency "diff-lcs", "~> 1.5"
73
73
  # dropped in ruby 3.4 stdlib
74
74
  gem.add_runtime_dependency "mutex_m", "~> 0.3" # until as-notifications -> webmachine requires it
75
75
  gem.add_runtime_dependency "csv", "~> 3.0" # lib/pact_broker/api/decorators/relationships_csv_decorator.rb
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.121.2
4
+ version: 2.123.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: 2026-09-17 00:00:00.000000000 Z
13
+ date: 2026-11-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -152,20 +152,6 @@ dependencies:
152
152
  - - "~>"
153
153
  - !ruby/object:Gem::Version
154
154
  version: '3.5'
155
- - !ruby/object:Gem::Dependency
156
- name: pact-support
157
- requirement: !ruby/object:Gem::Requirement
158
- requirements:
159
- - - "~>"
160
- - !ruby/object:Gem::Version
161
- version: '2.0'
162
- type: :runtime
163
- prerelease: false
164
- version_requirements: !ruby/object:Gem::Requirement
165
- requirements:
166
- - - "~>"
167
- - !ruby/object:Gem::Version
168
- version: '2.0'
169
155
  - !ruby/object:Gem::Dependency
170
156
  name: haml
171
157
  requirement: !ruby/object:Gem::Requirement
@@ -242,14 +228,14 @@ dependencies:
242
228
  requirements:
243
229
  - - "~>"
244
230
  - !ruby/object:Gem::Version
245
- version: '6.0'
231
+ version: '7.0'
246
232
  type: :runtime
247
233
  prerelease: false
248
234
  version_requirements: !ruby/object:Gem::Requirement
249
235
  requirements:
250
236
  - - "~>"
251
237
  - !ruby/object:Gem::Version
252
- version: '6.0'
238
+ version: '7.0'
253
239
  - !ruby/object:Gem::Dependency
254
240
  name: wisper
255
241
  requirement: !ruby/object:Gem::Requirement
@@ -306,6 +292,20 @@ dependencies:
306
292
  - - "~>"
307
293
  - !ruby/object:Gem::Version
308
294
  version: '0.2'
295
+ - !ruby/object:Gem::Dependency
296
+ name: diff-lcs
297
+ requirement: !ruby/object:Gem::Requirement
298
+ requirements:
299
+ - - "~>"
300
+ - !ruby/object:Gem::Version
301
+ version: '1.5'
302
+ type: :runtime
303
+ prerelease: false
304
+ version_requirements: !ruby/object:Gem::Requirement
305
+ requirements:
306
+ - - "~>"
307
+ - !ruby/object:Gem::Version
308
+ version: '1.5'
309
309
  - !ruby/object:Gem::Dependency
310
310
  name: mutex_m
311
311
  requirement: !ruby/object:Gem::Requirement
@@ -593,18 +593,6 @@ files:
593
593
  - docs/developer/matrix.md
594
594
  - docs/developer/rack.md
595
595
  - docs/matrix-optimisation/README.md
596
- - lib/pact/doc.rb
597
- - lib/pact/doc/doc_file.rb
598
- - lib/pact/doc/generate.rb
599
- - lib/pact/doc/generator.rb
600
- - lib/pact/doc/interaction_view_model.rb
601
- - lib/pact/doc/markdown/consumer_contract_renderer.rb
602
- - lib/pact/doc/markdown/generator.rb
603
- - lib/pact/doc/markdown/index_renderer.rb
604
- - lib/pact/doc/markdown/interaction.erb
605
- - lib/pact/doc/markdown/interaction_async.erb
606
- - lib/pact/doc/markdown/interaction_renderer.rb
607
- - lib/pact/doc/sort_interactions.rb
608
596
  - lib/pact_broker.rb
609
597
  - lib/pact_broker/api.rb
610
598
  - lib/pact_broker/api/authorization/resource_access_policy.rb
@@ -717,6 +705,12 @@ files:
717
705
  - lib/pact_broker/api/paths.rb
718
706
  - lib/pact_broker/api/renderers/html_pact_renderer.rb
719
707
  - lib/pact_broker/api/renderers/integrations_dot_renderer.rb
708
+ - lib/pact_broker/api/renderers/markdown.rb
709
+ - lib/pact_broker/api/renderers/markdown/consumer_contract_renderer.rb
710
+ - lib/pact_broker/api/renderers/markdown/interaction.erb
711
+ - lib/pact_broker/api/renderers/markdown/interaction_async.erb
712
+ - lib/pact_broker/api/renderers/markdown/interaction_renderer.rb
713
+ - lib/pact_broker/api/renderers/markdown/interaction_view_model.rb
720
714
  - lib/pact_broker/api/resources.rb
721
715
  - lib/pact_broker/api/resources/after_reply.rb
722
716
  - lib/pact_broker/api/resources/all_webhooks.rb
@@ -1093,6 +1087,7 @@ files:
1093
1087
  - lib/pact_broker/ui/views/index/show.haml
1094
1088
  - lib/pact_broker/ui/views/layouts/main.haml
1095
1089
  - lib/pact_broker/ui/views/matrix/show.haml
1090
+ - lib/pact_broker/unified_diff.rb
1096
1091
  - lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb
1097
1092
  - lib/pact_broker/verifications/latest_verification_for_consumer_version_tag.rb
1098
1093
  - lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb
@@ -1,38 +0,0 @@
1
- module Pact
2
- module Doc
3
- class DocFile
4
-
5
- def initialize consumer_contract, dir, consumer_contract_renderer, file_extension
6
- @dir = dir
7
- @consumer_contract = consumer_contract
8
- @consumer_contract_renderer = consumer_contract_renderer
9
- @file_extension = file_extension
10
- end
11
-
12
- def write
13
- File.open(path, "w") { |io| io << doc_file_contents }
14
- end
15
-
16
- def title
17
- consumer_contract.provider.name
18
- end
19
-
20
- def name
21
- "#{consumer_contract.consumer.name} - #{consumer_contract.provider.name}#{file_extension}"
22
- end
23
-
24
- private
25
-
26
- attr_reader :dir, :consumer_contract, :consumer_contract_renderer, :file_extension
27
-
28
- def path
29
- File.join(dir, name)
30
- end
31
-
32
- def doc_file_contents
33
- consumer_contract_renderer.call(consumer_contract)
34
- end
35
-
36
- end
37
- end
38
- end
@@ -1,11 +0,0 @@
1
- module Pact
2
- module Doc
3
- class Generate
4
-
5
- def self.call pact_dir = Pact.configuration.pact_dir, doc_dir = Pact.configuration.doc_dir, doc_generators = Pact.configuration.doc_generators
6
- doc_generators.each{| doc_generator| doc_generator.call pact_dir, doc_dir }
7
- end
8
-
9
- end
10
- end
11
- end