Skip to content

Commit ba4a1cc

Browse files
committed
feat(matrix): allow query to determine if a particular pacticipant version is compatible with the latest tagged versions of all its dependencies
eg. "can I deploy Foo version 2 to production?"
1 parent 8424e14 commit ba4a1cc

File tree

3 files changed

+188
-11
lines changed

3 files changed

+188
-11
lines changed

lib/pact_broker/matrix/parse_query.rb

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ def self.call query
3131
if params.key?('limit')
3232
options[:limit] = params['limit']
3333
end
34+
if params.key?('latest')
35+
options[:latest] = params['latest']
36+
end
37+
if params.key?('tag')
38+
options[:tag] = params['tag']
39+
end
3440
return selectors, options
3541
end
3642
end

lib/pact_broker/matrix/repository.rb

+49-11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class Repository
1818
def find selectors, options = {}
1919
# The group with the nil provider_version_numbers will be the results of the left outer join
2020
# that don't have verifications, so we need to include them all.
21-
lines = find_all(selectors, options)
21+
lines = find_all(resolve_selectors(selectors, options), options)
2222
lines = apply_scope(options, selectors, lines)
2323

2424
if options.key?(:success)
@@ -48,7 +48,8 @@ def apply_scope options, selectors, lines
4848

4949
def find_for_consumer_and_provider pacticipant_1_name, pacticipant_2_name
5050
selectors = [{ pacticipant_name: pacticipant_1_name }, { pacticipant_name: pacticipant_2_name }]
51-
find_all(selectors, {latestby: 'cvpv'}).sort.collect(&:values)
51+
options = { latestby: 'cvpv' }
52+
find_all(resolve_selectors(selectors, options), options).sort.collect(&:values)
5253
end
5354

5455
def find_compatible_pacticipant_versions selectors
@@ -59,15 +60,8 @@ def find_compatible_pacticipant_versions selectors
5960
# If the version is nil, it means all versions for that pacticipant are to be included
6061
#
6162
def find_all selectors, options
62-
selectors = look_up_versions_for_tags(selectors)
6363
query = base_table(options).select_all
64-
65-
if selectors.size == 1
66-
query = where_consumer_or_provider_is(selectors.first, query)
67-
else
68-
query = where_consumer_and_provider_in(selectors, query)
69-
end
70-
64+
query = where_row_matches_selectors selectors, query
7165
query = query.limit(options[:limit]) if options[:limit]
7266
query.order(
7367
Sequel.asc(:consumer_name),
@@ -83,11 +77,22 @@ def base_table(options)
8377
return LatestRow
8478
end
8579

86-
def look_up_versions_for_tags(selectors)
80+
def resolve_selectors(selectors, options)
81+
selectors = look_up_versions_for_tags(selectors, options)
82+
83+
if options[:latest]
84+
apply_latest_and_tag_to_inferred_selectors(selectors, options)
85+
else
86+
selectors
87+
end
88+
end
89+
90+
def look_up_versions_for_tags(selectors, options)
8791
selectors.collect do | selector |
8892
# resource validation currently stops tag being specified without latest=true
8993
if selector[:tag] && selector[:latest]
9094
version = version_repository.find_by_pacticpant_name_and_latest_tag(selector[:pacticipant_name], selector[:tag])
95+
raise "Could not find version with tag #{selector[:tag].inspect} for #{selector[:pacticipant_name]}" unless version
9196
# validation in resource should ensure we always have a version
9297
{
9398
pacticipant_name: selector[:pacticipant_name],
@@ -105,6 +110,39 @@ def look_up_versions_for_tags(selectors)
105110
end
106111
end
107112

113+
def apply_latest_and_tag_to_inferred_selectors(selectors, options)
114+
all_pacticipant_names = all_pacticipant_names_in_specified_matrix(selectors, options)
115+
specified_names = selectors.collect{ |s| s[:pacticipant_name] }
116+
inferred_names = all_pacticipant_names - specified_names
117+
118+
inferred_selectors = inferred_names.collect do | pacticipant_name |
119+
{
120+
pacticipant_name: pacticipant_name,
121+
latest: options[:latest]
122+
}.tap { |it| it[:tag] = options[:tag] if options[:tag] }
123+
end
124+
125+
selectors + look_up_versions_for_tags(inferred_selectors, options)
126+
end
127+
128+
def all_pacticipant_names_in_specified_matrix(selectors, options)
129+
query = base_table(options).select(:consumer_name, :provider_name)
130+
query = where_row_matches_selectors(selectors, query)
131+
query
132+
.all
133+
.collect{ | row | [row.consumer_name, row.provider_name] }
134+
.flatten
135+
.uniq
136+
end
137+
138+
def where_row_matches_selectors selectors, query
139+
if selectors.size == 1
140+
where_consumer_or_provider_is(selectors.first, query)
141+
else
142+
where_consumer_and_provider_in(selectors, query)
143+
end
144+
end
145+
108146
def where_consumer_and_provider_in selectors, query
109147
query.where{
110148
Sequel.&(

spec/lib/pact_broker/matrix/repository_spec.rb

+133
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,139 @@ def shorten_rows rows
555555
end
556556
end
557557

558+
describe "find with global latest and tag specified" do
559+
subject { shorten_rows(Repository.new.find(selectors, options)) }
560+
561+
context "with one consumer/version and latest tag specified for all the other pacticipants" do
562+
before do
563+
td.create_pact_with_hierarchy("A", "1", "B")
564+
.create_verification(provider_version: "1")
565+
.create_verification(provider_version: "2", number: 2)
566+
.use_provider_version("1")
567+
.create_provider_version_tag("prod")
568+
.create_provider("C")
569+
.create_pact
570+
.create_verification(provider_version: "3")
571+
.use_provider_version("3")
572+
.create_provider_version_tag("prod")
573+
.create_verification(provider_version: "4", number: 2)
574+
end
575+
576+
let(:selectors) { build_selectors('A'=> '1') }
577+
let(:options) { { tag: 'prod', latest: true } }
578+
579+
it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
580+
expect(subject).to include "A1 B1 n1"
581+
expect(subject).to include "A1 C3 n1"
582+
expect(subject.size).to eq 2
583+
end
584+
end
585+
586+
context "with one consumer/version and latest specified for all the other pacticipants" do
587+
before do
588+
td.create_pact_with_hierarchy("A", "1", "B")
589+
.create_verification(provider_version: "1")
590+
.create_verification(provider_version: "2", number: 2)
591+
.use_provider_version("1")
592+
.create_provider("C")
593+
.create_pact
594+
.create_verification(provider_version: "3")
595+
.create_verification(provider_version: "4", number: 2)
596+
end
597+
598+
let(:selectors) { build_selectors('A'=> '1') }
599+
let(:options) { { latest: true } }
600+
601+
it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
602+
expect(subject).to include "A1 B2 n2"
603+
expect(subject).to include "A1 C4 n2"
604+
expect(subject.size).to eq 2
605+
end
606+
end
607+
608+
context "with one pacticipant without a version and latest tag specified for all the other pacticipants" do
609+
before do
610+
td.create_pact_with_hierarchy("A", "1", "B")
611+
.create_verification(provider_version: "1")
612+
.create_verification(provider_version: "2", number: 2)
613+
.use_provider_version("1")
614+
.create_provider_version_tag("prod")
615+
.create_provider("C")
616+
.create_pact
617+
.create_verification(provider_version: "3")
618+
.use_provider_version("3")
619+
.create_provider_version_tag("prod")
620+
.create_verification(provider_version: "4", number: 2)
621+
.create_consumer_version("2")
622+
.create_pact
623+
end
624+
625+
let(:selectors) { build_selectors('A'=> nil) }
626+
let(:options) { { tag: 'prod', latest: true } }
627+
628+
it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
629+
expect(subject).to include "A1 B1 n1"
630+
expect(subject).to include "A1 C3 n1"
631+
expect(subject).to include "A2 C? n?"
632+
expect(subject.size).to eq 3
633+
end
634+
end
635+
636+
context "with one pacticipant/version that is both a consumer and provider and latest tag specified for all the other pacticipants" do
637+
before do
638+
td.create_pact_with_hierarchy("A", "1", "B")
639+
.create_consumer_version_tag("prod")
640+
.create_verification(provider_version: "1")
641+
.use_provider_version("1")
642+
.use_consumer("B")
643+
.use_consumer_version("1")
644+
.create_provider("C")
645+
.create_pact
646+
.create_verification(provider_version: "3")
647+
.use_provider_version("3")
648+
.create_provider_version_tag("prod")
649+
.create_verification(provider_version: "4", number: 2)
650+
end
651+
652+
let(:selectors) { build_selectors('B'=> '1') }
653+
let(:options) { { tag: 'prod', latest: true } }
654+
655+
it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
656+
expect(subject).to include "A1 B1 n1"
657+
expect(subject).to include "B1 C3 n1"
658+
expect(subject.size).to eq 2
659+
end
660+
end
661+
662+
context "with one pacticipant/latest tag and latest tag specified for all the other pacticipants" do
663+
before do
664+
td.create_pact_with_hierarchy("A", "1", "B")
665+
.create_consumer_version_tag("dev")
666+
.create_verification(provider_version: "1")
667+
.use_provider_version("1")
668+
.create_provider_version_tag("prod")
669+
.create_provider("C")
670+
.create_pact
671+
.create_verification(provider_version: "3")
672+
.use_provider_version("3")
673+
.create_provider_version_tag("prod")
674+
.create_verification(provider_version: "4", number: 2)
675+
end
676+
677+
let(:selectors) { [{ pacticipant_name: 'A', latest: true, tag: 'dev' } ] }
678+
let(:options) { { tag: 'prod', latest: true } }
679+
680+
it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
681+
expect(subject).to include "A1 B1 n1"
682+
expect(subject).to include "A1 C3 n1"
683+
expect(subject).to_not include "A1 C4 n2"
684+
expect(subject.size).to eq 2
685+
end
686+
end
687+
688+
689+
end
690+
558691
describe "#find_for_consumer_and_provider" do
559692
before do
560693
TestDataBuilder.new

0 commit comments

Comments
 (0)