Skip to content

Commit aa27cef

Browse files
authored
fix: pact broker client issue 53 (pact-foundation#299)
* fix(matrix): ensure relevant rows are not removed from the result set by the default query limit fixes: pact-foundation/pact_broker-client#53
1 parent de2bb9b commit aa27cef

File tree

5 files changed

+84
-28
lines changed

5 files changed

+84
-28
lines changed

lib/pact_broker/matrix/repository.rb

+29-25
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def is_a_row_for_this_integration_required?(specified_pacticipant_names, consume
103103
specified_pacticipant_names.include?(consumer_name)
104104
end
105105

106+
# It would be nicer to do this in the SQL, but it requires time that I don't have at the moment
106107
def apply_latestby options, selectors, lines
107108
return lines unless options[:latestby]
108109
group_by_columns = case options[:latestby]
@@ -119,6 +120,7 @@ def apply_latestby options, selectors, lines
119120
.flatten
120121
end
121122

123+
# It would be nicer to do this in the SQL, but it requires time that I don't have at the moment
122124
def remove_overwritten_revisions lines
123125
latest_revisions_keys = {}
124126
latest_revisions = []
@@ -143,7 +145,7 @@ def query_matrix selectors, options
143145
end
144146

145147
def resolve_selectors(specified_selectors, options)
146-
resolved_specified_selectors = resolve_versions_and_add_ids(specified_selectors, :specified)
148+
resolved_specified_selectors = resolve_versions_and_add_ids(specified_selectors, :specified, options[:latestby])
147149
if options[:latest] || options[:tag]
148150
add_inferred_selectors(resolved_specified_selectors, options)
149151
else
@@ -152,19 +154,19 @@ def resolve_selectors(specified_selectors, options)
152154
end
153155

154156
# Find the version number for selectors with the latest and/or tag specified
155-
def resolve_versions_and_add_ids(selectors, selector_type)
157+
def resolve_versions_and_add_ids(selectors, selector_type, latestby)
156158
selectors.collect do | selector |
157159
pacticipant = PactBroker::Domain::Pacticipant.find(name: selector[:pacticipant_name])
158160
versions = find_versions_for_selector(selector)
159-
build_selectors_for_pacticipant_and_versions(pacticipant, versions, selector, selector_type)
161+
build_selectors_for_pacticipant_and_versions(pacticipant, versions, selector, selector_type, latestby)
160162
end.flatten
161163
end
162164

163-
def build_selectors_for_pacticipant_and_versions(pacticipant, versions, original_selector, selector_type)
165+
def build_selectors_for_pacticipant_and_versions(pacticipant, versions, original_selector, selector_type, latestby)
164166
if versions
165167
versions.collect do | version |
166168
if version
167-
selector_for_version(pacticipant, version, original_selector, selector_type)
169+
selector_for_version(pacticipant, version, original_selector, selector_type, latestby)
168170
else
169171
selector_for_non_existing_version(pacticipant, original_selector, selector_type)
170172
end
@@ -192,23 +194,6 @@ def find_versions_for_selector(selector)
192194
end
193195
end
194196

195-
def add_ids_to_selector(selector)
196-
if selector[:pacticipant_name]
197-
pacticipant = PactBroker::Domain::Pacticipant.find(name: selector[:pacticipant_name])
198-
selector[:pacticipant_id] = pacticipant ? pacticipant.id : nil
199-
end
200-
201-
if selector[:pacticipant_name] && selector[:pacticipant_version_number] && !selector[:pacticipant_version_id]
202-
version = version_repository.find_by_pacticipant_name_and_number(selector[:pacticipant_name], selector[:pacticipant_version_number])
203-
selector[:pacticipant_version_id] = version ? version.id : nil
204-
end
205-
206-
if !selector.key?(:pacticipant_version_id)
207-
selector[:pacticipant_version_id] = nil
208-
end
209-
selector
210-
end
211-
212197
# When only one selector is specified, (eg. checking to see if Foo version 2 can be deployed to prod),
213198
# need to look up all integrated pacticipants, and determine their relevant (eg. latest prod) versions
214199
def add_inferred_selectors(resolved_specified_selectors, options)
@@ -228,7 +213,7 @@ def build_inferred_selectors(inferred_pacticipant_names, options)
228213
selector[:latest] = options[:latest] if options[:latest]
229214
selector
230215
end
231-
resolve_versions_and_add_ids(selectors, :inferred)
216+
resolve_versions_and_add_ids(selectors, :inferred, options[:latestby])
232217
end
233218

234219
def all_pacticipant_names_in_specified_matrix(selectors)
@@ -242,8 +227,27 @@ def selector_for_non_existing_version(pacticipant, original_selector, selector_t
242227
ResolvedSelector.for_pacticipant_and_non_existing_version(pacticipant, original_selector, selector_type)
243228
end
244229

245-
def selector_for_version(pacticipant, version, original_selector, selector_type)
246-
ResolvedSelector.for_pacticipant_and_version(pacticipant, version, original_selector, selector_type)
230+
def selector_for_version(pacticipant, version, original_selector, selector_type, latestby)
231+
pact_publication_ids, verification_ids = nil, nil
232+
233+
# Querying for the "pre-filtered" pact publications and verifications directly, rather than just using the consumer
234+
# and provider versions allows us to remove the "overwritten" pact revisions and verifications from the
235+
# matrix result set, making the final matrix query more efficient and stopping the query limit from
236+
# removing relevant data (eg. https://github.com/pact-foundation/pact_broker-client/issues/53)
237+
if latestby
238+
pact_publication_ids = PactBroker::Pacts::LatestPactPublicationsByConsumerVersion
239+
.select(:id)
240+
.where(consumer_version_id: version.id)
241+
.collect{ |pact_publication| pact_publication[:id] }
242+
243+
verification_ids = PactBroker::Verifications::LatestVerificationIdForPactVersionAndProviderVersion
244+
.select(:verification_id)
245+
.distinct
246+
.where(provider_version_id: version.id)
247+
.collect{ |pact_publication| pact_publication[:verification_id] }
248+
end
249+
250+
ResolvedSelector.for_pacticipant_and_version(pacticipant, version, pact_publication_ids, verification_ids, original_selector, selector_type)
247251
end
248252

249253
def selector_without_version(pacticipant, selector_type)

lib/pact_broker/matrix/resolved_selector.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ def self.for_pacticipant(pacticipant, type)
2323
)
2424
end
2525

26-
def self.for_pacticipant_and_version(pacticipant, version, original_selector, type)
26+
def self.for_pacticipant_and_version(pacticipant, version, pact_publication_ids = [], verification_ids = [], original_selector, type)
2727
ResolvedSelector.new(
2828
pacticipant_id: pacticipant.id,
2929
pacticipant_name: pacticipant.name,
3030
pacticipant_version_id: version.id,
31+
pact_publication_ids: pact_publication_ids,
32+
verification_ids: verification_ids,
3133
pacticipant_version_number: version.number,
3234
latest: original_selector[:latest],
3335
tag: original_selector[:tag],

lib/pact_broker/matrix/row.rb

+6-2
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,19 @@ def self.consumer_and_maybe_consumer_version_match_any_selector(selectors)
6767
end
6868

6969
def self.consumer_and_maybe_consumer_version_match_selector(s)
70-
if s[:pacticipant_version_id]
70+
if s[:pact_publication_ids]
71+
Sequel.&(pact_publication_id: s[:pact_publication_ids])
72+
elsif s[:pacticipant_version_id]
7173
Sequel.&(consumer_id: s[:pacticipant_id], consumer_version_id: s[:pacticipant_version_id])
7274
else
7375
Sequel.&(consumer_id: s[:pacticipant_id])
7476
end
7577
end
7678

7779
def self.provider_and_maybe_provider_version_match_selector(s)
78-
if s[:pacticipant_version_id]
80+
if s[:verification_ids]
81+
Sequel.&(verification_id: s[:verification_ids])
82+
elsif s[:pacticipant_version_id]
7983
Sequel.&(provider_id: s[:pacticipant_id], provider_version_id: s[:pacticipant_version_id])
8084
else
8185
Sequel.&(provider_id: s[:pacticipant_id])

spec/lib/pact_broker/matrix/deployment_status_summary_spec.rb

+2
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ module Matrix
190190
pacticipant_name: "Bar",
191191
pacticipant_version_id: 10,
192192
pacticipant_version_number: "14131c5da3abf323ccf410b1b619edac76231243",
193+
pact_publication_ids: [],
194+
verification_ids: [],
193195
latest: nil,
194196
tag: nil,
195197
type: :inferred
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
require 'pact_broker/matrix/repository'
2+
3+
module PactBroker
4+
module Matrix
5+
describe Repository do
6+
# See https://github.com/pact-foundation/pact_broker-client/issues/53
7+
# The problem occurs when a pact has so many verifications for the same provider
8+
# version that relevant rows do get returned in the result set because the specified limit
9+
# causes them to be truncated.
10+
# The most elegant solution is to create views have the data already grouped by
11+
# consumer version/provider version, consumer version/provider, and consumer/provider,
12+
# however, I don't have the time to work out how to make that view query efficient - I suspect
13+
# it will require lots of full table scans, as it will have to work out the latest pact revision
14+
# and latest verification for each pact publication and I'm not sure if it will have to do it
15+
# for the entire table, or whether it will apply the consumer/provider filters...
16+
# The quick and dirty solution is to do a pre-query to get the latest pact revision and latest
17+
# verifications for the pact versions before we do the matrix query.
18+
describe "Querying for can-i-deploy when there are more matrix rows than the specified query limit" do
19+
before do
20+
td.create_consumer("Foo")
21+
.create_provider("Bar")
22+
.create_consumer_version("1")
23+
.create_pact
24+
.create_verification(number: 1, provider_version: "2", tag_names: ['staging'])
25+
.create_verification(number: 2, provider_version: "2")
26+
.create_verification(number: 3, provider_version: "2")
27+
.create_verification(number: 4, provider_version: "2")
28+
.create_verification(number: 5, provider_version: "3")
29+
.create_provider("Wiffle")
30+
.create_pact
31+
.create_verification(number: 1, provider_version: "6", tag_names: ['staging'])
32+
end
33+
34+
let(:path) { "/matrix?q[][pacticipant]=Foo&q[][version]=1&tag=staging&latestby=cvp&limit=2" }
35+
36+
subject { get(path) }
37+
38+
it "does not remove relevant rows from the query due to the specified limit" do
39+
expect(JSON.parse(subject.body)['summary']['deployable']).to be true
40+
end
41+
end
42+
end
43+
end
44+
end

0 commit comments

Comments
 (0)