pact_broker 2.42.0 → 2.43.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
  SHA1:
3
- metadata.gz: 1beb82704006c0aa9708a9b4e9af93075bfc3bf9
4
- data.tar.gz: 8cb7b7148ce8a3152d6ccbe35e49f3e646f5e628
3
+ metadata.gz: 452acf4f3b4042c53c9e91a793f558f39ad6677e
4
+ data.tar.gz: ffcc62ddc9991aed25c87cbdea9925e9471bc4f8
5
5
  SHA512:
6
- metadata.gz: b289766c4bc421c975fefc7a109f97eb6bc5f31eac99d1dd6e7754d7f2c7cf7f7dca21652d55d28a1e64d5eacd5f26e16f30ac1af3e2111eefc9a38bb42128f4
7
- data.tar.gz: d6ce416cd0133c2d8278e1addc4f99814a118d9a800a74d1937168ac132e9f7ff27c3a48d63866ffaac0ec0424bd433f468812695e133edbf7a6698edbc9ba09
6
+ metadata.gz: e843bb458d91149a64f50092fcc1d17e63ed8b57468c7c2293033947648233cc015a010d421f7386531e89e90eb7d9499bc8da177da81c5518ea1bb937305ef8
7
+ data.tar.gz: c4df7ee48ef81fdb78beb668267ad13fcf848916b047c6f848a65bcc4979e7fac851617e93fba857b2a63bd77545d08a614e46257ede2e39e821d937ac3e79ef
@@ -1,3 +1,24 @@
1
+ <a name="v2.43.0"></a>
2
+ ### v2.43.0 (2020-01-06)
3
+
4
+
5
+ #### Features
6
+
7
+ * support DELETE /integrations for deleting all integration related data at once (pacticipants, pacts, verifications and webhooks) ([d7e2ef27](/../../commit/d7e2ef27))
8
+ * optimise query to automatically determine integrations ([147cbfb6](/../../commit/147cbfb6))
9
+ * change badge timeout message from error to warning ([e34f5676](/../../commit/e34f5676))
10
+
11
+ * **matrix**
12
+ * optimise the query that determines the integrations ([704944b6](/../../commit/704944b6))
13
+ * attempt to optimise the query that determines the integrations ([afde01e1](/../../commit/afde01e1))
14
+
15
+
16
+ #### Bug Fixes
17
+
18
+ * update rack for https://github.com/advisories/GHSA-hrqr-hxpp-chr3 ([c9352fde](/../../commit/c9352fde))
19
+ * correctly identify missing verification for bi-directional pacts ([3577968a](/../../commit/3577968a))
20
+
21
+
1
22
  <a name="v2.42.0"></a>
2
23
  ### v2.42.0 (2019-12-05)
3
24
 
@@ -30,7 +30,7 @@ module PactBroker
30
30
  end
31
31
 
32
32
  def delete_resource
33
- integration_service.delete(consumer_name, provider_name)
33
+ integration_service.delete_all
34
34
  true
35
35
  end
36
36
  end
@@ -84,8 +84,11 @@ module PactBroker
84
84
  begin
85
85
  response = do_request(uri)
86
86
  response.code == '200' ? response.body : nil
87
+ rescue Net::OpenTimeout => e
88
+ logger.warn "Timeout retrieving badge from #{uri} #{e.class} - #{e.message}"
89
+ nil
87
90
  rescue StandardError => e
88
- logger.error "Error retrieving badge from #{uri} due to #{e.class} - #{e.message}"
91
+ log_error e, "Error retrieving badge from #{uri}"
89
92
  nil
90
93
  end
91
94
  end
@@ -0,0 +1,37 @@
1
+ require 'pact_broker/webhooks/execution'
2
+ require 'pact_broker/webhooks/triggered_webhook'
3
+ require 'pact_broker/webhooks/webhook'
4
+ require 'pact_broker/pacts/latest_pact_publication_id_by_consumer_version'
5
+ require 'pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version'
6
+ require 'pact_broker/pacts/pact_publication'
7
+ require 'pact_broker/pacts/pact_version'
8
+ require 'pact_broker/domain/verification'
9
+ require 'pact_broker/domain/tag'
10
+ require 'pact_broker/domain/version'
11
+ require 'pact_broker/domain/label'
12
+ require 'pact_broker/domain/pacticipant'
13
+
14
+ module PactBroker
15
+ INTEGRATIONS_TABLES = [
16
+ PactBroker::Webhooks::Execution,
17
+ PactBroker::Webhooks::TriggeredWebhook,
18
+ PactBroker::Webhooks::Webhook,
19
+ PactBroker::Pacts::LatestPactPublicationIdForConsumerVersion,
20
+ PactBroker::Verifications::LatestVerificationIdForPactVersionAndProviderVersion,
21
+ PactBroker::Domain::Verification,
22
+ PactBroker::Pacts::PactPublication,
23
+ PactBroker::Pacts::PactVersion,
24
+ PactBroker::Domain::Tag,
25
+ PactBroker::Domain::Version,
26
+ PactBroker::Domain::Label,
27
+ PactBroker::Domain::Pacticipant
28
+ ]
29
+
30
+ module DB
31
+ def self.each_integration_model
32
+ INTEGRATIONS_TABLES.each do | model |
33
+ yield model
34
+ end
35
+ end
36
+ end
37
+ end
@@ -2,6 +2,8 @@ require 'pact_broker/services'
2
2
  require 'pact_broker/repositories'
3
3
  require 'pact_broker/logging'
4
4
  require 'pact_broker/integrations/integration'
5
+ require 'pact_broker/db/models'
6
+ require 'pact_broker/repositories/helpers'
5
7
 
6
8
  module PactBroker
7
9
  module Integrations
@@ -41,6 +43,22 @@ module PactBroker
41
43
  pacticipant_service.delete_if_orphan(consumer)
42
44
  pacticipant_service.delete_if_orphan(provider) unless consumer == provider
43
45
  end
46
+
47
+ def self.delete_all
48
+ # TODO move all these into their own repositories
49
+ PactBroker::DB.each_integration_model do | model |
50
+ if PactBroker::Repositories::Helpers.postgres?
51
+ logger.info("Truncating ", model.table_name)
52
+ model.truncate(cascade: true)
53
+ else
54
+ logger.info("Deleting all from ", model.table_name)
55
+ # Mysql adapter needs to support cascade truncate
56
+ # https://travis-ci.org/pact-foundation/pact_broker/jobs/633050220#L841
57
+ # https://travis-ci.org/pact-foundation/pact_broker/jobs/633053228#L849
58
+ model.dataset.delete
59
+ end
60
+ end
61
+ end
44
62
  end
45
63
  end
46
64
  end
@@ -0,0 +1,78 @@
1
+ module PactBroker
2
+ module Matrix
3
+ class QueryBuilder
4
+ def self.provider_or_provider_version_matches(query_ids, qualifier = nil)
5
+ Sequel.|(*provider_or_provider_version_criteria(query_ids, qualifier))
6
+ end
7
+
8
+ def self.provider_or_provider_version_matches_or_pact_unverified(query_ids, qualifier = nil)
9
+ ors = provider_or_provider_version_criteria(query_ids, qualifier)
10
+
11
+ ors << {
12
+ qualify(:lp, :provider_id) => query_ids.all_pacticipant_ids,
13
+ qualify(qualifier, :provider_version_id) => nil
14
+ }
15
+ Sequel.|(*ors)
16
+ end
17
+
18
+ def self.provider_or_provider_version_criteria(query_ids, qualifier = nil)
19
+ ors = []
20
+ ors << { qualify(qualifier, :provider_version_id) => query_ids.pacticipant_version_ids } if query_ids.pacticipant_version_ids.any?
21
+ ors << { qualify(qualifier, :provider_id) => query_ids.pacticipant_ids } if query_ids.pacticipant_ids.any?
22
+ ors
23
+ end
24
+
25
+ def self.consumer_in_pacticipant_ids(query_ids)
26
+ { consumer_id: query_ids.all_pacticipant_ids }
27
+ end
28
+
29
+ def self.consumer_or_consumer_version_matches(query_ids, qualifier)
30
+ ors = []
31
+ ors << { qualify(qualifier, :consumer_version_id) => query_ids.pacticipant_version_ids } if query_ids.pacticipant_version_ids.any?
32
+ ors << { qualify(qualifier, :consumer_id) => query_ids.pacticipant_ids } if query_ids.pacticipant_ids.any?
33
+
34
+ Sequel.|(*ors)
35
+ end
36
+
37
+ # Some selecters are specified in the query, others are implied (when only one pacticipant is specified,
38
+ # the integrations are automatically worked out, and the selectors for these are of type :implied )
39
+ # When there are 3 pacticipants that each have dependencies on each other (A->B, A->C, B->C), the query
40
+ # to deploy C (implied A, implied B, specified C) was returning the A->B row because it matched the
41
+ # implied selectors as well.
42
+ # This extra filter makes sure that every row that is returned actually matches one of the specified
43
+ # selectors.
44
+ def self.either_consumer_or_provider_was_specified_in_query(query_ids, qualifier = nil)
45
+ Sequel.|({
46
+ qualify(qualifier, :consumer_id) => query_ids.specified_pacticipant_ids
47
+ } , {
48
+ qualify(qualifier, :provider_id) => query_ids.specified_pacticipant_ids
49
+ })
50
+ end
51
+
52
+ # QueryIds is built from a single selector, so there is only one pacticipant_id or pacticipant_version_id
53
+ def self.consumer_or_consumer_version_or_provider_or_provider_or_provider_version_match(query_ids)
54
+ ors = if query_ids.pacticipant_version_id
55
+ [
56
+ { Sequel[:lp][:consumer_version_id] => query_ids.pacticipant_version_id },
57
+ { Sequel[:lv][:provider_version_id] => query_ids.pacticipant_version_id }
58
+ ]
59
+ else
60
+ [
61
+ { Sequel[:lp][:consumer_id] => query_ids.pacticipant_id },
62
+ { Sequel[:lp][:provider_id] => query_ids.pacticipant_id }
63
+ ]
64
+ end
65
+
66
+ Sequel.|(*ors)
67
+ end
68
+
69
+ def self.qualify(qualifier, column)
70
+ if qualifier
71
+ Sequel[qualifier][column]
72
+ else
73
+ column
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,40 @@
1
+ module PactBroker
2
+ module Matrix
3
+ class QueryIds
4
+ attr_reader :all_pacticipant_ids, :specified_pacticipant_ids, :pacticipant_ids, :pacticipant_version_ids
5
+
6
+ # pacticipant_version_ids - the pacticipant version ids from the selectors where the pacticipant version id is the most specific criterion
7
+ # pacticipant_ids - the pacticipant ids from the selectors where the pacticipant id is the most specific criterion
8
+ # all_pacticipant_ids - the pacticipant ids from all the selectors, regardless of whether or not a pacticipant version has also been specified
9
+ # specified_pacticipant_ids the IDs of the pacticipants that were specified in the can-i-deploy query
10
+ def initialize(all_pacticipant_ids, specified_pacticipant_ids, pacticipant_ids, pacticipant_version_ids)
11
+ @all_pacticipant_ids = all_pacticipant_ids
12
+ @specified_pacticipant_ids = specified_pacticipant_ids
13
+ @pacticipant_ids = pacticipant_ids
14
+ @pacticipant_version_ids = pacticipant_version_ids
15
+ @all_pacticipant_ids = all_pacticipant_ids
16
+ end
17
+
18
+ def self.from_selectors(selectors)
19
+ most_specific_criteria = selectors.collect(&:most_specific_criterion)
20
+ all_pacticipant_ids = selectors.collect(&:pacticipant_id)
21
+ specified_pacticipant_ids = selectors.select(&:specified?).collect(&:pacticipant_id)
22
+ pacticipant_version_ids = collect_ids(most_specific_criteria, :pacticipant_version_id)
23
+ pacticipant_ids = collect_ids(most_specific_criteria, :pacticipant_id)
24
+ QueryIds.new(all_pacticipant_ids, specified_pacticipant_ids, pacticipant_ids, pacticipant_version_ids)
25
+ end
26
+
27
+ def self.collect_ids(hashes, key)
28
+ hashes.collect{ |s| s[key] }.flatten.compact
29
+ end
30
+
31
+ def pacticipant_id
32
+ pacticipant_ids.first
33
+ end
34
+
35
+ def pacticipant_version_id
36
+ pacticipant_version_ids.first
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,11 +1,6 @@
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
1
+ require 'pact_broker/pacts/all_pact_publications'
2
+ require 'pact_broker/repositories/helpers'
3
+ require 'pact_broker/matrix/query_builder'
9
4
  require 'sequel'
10
5
  require 'pact_broker/repositories/helpers'
11
6
  require 'pact_broker/logging'
@@ -15,41 +10,64 @@ require 'pact_broker/domain/version'
15
10
  require 'pact_broker/domain/verification'
16
11
  require 'pact_broker/pacts/pact_publication'
17
12
  require 'pact_broker/tags/tag_with_latest_flag'
13
+ require 'pact_broker/matrix/query_ids'
14
+
15
+ # The difference between `join_verifications_for` and `join_verifications` is that
16
+ # the left outer join is done on a pre-filtered dataset in `join_verifications_for`,
17
+ # so that we get a row with null verification fields for a pact that has been verified
18
+ # by a *different* version of the provider we're interested in,
19
+ # rather than being excluded from the dataset altogether.
18
20
 
19
21
  module PactBroker
20
22
  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, :quick_rows)
45
-
46
- class QuickRow < Sequel::Model(ALIASED_QUERY)
47
- CONSUMER_ID = Sequel[:quick_rows][:consumer_id]
48
- PROVIDER_ID = Sequel[:quick_rows][:provider_id]
49
- CONSUMER_VERSION_ID = Sequel[:quick_rows][:consumer_version_id]
50
- PROVIDER_VERSION_ID = Sequel[:quick_rows][:provider_version_id]
51
- PACT_PUBLICATION_ID = Sequel[:quick_rows][:pact_publication_id]
52
- VERIFICATION_ID = Sequel[:quick_rows][:verification_id]
23
+ class QuickRow < Sequel::Model(Sequel.as(:latest_pact_publication_ids_for_consumer_versions, :lp))
24
+
25
+ # Tables
26
+ LV = :latest_verification_id_for_pact_version_and_provider_version
27
+ LP = :latest_pact_publication_ids_for_consumer_versions
28
+
29
+ # Joins
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
+ # Not sure why we're eager loading some things and including others in the base query :shrug:
37
+
38
+ # Columns
39
+ CONSUMER_COLUMNS = [
40
+ Sequel[:lp][:consumer_id],
41
+ Sequel[:consumers][:name].as(:consumer_name)
42
+ ]
43
+ PROVIDER_COLUMNS = [
44
+ Sequel[:lp][:provider_id],
45
+ Sequel[:providers][:name].as(:provider_name)
46
+ ]
47
+ CONSUMER_VERSION_COLUMNS = [
48
+ Sequel[:lp][:consumer_version_id],
49
+ Sequel[:cv][:number].as(:consumer_version_number),
50
+ Sequel[:cv][:order].as(:consumer_version_order)
51
+ ]
52
+ PROVIDER_VERSION_COLUMNS = [
53
+ Sequel[:lv][:provider_version_id],
54
+ Sequel[:pv][:number].as(:provider_version_number),
55
+ Sequel[:pv][:order].as(:provider_version_order)
56
+ ]
57
+ PACT_COLUMNS = [
58
+ Sequel[:lp][:pact_publication_id],
59
+ Sequel[:lp][:pact_version_id]
60
+ ]
61
+ VERIFICATION_COLUMNS = [
62
+ Sequel[:lv][:verification_id]
63
+ ]
64
+ ALL_COLUMNS = CONSUMER_COLUMNS + CONSUMER_VERSION_COLUMNS + PACT_COLUMNS +
65
+ PROVIDER_COLUMNS + PROVIDER_VERSION_COLUMNS + VERIFICATION_COLUMNS
66
+ PACTICIPANT_NAMES_AND_IDS = CONSUMER_COLUMNS + PROVIDER_COLUMNS
67
+
68
+ # cachable select arguments
69
+ SELECT_ALL_COLUMN_ARGS = [:select_all_columns] + ALL_COLUMNS
70
+ SELECT_PACTICIPANT_NAMES_AND_IDS_ARGS = [:select_pacticipant_names_and_ids] + PACTICIPANT_NAMES_AND_IDS
53
71
 
54
72
  associate(:many_to_one, :pact_publication, :class => "PactBroker::Pacts::PactPublication", :key => :pact_publication_id, :primary_key => :id)
55
73
  associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
@@ -63,102 +81,26 @@ module PactBroker
63
81
 
64
82
  dataset_module do
65
83
  include PactBroker::Repositories::Helpers
66
- include PactBroker::Logging
67
84
 
68
- def consumer_id consumer_id
69
- where(CONSUMER_ID => consumer_id)
85
+ select *SELECT_ALL_COLUMN_ARGS
86
+ select *SELECT_PACTICIPANT_NAMES_AND_IDS_ARGS
87
+
88
+ def distinct_integrations selectors
89
+ select_pacticipant_names_and_ids
90
+ .distinct
91
+ .matching_selectors(selectors)
70
92
  end
71
93
 
72
94
  def matching_selectors selectors
73
95
  if selectors.size == 1
74
- where_consumer_or_provider_is(selectors.first)
96
+ matching_one_selector(selectors)
75
97
  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 })
98
+ matching_multiple_selectors(selectors)
149
99
  end
150
100
  end
151
101
 
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
102
  def order_by_names_ascending_most_recent_first
103
+ from_self.
162
104
  order(
163
105
  Sequel.asc(:consumer_name),
164
106
  Sequel.desc(:consumer_version_order),
@@ -177,7 +119,86 @@ module PactBroker
177
119
  .eager(:pact_version)
178
120
  end
179
121
 
180
- end
122
+ def default_scope
123
+ select_all_columns.join_verifications.join_pacticipants_and_pacticipant_versions.from_self
124
+ end
125
+
126
+ # PRIVATE METHODS
127
+
128
+ # When we have one selector, we need to join ALL the verifications to find out
129
+ # what integrations exist
130
+ def matching_one_selector(selectors)
131
+ join_verifications
132
+ .join_pacticipants_and_pacticipant_versions
133
+ .where {
134
+ QueryBuilder.consumer_or_consumer_version_or_provider_or_provider_or_provider_version_match(QueryIds.from_selectors(selectors))
135
+ }
136
+ end
137
+
138
+ # When the user has specified multiple selectors, we only want to join the verifications for
139
+ # the specified selectors. This is because of the behaviour of the left outer join.
140
+ # Imagine a pact has been verified by a provider version that was NOT specified in the selectors.
141
+ # If we join all the verifications and THEN filter the rows to only show the versions specified
142
+ # in the selectors, we won't get a row for that pact, and hence, we won't
143
+ # know that it hasn't been verified by the provider version we're interested in.
144
+ # Instead, we need to filter the verifications dataset down to only the ones specified in the selectors first,
145
+ # and THEN join them to the pacts, so that we get a row for the pact with null provider version
146
+ # and verification fields.
147
+ def matching_multiple_selectors(selectors)
148
+ query_ids = QueryIds.from_selectors(selectors)
149
+ join_verifications_for(query_ids)
150
+ .join_pacticipants_and_pacticipant_versions
151
+ .where {
152
+ Sequel.&(
153
+ QueryBuilder.consumer_or_consumer_version_matches(query_ids, :lp),
154
+ QueryBuilder.provider_or_provider_version_matches_or_pact_unverified(query_ids, :lv),
155
+ QueryBuilder.either_consumer_or_provider_was_specified_in_query(query_ids, :lp)
156
+ )
157
+ }
158
+ end
159
+
160
+ def join_verifications_for(query_ids)
161
+ left_outer_join(verifications_for(query_ids), LP_LV_JOIN, { table_alias: :lv } )
162
+ end
163
+
164
+ def verifications_for(query_ids)
165
+ db[LV]
166
+ .select(:verification_id, :provider_version_id, :pact_version_id, :provider_id)
167
+ .where {
168
+ Sequel.&(
169
+ QueryBuilder.consumer_in_pacticipant_ids(query_ids),
170
+ QueryBuilder.provider_or_provider_version_matches(query_ids)
171
+ )
172
+ }
173
+ end
174
+
175
+ def join_pacticipants_and_pacticipant_versions
176
+ join_consumers
177
+ .join_providers
178
+ .join_consumer_versions
179
+ .join_provider_versions
180
+ end
181
+
182
+ def join_consumers
183
+ join(:pacticipants, CONSUMER_JOIN, { table_alias: :consumers })
184
+ end
185
+
186
+ def join_providers
187
+ join(:pacticipants, PROVIDER_JOIN, { table_alias: :providers })
188
+ end
189
+
190
+ def join_consumer_versions
191
+ join(:versions, CONSUMER_VERSION_JOIN, { table_alias: :cv })
192
+ end
193
+
194
+ def join_provider_versions
195
+ left_outer_join(:versions, PROVIDER_VERSION_JOIN, { table_alias: :pv } )
196
+ end
197
+
198
+ def join_verifications
199
+ left_outer_join(LV, LP_LV_JOIN, { table_alias: :lv } )
200
+ end
201
+ end # end dataset_module
181
202
 
182
203
  def success
183
204
  verification&.success
@@ -250,6 +271,50 @@ module PactBroker
250
271
  def involves_pacticipant_with_name?(pacticipant_name)
251
272
  pacticipant_name.include?(pacticipant_name)
252
273
  end
274
+
275
+ def provider_version_id
276
+ # null when not verified
277
+ values[:provider_version_id]
278
+ end
279
+
280
+ def verification_id
281
+ # null when not verified
282
+ return_or_raise_if_not_set(:verification_id)
283
+ end
284
+
285
+ def consumer_name
286
+ return_or_raise_if_not_set(:consumer_name)
287
+ end
288
+
289
+ def consumer_version_number
290
+ return_or_raise_if_not_set(:consumer_version_number)
291
+ end
292
+
293
+ def consumer_version_order
294
+ return_or_raise_if_not_set(:consumer_version_order)
295
+ end
296
+
297
+ def provider_name
298
+ return_or_raise_if_not_set(:provider_name)
299
+ end
300
+
301
+ def provider_version_number
302
+ return_or_raise_if_not_set(:provider_version_number)
303
+ end
304
+
305
+ def provider_version_order
306
+ return_or_raise_if_not_set(:provider_version_order)
307
+ end
308
+
309
+ # This model needs the verifications and pacticipants joined to it
310
+ # before it can be used, as it's not a "real" model.
311
+ def return_or_raise_if_not_set(key)
312
+ if values.key?(key)
313
+ values[key]
314
+ else
315
+ raise "Required table not joined"
316
+ end
317
+ end
253
318
  end
254
319
  end
255
320
  end