Skip to content

Commit 8488212

Browse files
committed
feat(matrix): use views to create matrix query
1 parent 852324a commit 8488212

13 files changed

+230
-41
lines changed

DEVELOPER_DOCUMENTATION.md

+4
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ Domain classes are found in `lib/pact_broker/domain`. Many of these classes are
5555
* `latest_tagged_pact_publications` - This view has the same columns as `all_pact_publications`, plus a `tag_name` column. It is used to return the pact for the latest tagged version of a consumer.
5656

5757
* `latest_verifications` - The most recent verification for each pact version.
58+
59+
* `matrix` - The matrix of every pact publication and verification. Includes every pact revision (eg. publishing to the same consumer version twice, or using PATCH) and every verification (including 'overwritten' ones. eg. when the same provider build runs twice.)
60+
61+
* `latest_matrix` - This view is a subset of, and has the same columns as, the `matrix`. It removes 'overwritten' pacts and verifications from the matrix (ie. only show latest pact revision for each consumer version and latest verification for each provider version)

db/migrations/000048_create_matrix.rb

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Sequel.migration do
22
up do
3-
p = :latest_pact_publications_by_consumer_versions
3+
# Includes every pact revision (eg. publishing to the same consumer version twice,
4+
# or using PATCH) and every verification
5+
# (including 'overwritten' ones. eg. when the same provider build runs twice.)
6+
p = :all_pact_publications
47
create_view(:matrix,
58
from(p)
69
.select(
@@ -12,15 +15,16 @@
1215
Sequel[p][:id].as(:pact_publication_id),
1316
Sequel[p][:pact_version_id],
1417
Sequel[p][:pact_version_sha],
15-
Sequel[p][:revision_number],
18+
Sequel[p][:revision_number].as(:pact_revision_number),
1619
Sequel[p][:created_at].as(:pact_created_at),
1720
Sequel[p][:provider_id],
1821
Sequel[p][:provider_name],
1922
Sequel[:versions][:id].as(:provider_version_id),
2023
Sequel[:versions][:number].as(:provider_version_number),
2124
Sequel[:versions][:order].as(:provider_version_order),
25+
Sequel[:verifications][:id].as(:verification_id),
2226
Sequel[:verifications][:success],
23-
Sequel[:verifications][:number],
27+
Sequel[:verifications][:number].as(:verification_number),
2428
Sequel[:verifications][:id].as(:verification_id),
2529
Sequel[:verifications][:execution_date].as(:verification_executed_at),
2630
Sequel[:verifications][:build_url].as(:verification_build_url)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Sequel.migration do
2+
change do
3+
# joining with latest_pact_publication_revision_numbers gets rid of the overwritten
4+
# pact revisions, and the max(verification_id) gets rid of the overwritten
5+
# verifications
6+
create_view(:latest_verification_id_for_consumer_version_and_provider_version,
7+
"select consumer_version_id, provider_version_id, max(verification_id) as latest_verification_id
8+
from matrix
9+
inner join latest_pact_publication_revision_numbers lr
10+
on matrix.consumer_id = lr.consumer_id
11+
and matrix.provider_id = lr.provider_id
12+
and matrix.consumer_version_order = lr.consumer_version_order
13+
and matrix.pact_revision_number = lr.latest_revision_number
14+
group by consumer_version_id, provider_version_id"
15+
)
16+
end
17+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Sequel.migration do
2+
change do
3+
# Removes 'overwritten' pacts and verifications from the matrix
4+
# (ie. only show latest pact revision for each consumer version and
5+
# latest verification for each provider version)
6+
# Must include lines where verification_id is null so that we don't
7+
# lose the unverified pacts.
8+
create_view(:latest_matrix,
9+
"SELECT matrix.* FROM matrix
10+
INNER JOIN latest_verification_id_for_consumer_version_and_provider_version AS lv
11+
ON ((matrix.consumer_version_id = lv.consumer_version_id)
12+
AND (matrix.provider_version_id = lv.provider_version_id)
13+
AND ((matrix.verification_id = lv.latest_verification_id)))
14+
15+
UNION
16+
17+
select * from matrix where verification_id is null"
18+
)
19+
end
20+
end

lib/pact_broker/api/decorators/matrix_decorator.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def verification_hash(line, base_url)
118118
verifiedAt: line[:verification_executed_at].to_datetime.xmlschema,
119119
_links: {
120120
self: {
121-
href: verification_url(OpenStruct.new(line), base_url)
121+
href: verification_url(OpenStruct.new(line.merge(number: line[:verification_number])), base_url)
122122
}
123123
}
124124
}

lib/pact_broker/matrix/latest_row.rb

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require 'pact_broker/matrix/row'
2+
3+
module PactBroker
4+
module Matrix
5+
6+
# Latest pact revision for each consumer version => latest verification
7+
8+
class LatestRow < Row
9+
set_dataset(:latest_matrix)
10+
end
11+
end
12+
end

lib/pact_broker/matrix/repository.rb

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'pact_broker/repositories/helpers'
22
require 'pact_broker/matrix/row'
3+
require 'pact_broker/matrix/latest_row'
34

45
module PactBroker
56
module Matrix
@@ -18,7 +19,7 @@ class Repository
1819
def find selectors, options = {}
1920
# The group with the nil provider_version_numbers will be the results of the left outer join
2021
# that don't have verifications, so we need to include them all.
21-
lines = find_all(selectors)
22+
lines = find_all(selectors, options)
2223
lines = apply_scope(options, selectors, lines)
2324

2425
if options.key?(:success)
@@ -33,13 +34,11 @@ def all_versions_specified? selectors
3334
end
3435

3536
def apply_scope options, selectors, lines
36-
return lines unless options[:latestby]
37+
return lines unless options[:latestby] == 'cvp' || options[:latestby] == 'cp'
3738

3839
group_by_columns = case options[:latestby]
3940
when 'cvp' then GROUP_BY_PROVIDER
4041
when 'cp' then GROUP_BY_PACT
41-
else
42-
GROUP_BY_PROVIDER_VERSION_NUMBER
4342
end
4443

4544
lines.group_by{|line| group_by_columns.collect{|key| line[key] }}
@@ -50,25 +49,22 @@ def apply_scope options, selectors, lines
5049

5150
def find_for_consumer_and_provider pacticipant_1_name, pacticipant_2_name
5251
selectors = [{ pacticipant_name: pacticipant_1_name }, { pacticipant_name: pacticipant_2_name }]
53-
find_all(selectors)
52+
find_all(selectors, {latestby: 'cvpv'})
5453
.sort{|l1, l2| l2[:consumer_version_order] <=> l1[:consumer_version_order]}
5554
end
5655

5756
def find_compatible_pacticipant_versions selectors
58-
find(selectors)
59-
.group_by{|line| GROUP_BY_PROVIDER_VERSION_NUMBER.collect{|key| line[key] }}
60-
.values
61-
.collect{ | lines | lines.first[:provider_version_number].nil? ? lines : lines.last }
62-
.flatten
57+
58+
find(selectors, latestby: 'cvpv')
6359
.select{|line| line[:success] }
6460
end
6561

6662
##
6763
# If the version is nil, it means all versions for that pacticipant are to be included
6864
#
69-
def find_all selectors
65+
def find_all selectors, options
7066
selectors = look_up_versions_for_tags(selectors)
71-
query = Row.select_all
67+
query = base_table(options).select_all
7268

7369
if selectors.size == 1
7470
query = where_consumer_or_provider_is(selectors.first, query)
@@ -80,6 +76,11 @@ def find_all selectors
8076
.collect(&:values)
8177
end
8278

79+
def base_table(options)
80+
return Row unless options[:latestby]
81+
return LatestRow
82+
end
83+
8384
def look_up_versions_for_tags(selectors)
8485
selectors.collect do | selector |
8586
# resource validation currently stops tag being specified without latest=true

lib/pact_broker/matrix/row.rb

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ class Row < Sequel::Model
88
dataset_module do
99
include PactBroker::Repositories::Helpers
1010
end
11+
12+
def summary
13+
"#{consumer_name}#{consumer_version_number} #{provider_name}#{provider_version_number || '?'} (r#{pact_revision_number}n#{verification_number || '?'})"
14+
end
1115
end
1216
end
1317
end

script/seed-matrix.rb

+20-13
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@
4242
TestDataBuilder.new
4343
.create_pact_with_hierarchy("A", "1", "B")
4444
.create_verification(provider_version: '1', success: false)
45-
.create_verification(provider_version: '2', number: 2)
46-
.create_verification(provider_version: '4', number: 3)
45+
.create_verification(provider_version: '1', number: 2, success: true)
46+
.create_verification(provider_version: '2', number: 3)
47+
.create_verification(provider_version: '4', number: 4)
4748
.create_provider_version("5")
4849
.use_consumer("B")
4950
.use_consumer_version("1")
@@ -55,17 +56,23 @@
5556
.create_verification(provider_version: '2', success: true)
5657
.create_consumer_version("3")
5758
.create_pact
58-
.create_pact_with_hierarchy("the-example-application", "391c43cae8c0e83c570c191f7324fccd67e53abc", "another-example-application")
59-
.create_verification(provider_version: '391c43cae8c0e83c570c191f7324fccd67e53abc')
60-
.create_verification(provider_version: '57fa24e44efc4d8aa42bb855a8217f145b5b1b5b', number: 2, success: false)
61-
.create_verification(provider_version: '4', number: 3)
62-
.use_consumer("another-example-application")
63-
.use_consumer_version("391c43cae8c0e83c570c191f7324fccd67e53abc")
64-
.create_provider("a-third-example-application")
59+
.use_consumer("A")
60+
.create_consumer_version("2")
61+
.use_provider("B")
6562
.create_pact
66-
.create_verification(provider_version: '391c43cae8c0e83c570c191f7324fccd67e53abc', success: false)
67-
.use_consumer_version("57fa24e44efc4d8aa42bb855a8217f145b5b1b5b")
68-
.create_pact
69-
.create_verification(provider_version: '57fa24e44efc4d8aa42bb855a8217f145b5b1b5b', success: true)
63+
.create_verification(provider_version: '5')
64+
65+
# .create_pact_with_hierarchy("the-example-application", "391c43cae8c0e83c570c191f7324fccd67e53abc", "another-example-application")
66+
# .create_verification(provider_version: '391c43cae8c0e83c570c191f7324fccd67e53abc')
67+
# .create_verification(provider_version: '57fa24e44efc4d8aa42bb855a8217f145b5b1b5b', number: 2, success: false)
68+
# .create_verification(provider_version: '4', number: 3)
69+
# .use_consumer("another-example-application")
70+
# .use_consumer_version("391c43cae8c0e83c570c191f7324fccd67e53abc")
71+
# .create_provider("a-third-example-application")
72+
# .create_pact
73+
# .create_verification(provider_version: '391c43cae8c0e83c570c191f7324fccd67e53abc', success: false)
74+
# .use_consumer_version("57fa24e44efc4d8aa42bb855a8217f145b5b1b5b")
75+
# .create_pact
76+
# .create_verification(provider_version: '57fa24e44efc4d8aa42bb855a8217f145b5b1b5b', success: true)
7077

7178

spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ module Decorators
1818
provider_version_number: "4.5.6",
1919
provider_name: "Provider",
2020
success: line_1_success,
21-
number: 1,
22-
build_url: nil,
21+
verification_number: 1,
22+
verification_build_url: nil,
2323
verification_executed_at: verification_date
2424
}
2525
end
@@ -33,8 +33,8 @@ module Decorators
3333
provider_version_number: nil,
3434
provider_name: "Provider",
3535
success: line_2_success,
36-
number: nil,
37-
build_url: nil,
36+
verification_number: nil,
37+
verification_build_url: nil,
3838
verification_executed_at: verification_date
3939
}
4040
end

spec/lib/pact_broker/matrix/repository_spec.rb

+13-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def build_selectors(hash)
1212
end
1313

1414
def shorten_row row
15-
"#{row[:consumer_name]}#{row[:consumer_version_number]} #{row[:provider_name]}#{row[:provider_version_number] || '?'} n#{row[:number] || '?'}"
15+
"#{row[:consumer_name]}#{row[:consumer_version_number]} #{row[:provider_name]}#{row[:provider_version_number] || '?'} n#{row[:verification_number] || '?'}"
1616
end
1717

1818
def shorten_rows rows
@@ -23,6 +23,7 @@ def shorten_rows rows
2323
before do
2424
# A1 - B1
2525
# A1 - B1 r2
26+
# A1 - B2 r3
2627
# A1 - C1
2728
# A2 - B?
2829
# A2 - C2
@@ -46,7 +47,8 @@ def shorten_rows rows
4647
let(:a1_b1_n1) { "A1 B1 n1" }
4748
let(:a1_b1_n2) { "A1 B1 n2" }
4849
let(:a1_b2_n3) { "A1 B2 n3" }
49-
let(:a1_c1_n1) { "A1 C1 n1"}
50+
let(:a1_c1_n1) { "A1 C1 n1" }
51+
let(:a2_b__n_) { "A2 B? n?" }
5052

5153
context "when just the consumer name is specified" do
5254
let(:selectors) { build_selectors('A' => nil) }
@@ -56,6 +58,7 @@ def shorten_rows rows
5658
expect(subject).to include a1_b1_n1
5759
expect(subject).to include a1_b1_n2
5860
expect(subject).to include a1_c1_n1
61+
expect(subject).to include a2_b__n_
5962
expect(subject.size).to eq 6
6063
end
6164
end
@@ -67,6 +70,7 @@ def shorten_rows rows
6770
expect(subject).to_not include a1_b1_n1
6871
expect(subject).to include a1_b1_n2
6972
expect(subject).to include a1_c1_n1
73+
expect(subject).to include a2_b__n_
7074
expect(subject.size).to eq 5
7175
end
7276
end
@@ -79,6 +83,7 @@ def shorten_rows rows
7983
expect(subject).to_not include a1_b1_n2
8084
expect(subject).to include a1_b2_n3
8185
expect(subject).to include a1_c1_n1
86+
expect(subject).to include a2_b__n_
8287
expect(subject.size).to eq 4
8388
end
8489
end
@@ -554,8 +559,8 @@ def shorten_rows rows
554559

555560
describe "#find_compatible_pacticipant_versions" do
556561
let(:td) { TestDataBuilder.new }
557-
# subject { Repository.new.find_compatible_pacticipant_versions(selectors) }
558-
subject { Repository.new.find(selectors, success: [true], scope: 'latest')}
562+
563+
subject { Repository.new.find(selectors, success: [true], latestby: 'cvpv')}
559564

560565
context "when compatible versions can be found" do
561566
before do
@@ -582,22 +587,23 @@ def shorten_rows rows
582587
expect(subject.first[:consumer_version_number]).to eq "1"
583588
expect(subject.first[:provider_name]).to eq "B"
584589
expect(subject.first[:provider_version_number]).to eq "2"
585-
expect(subject.first[:number]).to eq 2
590+
expect(subject.first[:verification_number]).to eq 2
586591
expect(subject.first[:pact_created_at]).to be_datey
587592
expect(subject.first[:verification_executed_at]).to be_datey
588593

589594
expect(subject.last[:consumer_name]).to eq "B"
590595
expect(subject.last[:consumer_version_number]).to eq "2"
591596
expect(subject.last[:provider_name]).to eq "C"
592597
expect(subject.last[:provider_version_number]).to eq "2"
593-
expect(subject.last[:number]).to eq 1
598+
expect(subject.last[:verification_number]).to eq 1
594599
expect(subject.last[:pact_created_at]).to be_datey
595600

596601
expect(subject.size).to eq 2
597602
end
598603

599604
context "when one or more pacticipants does not have a version specified" do
600605
let(:selectors){ build_selectors("A" => "1", "B" => "2", "C" => nil) }
606+
let(:options) { { latestby: 'cvpv'} }
601607

602608
it "returns all the rows for that pacticipant" do
603609
expect(subject).to include_hash_matching(provider_name: "C", provider_version_number: "2")
@@ -628,7 +634,7 @@ def shorten_rows rows
628634

629635
it "returns the last line" do
630636
expect(subject.size).to eq 1
631-
expect(subject.first[:number]).to eq 2
637+
expect(subject).to include_hash_matching verification_number: 2
632638
end
633639
end
634640

0 commit comments

Comments
 (0)