pact_broker 2.38.1 → 2.39.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +5 -5
  2. data/.github/ISSUE_TEMPLATE.md +2 -2
  3. data/CHANGELOG.md +45 -0
  4. data/DEVELOPER_DOCUMENTATION.md +9 -0
  5. data/Dockerfile +2 -0
  6. data/Gemfile +1 -1
  7. data/README.md +10 -3
  8. data/config/database.yml +10 -2
  9. data/docs/images/Pactflow logo - black small.png +0 -0
  10. data/lib/pact_broker/api.rb +5 -2
  11. data/lib/pact_broker/api/contracts/verifiable_pacts_query_schema.rb +41 -0
  12. data/lib/pact_broker/api/decorators/dashboard_decorator.rb +1 -1
  13. data/lib/pact_broker/api/decorators/dashboard_text_decorator.rb +1 -1
  14. data/lib/pact_broker/api/decorators/integration_decorator.rb +2 -0
  15. data/lib/pact_broker/api/decorators/verifiable_pact_decorator.rb +34 -0
  16. data/lib/pact_broker/api/decorators/verifiable_pacts_query_decorator.rb +27 -0
  17. data/lib/pact_broker/api/resources/badge.rb +4 -4
  18. data/lib/pact_broker/api/resources/can_i_deploy.rb +41 -0
  19. data/lib/pact_broker/api/resources/dashboard.rb +1 -1
  20. data/lib/pact_broker/api/resources/index.rb +15 -4
  21. data/lib/pact_broker/api/resources/latest_pact.rb +10 -4
  22. data/lib/pact_broker/api/resources/latest_verification_for_latest_pact.rb +19 -0
  23. data/lib/pact_broker/api/resources/pact.rb +5 -4
  24. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +54 -0
  25. data/lib/pact_broker/api/resources/verification.rb +6 -1
  26. data/lib/pact_broker/badges/service.rb +11 -11
  27. data/lib/pact_broker/db/clean.rb +72 -12
  28. data/lib/pact_broker/doc/controllers/app.rb +3 -1
  29. data/lib/pact_broker/doc/views/can-i-deploy.markdown +14 -0
  30. data/lib/pact_broker/doc/views/index/pacticipant-version-tag.markdown +8 -0
  31. data/lib/pact_broker/doc/views/integrations.markdown +1 -1
  32. data/lib/pact_broker/doc/views/matrix.markdown +7 -0
  33. data/lib/pact_broker/doc/views/metrics.markdown +5 -0
  34. data/lib/pact_broker/domain/index_item.rb +3 -3
  35. data/lib/pact_broker/domain/pact.rb +28 -1
  36. data/lib/pact_broker/index/service.rb +38 -20
  37. data/lib/pact_broker/integrations/integration.rb +16 -0
  38. data/lib/pact_broker/integrations/service.rb +15 -1
  39. data/lib/pact_broker/matrix/can_i_deploy_query_schema.rb +25 -0
  40. data/lib/pact_broker/matrix/parse_can_i_deploy_query.rb +29 -0
  41. data/lib/pact_broker/matrix/quick_row.rb +255 -0
  42. data/lib/pact_broker/matrix/repository.rb +5 -17
  43. data/lib/pact_broker/matrix/row.rb +0 -1
  44. data/lib/pact_broker/metrics/service.rb +36 -2
  45. data/lib/pact_broker/pacts/all_pact_publications.rb +3 -2
  46. data/lib/pact_broker/pacts/head_pact.rb +30 -0
  47. data/lib/pact_broker/pacts/latest_pact_publications.rb +8 -1
  48. data/lib/pact_broker/pacts/latest_tagged_pact_publications.rb +5 -1
  49. data/lib/pact_broker/pacts/pact_publication.rb +2 -1
  50. data/lib/pact_broker/pacts/pact_version.rb +20 -0
  51. data/lib/pact_broker/pacts/repository.rb +33 -4
  52. data/lib/pact_broker/pacts/service.rb +15 -2
  53. data/lib/pact_broker/pacts/squash_pacts_for_verification.rb +37 -0
  54. data/lib/pact_broker/pacts/verifiable_pact.rb +30 -0
  55. data/lib/pact_broker/pacts/verifiable_pact_messages.rb +75 -0
  56. data/lib/pact_broker/test/test_data_builder.rb +30 -2
  57. data/lib/pact_broker/ui/view_models/index_item.rb +4 -4
  58. data/lib/pact_broker/ui/view_models/matrix_line.rb +1 -1
  59. data/lib/pact_broker/ui/views/index/show-with-tags.haml +1 -1
  60. data/lib/pact_broker/ui/views/index/show.haml +1 -1
  61. data/lib/pact_broker/ui/views/matrix/show.haml +2 -2
  62. data/lib/pact_broker/verifications/{verification_status.rb → pseudo_branch_status.rb} +10 -4
  63. data/lib/pact_broker/version.rb +1 -1
  64. data/script/docker/mysql-db-start.sh +10 -0
  65. data/spec/features/can_i_deploy_spec.rb +31 -0
  66. data/spec/features/get_latest_verification_for_pact_spec.rb +17 -0
  67. data/spec/features/get_provider_pacts_for_verification_spec.rb +39 -0
  68. data/spec/features/metrics_spec.rb +1 -1
  69. data/spec/features/pending_pacts_spec.rb +109 -0
  70. data/spec/integration/ui/matrix_spec.rb +30 -0
  71. data/spec/lib/pact_broker/api/contracts/verifiable_pacts_query_schema_spec.rb +62 -0
  72. data/spec/lib/pact_broker/api/decorators/dashboard_decorator_spec.rb +1 -1
  73. data/spec/lib/pact_broker/api/decorators/integration_decorator_spec.rb +5 -1
  74. data/spec/lib/pact_broker/api/decorators/verifiable_pact_decorator_spec.rb +30 -5
  75. data/spec/lib/pact_broker/api/decorators/verifiable_pacts_query_decorator_spec.rb +46 -0
  76. data/spec/lib/pact_broker/api/resources/badge_spec.rb +3 -3
  77. data/spec/lib/pact_broker/api/resources/provider_pacts_for_verification_spec.rb +35 -0
  78. data/spec/lib/pact_broker/api/resources/verification_spec.rb +18 -0
  79. data/spec/lib/pact_broker/badges/service_spec.rb +18 -18
  80. data/spec/lib/pact_broker/db/clean_spec.rb +69 -8
  81. data/spec/lib/pact_broker/doc/coverage_spec.rb +8 -2
  82. data/spec/lib/pact_broker/index/service_spec.rb +28 -14
  83. data/spec/lib/pact_broker/integrations/integration_spec.rb +60 -0
  84. data/spec/lib/pact_broker/integrations/service_spec.rb +27 -0
  85. data/spec/lib/pact_broker/matrix/quick_row_spec.rb +31 -0
  86. data/spec/lib/pact_broker/matrix/repository_spec.rb +8 -8
  87. data/spec/lib/pact_broker/matrix/service_spec.rb +4 -4
  88. data/spec/lib/pact_broker/metrics/service_spec.rb +56 -0
  89. data/spec/lib/pact_broker/pacts/pact_version_spec.rb +69 -0
  90. data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +65 -0
  91. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +54 -24
  92. data/spec/lib/pact_broker/pacts/service_find_for_verification_spec.rb +51 -0
  93. data/spec/lib/pact_broker/pacts/squash_pacts_for_verification_spec.rb +92 -0
  94. data/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb +93 -0
  95. data/spec/lib/pact_broker/pacts/verifiable_pact_spec.rb +0 -0
  96. data/spec/lib/pact_broker/ui/view_models/index_item_spec.rb +10 -10
  97. data/spec/lib/pact_broker/verifications/{verification_status_spec.rb → pseudo_branch_status_spec.rb} +4 -4
  98. data/spec/support/shared_examples_for_responses.rb +7 -1
  99. metadata +56 -13
  100. data/lib/pact_broker/api/resources/pending_provider_pacts.rb +0 -21
  101. data/spec/features/get_pacts_to_verify_spec.rb +0 -41
  102. data/spec/features/get_wip_provider_pacts_spec.rb +0 -26
  103. data/spec/lib/pact_broker/api/resources/pending_provider_pacts_spec.rb +0 -34
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/repositories/helpers'
2
2
  require 'pact_broker/matrix/row'
3
+ require 'pact_broker/matrix/quick_row'
3
4
  require 'pact_broker/matrix/head_row'
4
5
  require 'pact_broker/error'
5
6
  require 'pact_broker/matrix/query_results'
@@ -80,7 +81,7 @@ module PactBroker
80
81
  # If two or more are specified, just return the integrations that involve the specified pacticipants
81
82
  def find_integrations_for_specified_selectors(resolved_specified_selectors)
82
83
  specified_pacticipant_names = resolved_specified_selectors.collect(&:pacticipant_name)
83
- Row
84
+ QuickRow
84
85
  .select(:consumer_name, :consumer_id, :provider_name, :provider_id)
85
86
  .matching_selectors(resolved_specified_selectors)
86
87
  .distinct
@@ -116,28 +117,15 @@ module PactBroker
116
117
 
117
118
  # The group with the nil provider_version_numbers will be the results of the left outer join
118
119
  # that don't have verifications, so we need to include them all.
119
- remove_overwritten_revisions(lines).group_by{|line| group_by_columns.collect{|key| line.send(key) }}
120
+ lines.group_by{|line| group_by_columns.collect{|key| line.send(key) }}
120
121
  .values
121
122
  .collect{ | lines | lines.first.provider_version_number.nil? ? lines : lines.first }
122
123
  .flatten
123
124
  end
124
125
 
125
- # It would be nicer to do this in the SQL, but it requires time that I don't have at the moment
126
- def remove_overwritten_revisions lines
127
- latest_revisions_keys = {}
128
- latest_revisions = []
129
- lines.each do | line |
130
- key = "#{line.consumer_name}-#{line.provider_name}-#{line.consumer_version_number}"
131
- if !latest_revisions_keys.key?(key) || latest_revisions_keys[key] == line.pact_revision_number
132
- latest_revisions << line
133
- latest_revisions_keys[key] ||= line.pact_revision_number
134
- end
135
- end
136
- latest_revisions
137
- end
138
-
139
126
  def query_matrix selectors, options
140
- query = Row.select_all.matching_selectors(selectors)
127
+ query = options[:latestby] ? QuickRow.eager_all_the_things : Row
128
+ query = query.select_all.matching_selectors(selectors)
141
129
  query = query.limit(options[:limit]) if options[:limit]
142
130
  query
143
131
  .order_by_names_ascending_most_recent_first
@@ -7,7 +7,6 @@ require 'pact_broker/verifications/latest_verification_for_consumer_and_provider
7
7
 
8
8
  module PactBroker
9
9
  module Matrix
10
-
11
10
  class Row < Sequel::Model(:matrix)
12
11
 
13
12
  # Used when using table_print to output query results
@@ -8,6 +8,8 @@ require 'pact_broker/api/decorators/format_date_time'
8
8
  require 'pact_broker/webhooks/webhook'
9
9
  require 'pact_broker/webhooks/triggered_webhook'
10
10
  require 'pact_broker/webhooks/execution'
11
+ require 'pact_broker/matrix/row'
12
+ require 'pact_broker/matrix/head_row'
11
13
 
12
14
  module PactBroker
13
15
  module Metrics
@@ -32,10 +34,19 @@ module PactBroker
32
34
  pactVersions: {
33
35
  count: PactBroker::Pacts::PactVersion.count
34
36
  },
37
+ pactRevisionsPerConsumerVersion: {
38
+ distribution: pact_revision_counts
39
+ },
35
40
  verificationResults: {
36
41
  count: PactBroker::Domain::Verification.count,
37
- first: format_date_time(PactBroker::Domain::Verification.order(:id).first.created_at),
38
- last: format_date_time(PactBroker::Domain::Verification.order(:id).last.created_at)
42
+ successCount: PactBroker::Domain::Verification.where(success: true).count,
43
+ failureCount: PactBroker::Domain::Verification.where(success: false).count,
44
+ distinctCount: PactBroker::Domain::Verification.distinct.select(:provider_version_id, :pact_version_id, :success).count,
45
+ first: format_date_time(PactBroker::Domain::Verification.order(:id).first&.created_at),
46
+ last: format_date_time(PactBroker::Domain::Verification.order(:id).last&.created_at),
47
+ },
48
+ verificationResultsPerPactVersion: {
49
+ distribution: verification_distribution
39
50
  },
40
51
  pacticipantVersions: {
41
52
  count: PactBroker::Domain::Version.count
@@ -48,9 +59,32 @@ module PactBroker
48
59
  },
49
60
  webhookExecutions: {
50
61
  count: PactBroker::Webhooks::Execution.count
62
+ },
63
+ matrix: {
64
+ count: PactBroker::Matrix::Row.count
65
+ },
66
+ headMatrix: {
67
+ count: PactBroker::Matrix::HeadRow.count
51
68
  }
52
69
  }
53
70
  end
71
+
72
+ def pact_revision_counts
73
+ query = "select revision_count as number_of_revisions, count(consumer_version_id) as consumer_version_count
74
+ from (select consumer_version_id, count(*) as revision_count from pact_publications group by consumer_version_id) foo
75
+ group by revision_count
76
+ order by 1"
77
+ PactBroker::Pacts::PactPublication.db[query].all.each_with_object({}) { |row, hash| hash[row[:number_of_revisions]] = row[:consumer_version_count] }
78
+ end
79
+
80
+ #
81
+ def verification_distribution
82
+ query = "select verification_count as number_of_verifications, count(*) as pact_version_count
83
+ from (select pact_version_id, count(*) as verification_count from verifications group by pact_version_id) foo
84
+ group by verification_count
85
+ order by 1"
86
+ PactBroker::Pacts::PactPublication.db[query].all.each_with_object({}) { |row, hash| hash[row[:number_of_verifications]] = row[:pact_version_count] }
87
+ end
54
88
  end
55
89
  end
56
90
  end
@@ -6,7 +6,6 @@ require 'pact_broker/repositories/helpers'
6
6
 
7
7
  module PactBroker
8
8
  module Pacts
9
-
10
9
  class AllPactPublications < Sequel::Model(:all_pact_publications)
11
10
 
12
11
  extend Forwardable
@@ -16,6 +15,7 @@ module PactBroker
16
15
  set_primary_key :id
17
16
  associate(:one_to_many, :tags, :class => "PactBroker::Domain::Tag", :reciprocal => :version, :key => :version_id, :primary_key => :consumer_version_id)
18
17
  associate(:many_to_one, :pact_version, :key => :pact_version_id, :primary_key => :id)
18
+ associate(:many_to_one, :latest_verification, :class => "PactBroker::Verifications::LatestVerificationForPactVersion", key: :pact_version_id, primary_key: :pact_version_id)
19
19
 
20
20
  dataset_module do
21
21
  include PactBroker::Repositories::Helpers
@@ -102,7 +102,8 @@ module PactBroker
102
102
  revision_number: revision_number,
103
103
  pact_version_sha: pact_version_sha,
104
104
  created_at: created_at,
105
- head_tag_names: head_tag_names)
105
+ head_tag_names: head_tag_names,
106
+ db_model: self)
106
107
  end
107
108
 
108
109
  def head_tag_names
@@ -0,0 +1,30 @@
1
+ require 'delegate'
2
+
3
+ # A head pact is the pact for the latest consumer version with the specified tag
4
+ # (ignoring later versions that might have the specified tag but no pact)
5
+
6
+ module PactBroker
7
+ module Pacts
8
+ class HeadPact < SimpleDelegator
9
+ attr_reader :tag, :consumer_version_number
10
+
11
+ def initialize(pact, consumer_version_number, tag)
12
+ super(pact)
13
+ @consumer_version_number = consumer_version_number
14
+ @tag = tag
15
+ end
16
+
17
+ # The underlying pact publication may well be the overall latest as well, but
18
+ # this row does not know that, as there will be a row with a nil tag
19
+ # if it is the overall latest as well as a row with the
20
+ # tag set, as the data is denormalised in the LatestTaggedPactPublications table.
21
+ def overall_latest?
22
+ tag.nil?
23
+ end
24
+
25
+ def pact
26
+ __getobj__()
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,12 +1,19 @@
1
1
  require 'pact_broker/pacts/latest_pact_publications_by_consumer_version'
2
+ require 'pact_broker/pacts/head_pact'
2
3
 
3
4
  module PactBroker
4
5
  module Pacts
5
6
 
7
+ # latest pact for each consumer/provider pair
6
8
  class LatestPactPublications < LatestPactPublicationsByConsumerVersion
7
9
  set_dataset(:latest_pact_publications)
8
- end
9
10
 
11
+ # This pact may well be the latest for certain tags, but in this query
12
+ # we don't know what they are
13
+ def to_domain
14
+ HeadPact.new(super, consumer_version_number, nil)
15
+ end
16
+ end
10
17
  end
11
18
  end
12
19
 
@@ -1,12 +1,16 @@
1
1
  require 'pact_broker/pacts/latest_pact_publications_by_consumer_version'
2
+ require 'pact_broker/pacts/head_pact'
2
3
 
3
4
  module PactBroker
4
5
  module Pacts
5
6
 
6
7
  class LatestTaggedPactPublications < LatestPactPublicationsByConsumerVersion
7
8
  set_dataset(:latest_tagged_pact_publications)
8
- end
9
9
 
10
+ def to_domain
11
+ HeadPact.new(super, consumer_version_number, tag_name)
12
+ end
13
+ end
10
14
  end
11
15
  end
12
16
 
@@ -57,7 +57,8 @@ module PactBroker
57
57
  pact_version_sha: pact_version.sha,
58
58
  latest_verification: latest_verification,
59
59
  created_at: created_at,
60
- head_tag_names: head_tag_names
60
+ head_tag_names: head_tag_names,
61
+ db_model: self
61
62
  )
62
63
  end
63
64
 
@@ -50,6 +50,26 @@ module PactBroker
50
50
  latest_consumer_version.number
51
51
  end
52
52
 
53
+ def select_provider_tags_with_successful_verifications(tags)
54
+ tags.select do | tag |
55
+ PactVersion.where(Sequel[:pact_versions][:id] => id)
56
+ .join(:verifications, Sequel[:verifications][:pact_version_id] => Sequel[:pact_versions][:id])
57
+ .join(:versions, Sequel[:versions][:id] => Sequel[:verifications][:provider_version_id])
58
+ .join(:tags, Sequel[:tags][:version_id] => Sequel[:versions][:id])
59
+ .where(Sequel[:tags][:name] => tag)
60
+ .where(Sequel[:verifications][:success] => true)
61
+ .any?
62
+ end
63
+ end
64
+
65
+ def verified_successfully_by_any_provider_version?
66
+ PactVersion.where(Sequel[:pact_versions][:id] => id)
67
+ .join(:verifications, Sequel[:verifications][:pact_version_id] => Sequel[:pact_versions][:id])
68
+ .join(:versions, Sequel[:versions][:id] => Sequel[:verifications][:provider_version_id])
69
+ .where(Sequel[:verifications][:success] => true)
70
+ .any?
71
+ end
72
+
53
73
  def upsert
54
74
  self.class.upsert(to_hash, [:consumer_id, :provider_id, :sha])
55
75
  end
@@ -12,6 +12,7 @@ require 'pact_broker/domain'
12
12
  require 'pact_broker/pacts/parse'
13
13
  require 'pact_broker/matrix/head_row'
14
14
  require 'pact_broker/pacts/latest_pact_publication_id_by_consumer_version'
15
+ require 'pact_broker/pacts/verifiable_pact'
15
16
 
16
17
  module PactBroker
17
18
  module Pacts
@@ -124,10 +125,30 @@ module PactBroker
124
125
  end
125
126
  end
126
127
 
127
- def find_pending_pact_versions_for_provider provider_name
128
- provider_id = pacticipant_repository.find_by_name(provider_name).id
129
- pact_publication_ids = PactBroker::Matrix::HeadRow.where(provider_id: provider_id).exclude(success: true).select_for_subquery(:pact_publication_id)
130
- AllPactPublications.where(id: pact_publication_ids).order_ignore_case(:consumer_name).order_append(:consumer_version_order).collect(&:to_domain)
128
+ def find_wip_pact_versions_for_provider provider_name, provider_tags = []
129
+ return [] if provider_tags.empty?
130
+ successfully_verified_pact_publication_ids_for_each_tag = provider_tags.collect do | provider_tag |
131
+ ids = LatestTaggedPactPublications
132
+ .join(:verifications, { pact_version_id: :pact_version_id })
133
+ .join(:tags, { Sequel[:verifications][:provider_version_id] => Sequel[:provider_tags][:version_id] }, {table_alias: :provider_tags})
134
+ .where(Sequel[:provider_tags][:name] => provider_tag)
135
+ .provider(provider_name)
136
+ .where(Sequel[:verifications][:success] => true)
137
+ .select(Sequel[:latest_tagged_pact_publications][:id].as(:id))
138
+ .collect(&:id)
139
+ [provider_tag, ids]
140
+ end
141
+
142
+ successfully_verified_pact_publication_ids_for_all_tags = successfully_verified_pact_publication_ids_for_each_tag.collect(&:last).reduce(:&)
143
+ pact_publication_ids = LatestTaggedPactPublications.provider(provider_name).exclude(id: successfully_verified_pact_publication_ids_for_all_tags).select_for_subquery(:id)
144
+
145
+ pacts = AllPactPublications.where(id: pact_publication_ids).order_ignore_case(:consumer_name).order_append(:consumer_version_order).collect(&:to_domain)
146
+ pacts.collect do | pact|
147
+ pending_tags = successfully_verified_pact_publication_ids_for_each_tag.select do | (provider_tag, pact_publication_ids) |
148
+ !pact_publication_ids.include?(pact.id)
149
+ end.collect(&:first)
150
+ VerifiablePact.new(pact, true, pending_tags, [], pact.consumer_version_tag_names)
151
+ end
131
152
  end
132
153
 
133
154
  def find_pact_versions_for_provider provider_name, tag = nil
@@ -263,6 +284,14 @@ module PactBroker
263
284
  end
264
285
  end
265
286
 
287
+ # Returns a list of Domain::Pact objects the represent pact publications
288
+ def find_for_verification(provider_name, consumer_version_selectors)
289
+ latest_tags = consumer_version_selectors.any? ?
290
+ consumer_version_selectors.select(&:latest).collect(&:tag) :
291
+ nil
292
+ find_latest_pact_versions_for_provider(provider_name, latest_tags)
293
+ end
294
+
266
295
  private
267
296
 
268
297
  def find_previous_distinct_pact_by_sha pact
@@ -2,6 +2,8 @@ require 'pact_broker/repositories'
2
2
  require 'pact_broker/services'
3
3
  require 'pact_broker/logging'
4
4
  require 'pact_broker/pacts/merger'
5
+ require 'pact_broker/pacts/verifiable_pact'
6
+ require 'pact_broker/pacts/squash_pacts_for_verification'
5
7
 
6
8
  module PactBroker
7
9
  module Pacts
@@ -12,6 +14,7 @@ module PactBroker
12
14
  extend PactBroker::Repositories
13
15
  extend PactBroker::Services
14
16
  include PactBroker::Logging
17
+ extend SquashPactsForVerification
15
18
 
16
19
  def find_latest_pact params
17
20
  pact_repository.find_latest_pact(params[:consumer_name], params[:provider_name], params[:tag])
@@ -80,8 +83,8 @@ module PactBroker
80
83
  pact_repository.find_latest_pact_versions_for_provider provider_name, options[:tag]
81
84
  end
82
85
 
83
- def find_pending_pact_versions_for_provider provider_name
84
- pact_repository.find_pending_pact_versions_for_provider provider_name
86
+ def find_wip_pact_versions_for_provider provider_name
87
+ pact_repository.find_wip_pact_versions_for_provider provider_name
85
88
  end
86
89
 
87
90
  def find_pact_versions_for_provider provider_name, options = {}
@@ -110,6 +113,16 @@ module PactBroker
110
113
  distinct
111
114
  end
112
115
 
116
+ def find_for_verification(provider_name, provider_version_tags, consumer_version_selectors)
117
+ pact_repository
118
+ .find_for_verification(provider_name, consumer_version_selectors)
119
+ .group_by(&:pact_version_sha)
120
+ .values
121
+ .collect do | head_pacts |
122
+ squash_pacts_for_verification(provider_version_tags, head_pacts)
123
+ end
124
+ end
125
+
113
126
  private
114
127
 
115
128
  # Overwriting an existing pact with the same consumer/provider/consumer version number
@@ -0,0 +1,37 @@
1
+ # All of these pacts have the same underlying pact_version_sha (content)
2
+ # No point verifying them multiple times, so squash all the relevant info into one
3
+ # "verifiable pact"
4
+
5
+ module PactBroker
6
+ module Pacts
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?
15
+ else
16
+ pending = domain_pact.pending?
17
+ 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
+ end
31
+
32
+ def squash_pacts_for_verification(*args)
33
+ SquashPactsForVerification.call(*args)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,30 @@
1
+ require 'delegate'
2
+
3
+ module PactBroker
4
+ module Pacts
5
+ class VerifiablePact < SimpleDelegator
6
+ attr_reader :pending, :pending_provider_tags, :non_pending_provider_tags, :head_consumer_tags
7
+
8
+ def initialize(pact, pending, pending_provider_tags = [], non_pending_provider_tags = [], head_consumer_tags = [], overall_latest = false)
9
+ super(pact)
10
+ @pending = pending
11
+ @pending_provider_tags = pending_provider_tags
12
+ @non_pending_provider_tags = non_pending_provider_tags
13
+ @head_consumer_tags = head_consumer_tags
14
+ @overall_latest = overall_latest
15
+ end
16
+
17
+ def consumer_tags
18
+ head_consumer_tags
19
+ end
20
+
21
+ def overall_latest?
22
+ @overall_latest
23
+ end
24
+
25
+ def pending?
26
+ pending
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,75 @@
1
+ module PactBroker
2
+ module Pacts
3
+ class VerifiablePactMessages
4
+ extend Forwardable
5
+
6
+ READ_MORE = "Read more at https://pact.io/pending"
7
+
8
+ delegate [:consumer_name, :provider_name, :head_consumer_tags, :pending_provider_tags, :non_pending_provider_tags, :pending?] => :verifiable_pact
9
+
10
+ def initialize(verifiable_pact)
11
+ @verifiable_pact = verifiable_pact
12
+ end
13
+
14
+ def inclusion_reason
15
+ if head_consumer_tags.any?
16
+ version_text = head_consumer_tags.size == 1 ? "version" : "versions"
17
+ "This pact is being verified because it is the pact for the latest #{version_text} of Foo tagged with #{joined_head_consumer_tags}"
18
+ else
19
+ "This pact is being verified because it is the latest pact between #{consumer_name} and #{provider_name}."
20
+ end
21
+ end
22
+
23
+ def pending_reason
24
+ if pending?
25
+ "This pact is in pending state because it has not yet been successfully verified by #{pending_provider_tags_description}. If this verification fails, it will not cause the overall build to fail. #{READ_MORE}"
26
+ else
27
+ "This pact has previously been successfully verified by #{non_pending_provider_tags_description}. If this verification fails, it will fail the build. #{READ_MORE}"
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :verifiable_pact
34
+
35
+ def join(list, last_joiner = " and ")
36
+ quoted_list = list.collect { | tag | "'#{tag}'" }
37
+ comma_joined = quoted_list[0..-3] || []
38
+ and_joined = quoted_list[-2..-1] || quoted_list
39
+ if comma_joined.any?
40
+ "#{comma_joined.join(', ')}, #{and_joined.join(last_joiner)}"
41
+ else
42
+ and_joined.join(last_joiner)
43
+ end
44
+ end
45
+
46
+ def joined_head_consumer_tags
47
+ join(head_consumer_tags) + same_content_note
48
+ end
49
+
50
+ def same_content_note
51
+ case head_consumer_tags.size
52
+ when 1 then ""
53
+ when 2 then " (both have the same content)"
54
+ else " (all have the same content)"
55
+ end
56
+ end
57
+
58
+ def pending_provider_tags_description
59
+ case pending_provider_tags.size
60
+ when 0 then provider_name
61
+ when 1 then "any version of #{provider_name} with tag '#{pending_provider_tags.first}'"
62
+ else "any versions of #{provider_name} with tag #{join(pending_provider_tags)}"
63
+ end
64
+ end
65
+
66
+ def non_pending_provider_tags_description
67
+ case non_pending_provider_tags.size
68
+ when 0 then provider_name
69
+ when 1 then "a version of #{provider_name} with tag '#{non_pending_provider_tags.first}'"
70
+ else "a version of #{provider_name} with tag #{join(non_pending_provider_tags)}"
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end