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
@@ -13,51 +13,41 @@ module PactBroker
13
13
  extend PactBroker::Services
14
14
  extend PactBroker::Logging
15
15
 
16
+ # This method provides data for both the OSS server side rendered index (with and without tags)
17
+ # and the Pactflow UI. It really needs to be broken into to separate methods, as it's getting too messy
18
+ # supporting both
19
+
16
20
  def self.find_index_items options = {}
17
21
  rows = PactBroker::Matrix::HeadRow
18
22
  .select_all_qualified
19
23
  .eager(:latest_triggered_webhooks)
20
24
  .eager(:webhooks)
21
25
 
22
- rows = rows.consumer(options[:consumer_name]) if options[:consumer_name]
23
- rows = rows.provider(options[:provider_name]) if options[:provider_name]
24
-
25
26
  if !options[:tags]
27
+ # server side rendered index page without tags
26
28
  rows = rows.where(consumer_version_tag_name: nil)
27
29
  else
30
+ # server side rendered index page with tags=true or tags[]=a&tags=[]b
28
31
  if options[:tags].is_a?(Array)
29
32
  rows = rows.where(consumer_version_tag_name: options[:tags]).or(consumer_version_tag_name: nil)
30
33
  end
31
34
  rows = rows.eager(:consumer_version_tags)
32
- .eager(:provider_version_tags)
33
- .eager(:latest_verification_for_consumer_version_tag)
34
- .eager(:latest_verification_for_consumer_and_provider)
35
+ .eager(:provider_version_tags)
36
+ .eager(:latest_verification_for_consumer_version_tag)
37
+ .eager(:latest_verification_for_consumer_and_provider)
35
38
  end
36
39
  rows = rows.all.group_by(&:pact_publication_id).values.collect{ | rows| Matrix::AggregatedRow.new(rows) }
37
40
 
38
41
 
39
42
 
40
43
  rows.sort.collect do | row |
41
- # The concept of "stale" (the pact used to be verified but then it changed and we haven't got
42
- # a new verification result yet) only really make sense if we're trying to summarise
43
- # the latest state of an integration. Once we start showing multiple pacts for each
44
- # integration (ie. the latest for each tag) then each pact version is either verified,
45
- # or it's not verified.
46
- # For backwards compatiblity with the existing UI, don't change the 'stale' concept for the OSS
47
- # UI - just ensure we don't use it for the new dashboard endpoint with the consumer/provider specified.
48
- latest_verification = if options[:dashboard]
49
- row.latest_verification_for_pact_version
50
- else
51
- row.latest_verification_for_pseudo_branch
52
- end
53
-
54
44
  # TODO simplify. Do we really need 3 layers of abstraction?
55
45
  PactBroker::Domain::IndexItem.create(
56
46
  row.consumer,
57
47
  row.provider,
58
48
  row.pact,
59
49
  row.overall_latest?,
60
- latest_verification,
50
+ row.latest_verification_for_pseudo_branch,
61
51
  row.webhooks,
62
52
  row.latest_triggered_webhooks,
63
53
  options[:tags] ? row.consumer_head_tag_names : [],
@@ -65,6 +55,34 @@ module PactBroker
65
55
  )
66
56
  end
67
57
  end
58
+
59
+ def self.find_index_items_for_api(consumer_name: nil, provider_name: nil, **ignored)
60
+ rows = PactBroker::Matrix::HeadRow
61
+ .eager(:consumer_version_tags)
62
+ .eager(:provider_version_tags)
63
+ .select_all_qualified
64
+
65
+ rows = rows.consumer(consumer_name) if consumer_name
66
+ rows = rows.provider(provider_name) if provider_name
67
+
68
+ rows = rows.all.group_by(&:pact_publication_id).values.collect{ | rows| Matrix::AggregatedRow.new(rows) }
69
+
70
+ rows.sort.collect do | row |
71
+ # TODO separate this model from IndexItem
72
+ # webhook status not currently displayed in Pactflow UI, so don't query for it.
73
+ PactBroker::Domain::IndexItem.create(
74
+ row.consumer,
75
+ row.provider,
76
+ row.pact,
77
+ row.overall_latest?,
78
+ row.latest_verification_for_pact_version,
79
+ [],
80
+ [],
81
+ row.consumer_head_tag_names,
82
+ row.provider_version_tags.select(&:latest?)
83
+ )
84
+ end
85
+ end
68
86
  end
69
87
  end
70
88
  end
@@ -1,10 +1,26 @@
1
1
  require 'pact_broker/db'
2
+ require 'pact_broker/verifications/pseudo_branch_status'
3
+ require 'pact_broker/domain/verification'
2
4
 
3
5
  module PactBroker
4
6
  module Integrations
5
7
  class Integration < Sequel::Model
6
8
  associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
7
9
  associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
10
+ associate(:one_to_one, :latest_pact, :class => "PactBroker::Pacts::LatestPactPublications", key: [:consumer_id, :provider_id], primary_key: [:consumer_id, :provider_id])
11
+ associate(:one_to_one, :latest_verification, :class => "PactBroker::Verifications::LatestVerificationForConsumerAndProvider", key: [:consumer_id, :provider_id], primary_key: [:consumer_id, :provider_id])
12
+
13
+ def verification_status_for_latest_pact
14
+ @verification_status_for_latest_pact ||= PactBroker::Verifications::PseudoBranchStatus.new(latest_pact, latest_pact&.latest_verification)
15
+ end
16
+
17
+ def latest_pact_or_verification_publication_date
18
+ [latest_pact.created_at, latest_verification_publication_date].compact.max
19
+ end
20
+
21
+ def latest_verification_publication_date
22
+ latest_verification&.execution_date
23
+ end
8
24
  end
9
25
  end
10
26
  end
@@ -11,7 +11,21 @@ module PactBroker
11
11
  include PactBroker::Logging
12
12
 
13
13
  def self.find_all
14
- PactBroker::Integrations::Integration.eager(:consumer).eager(:provider).all
14
+ # The only reason the pact_version needs to be loaded is that
15
+ # the Verification::PseudoBranchStatus uses it to determine if
16
+ # the pseudo branch is 'stale'.
17
+ # Because this is the status for a pact, and not a pseudo branch,
18
+ # the status can never be 'stale',
19
+ # so it would be better to create a Verification::PactStatus class
20
+ # that doesn't have the 'stale' logic in it.
21
+ # Then we can remove the eager loading of the pact_version
22
+ PactBroker::Integrations::Integration
23
+ .eager(:consumer)
24
+ .eager(:provider)
25
+ .eager(latest_pact: [:latest_verification, :pact_version])
26
+ .eager(:latest_verification)
27
+ .all
28
+ .sort { | a, b| b.latest_pact_or_verification_publication_date <=> a.latest_pact_or_verification_publication_date }
15
29
  end
16
30
 
17
31
  def self.delete(consumer_name, provider_name)
@@ -0,0 +1,25 @@
1
+ require 'dry-validation'
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Contracts
6
+ class CanIDeployQuerySchema
7
+ SCHEMA = Dry::Validation.Schema do
8
+ required(:pacticipant).filled(:str?)
9
+ required(:version).filled(:str?)
10
+ optional(:to).filled(:str?)
11
+ end
12
+
13
+ def self.call(params)
14
+ select_first_message(SCHEMA.call(params).messages(full: true))
15
+ end
16
+
17
+ def self.select_first_message(messages)
18
+ messages.each_with_object({}) do | (key, value), new_messages |
19
+ new_messages[key] = [value.first]
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,29 @@
1
+ require 'rack/utils'
2
+
3
+ module PactBroker
4
+ module Matrix
5
+ class ParseCanIDeployQuery
6
+ def self.call params
7
+ selector = {}
8
+ options = {
9
+ latestby: 'cvp',
10
+ latest: true
11
+ }
12
+
13
+ if params[:pacticipant].is_a?(String)
14
+ selector[:pacticipant_name] = params[:pacticipant]
15
+ end
16
+
17
+ if params[:version].is_a?(String)
18
+ selector[:pacticipant_version_number] = params[:version]
19
+ end
20
+
21
+ if params[:to].is_a?(String)
22
+ options[:tag] = params[:to]
23
+ end
24
+
25
+ return [selector], options
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,255 @@
1
+ =begin
2
+ The Matrix::Row is based on the matrix view which does a join of every pact/verification
3
+ and then selects the relevant ones.
4
+
5
+ The Matrix::QuickRow starts with the relevant rows, and builds the matrix query from that,
6
+ making it much quicker.
7
+
8
+ =end
9
+ require 'sequel'
10
+ require 'pact_broker/repositories/helpers'
11
+ require 'pact_broker/logging'
12
+ require 'pact_broker/pacts/pact_version'
13
+ require 'pact_broker/domain/pacticipant'
14
+ require 'pact_broker/domain/version'
15
+ require 'pact_broker/domain/verification'
16
+ require 'pact_broker/pacts/pact_publication'
17
+ require 'pact_broker/tags/tag_with_latest_flag'
18
+
19
+ module PactBroker
20
+ module Matrix
21
+ LV = :latest_verification_id_for_pact_version_and_provider_version
22
+ LP = :latest_pact_publication_ids_for_consumer_versions
23
+
24
+ CONSUMER_COLUMNS = [Sequel[:lp][:consumer_id], Sequel[:consumers][:name].as(:consumer_name), Sequel[:lp][:pact_publication_id], Sequel[:lp][:pact_version_id]]
25
+ PROVIDER_COLUMNS = [Sequel[:lp][:provider_id], Sequel[:providers][:name].as(:provider_name), Sequel[:lv][:verification_id]]
26
+ CONSUMER_VERSION_COLUMNS = [Sequel[:lp][:consumer_version_id], Sequel[:cv][:number].as(:consumer_version_number), Sequel[:cv][:order].as(:consumer_version_order)]
27
+ PROVIDER_VERSION_COLUMNS = [Sequel[:lv][:provider_version_id], Sequel[:pv][:number].as(:provider_version_number), Sequel[:pv][:order].as(:provider_version_order)]
28
+ ALL_COLUMNS = CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PROVIDER_COLUMNS + PROVIDER_VERSION_COLUMNS
29
+
30
+ LP_LV_JOIN = { Sequel[:lp][:pact_version_id] => Sequel[:lv][:pact_version_id] }
31
+ CONSUMER_JOIN = { Sequel[:lp][:consumer_id] => Sequel[:consumers][:id] }
32
+ PROVIDER_JOIN = { Sequel[:lp][:provider_id] => Sequel[:providers][:id] }
33
+ CONSUMER_VERSION_JOIN = { Sequel[:lp][:consumer_version_id] => Sequel[:cv][:id] }
34
+ PROVIDER_VERSION_JOIN = { Sequel[:lv][:provider_version_id] => Sequel[:pv][:id] }
35
+
36
+ RAW_QUERY = Sequel::Model.db[Sequel.as(LP, :lp)]
37
+ .select(*ALL_COLUMNS)
38
+ .left_outer_join(LV, LP_LV_JOIN, { table_alias: :lv } )
39
+ .join(:pacticipants, CONSUMER_JOIN, { table_alias: :consumers })
40
+ .join(:pacticipants, PROVIDER_JOIN, { table_alias: :providers })
41
+ .join(:versions, CONSUMER_VERSION_JOIN, { table_alias: :cv })
42
+ .left_outer_join(:versions, PROVIDER_VERSION_JOIN, { table_alias: :pv } )
43
+
44
+ ALIASED_QUERY = Sequel.as(RAW_QUERY, :smart_rows)
45
+
46
+ class QuickRow < Sequel::Model(ALIASED_QUERY)
47
+ CONSUMER_ID = Sequel[:smart_rows][:consumer_id]
48
+ PROVIDER_ID = Sequel[:smart_rows][:provider_id]
49
+ CONSUMER_VERSION_ID = Sequel[:smart_rows][:consumer_version_id]
50
+ PROVIDER_VERSION_ID = Sequel[:smart_rows][:provider_version_id]
51
+ PACT_PUBLICATION_ID = Sequel[:smart_rows][:pact_publication_id]
52
+ VERIFICATION_ID = Sequel[:smart_rows][:verification_id]
53
+
54
+ associate(:many_to_one, :pact_publication, :class => "PactBroker::Pacts::PactPublication", :key => :pact_publication_id, :primary_key => :id)
55
+ associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
56
+ associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
57
+ associate(:many_to_one, :consumer_version, :class => "PactBroker::Domain::Version", :key => :consumer_version_id, :primary_key => :id)
58
+ associate(:many_to_one, :provider_version, :class => "PactBroker::Domain::Version", :key => :provider_version_id, :primary_key => :id)
59
+ associate(:many_to_one, :pact_version, class: "PactBroker::Pacts::PactVersion", :key => :pact_version_id, :primary_key => :id)
60
+ associate(:many_to_one, :verification, class: "PactBroker::Domain::Verification", :key => :verification_id, :primary_key => :id)
61
+ associate(:one_to_many, :consumer_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :consumer_version_id, key: :version_id)
62
+ associate(:one_to_many, :provider_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :provider_version_id, key: :version_id)
63
+
64
+ dataset_module do
65
+ include PactBroker::Repositories::Helpers
66
+ include PactBroker::Logging
67
+
68
+ def consumer_id consumer_id
69
+ where(CONSUMER_ID => consumer_id)
70
+ end
71
+
72
+ def matching_selectors selectors
73
+ if selectors.size == 1
74
+ where_consumer_or_provider_is(selectors.first)
75
+ else
76
+ where_consumer_and_provider_in(selectors)
77
+ end
78
+ end
79
+
80
+ # find rows where (the consumer (and optional version) matches any of the selectors)
81
+ # AND
82
+ # the (provider (and optional version) matches any of the selectors OR the provider matches
83
+ # and the verification is missing (and hence the provider version is null))
84
+ def where_consumer_and_provider_in selectors
85
+ where{
86
+ Sequel.&(
87
+ Sequel.|(
88
+ *QueryHelper.consumer_and_maybe_consumer_version_match_any_selector(selectors)
89
+ ),
90
+ Sequel.|(
91
+ *QueryHelper.provider_and_maybe_provider_version_match_any_selector_or_verification_is_missing(selectors)
92
+ ),
93
+ QueryHelper.either_consumer_or_provider_was_specified_in_query(selectors)
94
+ )
95
+ }
96
+ end
97
+
98
+ # Can't access other dataset_module methods from inside the Sequel `where{ ... }` block, so make a private class
99
+ # with some helper methods
100
+ class QueryHelper
101
+ def self.consumer_and_maybe_consumer_version_match_any_selector(selectors)
102
+ selectors.collect { |s| consumer_and_maybe_consumer_version_match_selector(s) }
103
+ end
104
+
105
+ def self.consumer_and_maybe_consumer_version_match_selector(s)
106
+ if s[:pact_publication_ids]
107
+ Sequel.&(PACT_PUBLICATION_ID => s[:pact_publication_ids])
108
+ elsif s[:pacticipant_version_id]
109
+ Sequel.&(CONSUMER_ID => s[:pacticipant_id], CONSUMER_VERSION_ID => s[:pacticipant_version_id])
110
+ else
111
+ Sequel.&(CONSUMER_ID => s[:pacticipant_id])
112
+ end
113
+ end
114
+
115
+ def self.provider_and_maybe_provider_version_match_selector(s)
116
+ if s[:verification_ids]
117
+ Sequel.&(VERIFICATION_ID => s[:verification_ids])
118
+ elsif s[:pacticipant_version_id]
119
+ Sequel.&(PROVIDER_ID => s[:pacticipant_id], PROVIDER_VERSION_ID => s[:pacticipant_version_id])
120
+ else
121
+ Sequel.&(PROVIDER_ID => s[:pacticipant_id])
122
+ end
123
+ end
124
+
125
+ # if the pact for a consumer version has never been verified, it exists in the matrix as a row
126
+ # with a blank provider version id
127
+ def self.provider_verification_is_missing_for_matching_selector(s)
128
+ Sequel.&(PROVIDER_ID => s[:pacticipant_id], PROVIDER_VERSION_ID => nil)
129
+ end
130
+
131
+ def self.provider_and_maybe_provider_version_match_any_selector_or_verification_is_missing(selectors)
132
+ selectors.collect { |s|
133
+ provider_and_maybe_provider_version_match_selector(s)
134
+ } + selectors.collect { |s|
135
+ provider_verification_is_missing_for_matching_selector(s)
136
+ }
137
+ end
138
+
139
+ # Some selecters are specified in the query, others are implied (when only one pacticipant is specified,
140
+ # the integrations are automatically worked out, and the selectors for these are of type :implied )
141
+ # When there are 3 pacticipants that each have dependencies on each other (A->B, A->C, B->C), the query
142
+ # to deploy C (implied A, implied B, specified C) was returning the A->B row because it matched the
143
+ # implied selectors as well.
144
+ # This extra filter makes sure that every row that is returned actually matches one of the specified
145
+ # selectors.
146
+ def self.either_consumer_or_provider_was_specified_in_query(selectors)
147
+ specified_pacticipant_ids = selectors.select{ |s| s[:type] == :specified }.collect{ |s| s[:pacticipant_id] }
148
+ Sequel.|({ CONSUMER_ID => specified_pacticipant_ids } , { PROVIDER_ID => specified_pacticipant_ids })
149
+ end
150
+ end
151
+
152
+ def where_consumer_or_provider_is s
153
+ where{
154
+ Sequel.|(
155
+ s[:pacticipant_version_id] ? Sequel.&(CONSUMER_ID => s[:pacticipant_id], CONSUMER_VERSION_ID => s[:pacticipant_version_id]) : Sequel.&(CONSUMER_ID => s[:pacticipant_id]),
156
+ s[:pacticipant_version_id] ? Sequel.&(PROVIDER_ID => s[:pacticipant_id], PROVIDER_VERSION_ID => s[:pacticipant_version_id]) : Sequel.&(PROVIDER_ID => s[:pacticipant_id])
157
+ )
158
+ }
159
+ end
160
+
161
+ def order_by_names_ascending_most_recent_first
162
+ order(
163
+ Sequel.asc(:consumer_name),
164
+ Sequel.desc(:consumer_version_order),
165
+ Sequel.asc(:provider_name),
166
+ Sequel.desc(:provider_version_order),
167
+ Sequel.desc(:verification_id))
168
+ end
169
+
170
+ def eager_all_the_things
171
+ eager(:consumer)
172
+ .eager(:provider)
173
+ .eager(:consumer_version)
174
+ .eager(:provider_version)
175
+ .eager(:verification)
176
+ .eager(:pact_publication)
177
+ .eager(:pact_version)
178
+ end
179
+
180
+ end
181
+
182
+ def success
183
+ verification&.success
184
+ end
185
+
186
+ def pact_version_sha
187
+ pact_version.sha
188
+ end
189
+
190
+ def pact_revision_number
191
+ pact_publication.revision_number
192
+ end
193
+
194
+ def verification_number
195
+ verification&.number
196
+ end
197
+
198
+ def success
199
+ verification&.success
200
+ end
201
+
202
+ def pact_created_at
203
+ pact_publication.created_at
204
+ end
205
+
206
+ def verification_executed_at
207
+ verification.execution_date
208
+ end
209
+
210
+ # Add logic for ignoring case
211
+ def <=> other
212
+ comparisons = [
213
+ compare_name_asc(consumer_name, other.consumer_name),
214
+ compare_number_desc(consumer_version_order, other.consumer_version_order),
215
+ compare_number_desc(pact_revision_number, other.pact_revision_number),
216
+ compare_name_asc(provider_name, other.provider_name),
217
+ compare_number_desc(provider_version_order, other.provider_version_order),
218
+ compare_number_desc(verification_id, other.verification_id)
219
+ ]
220
+
221
+ comparisons.find{|c| c != 0 } || 0
222
+ end
223
+
224
+ def compare_name_asc name1, name2
225
+ name1 <=> name2
226
+ end
227
+
228
+ def to_s
229
+ "#{consumer_name} v#{consumer_version_number} #{provider_name} #{provider_version_number} #{success}"
230
+ end
231
+
232
+ def compare_number_desc number1, number2
233
+ if number1 && number2
234
+ number2 <=> number1
235
+ elsif number1
236
+ 1
237
+ else
238
+ -1
239
+ end
240
+ end
241
+
242
+ def eql?(obj)
243
+ (obj.class == model) && (obj.values == values)
244
+ end
245
+
246
+ def pacticipant_names
247
+ [consumer_name, provider_name]
248
+ end
249
+
250
+ def involves_pacticipant_with_name?(pacticipant_name)
251
+ pacticipant_name.include?(pacticipant_name)
252
+ end
253
+ end
254
+ end
255
+ end