Skip to content

Commit

Permalink
feat(matrix): allow query to determine if a particular pacticipant ve…
Browse files Browse the repository at this point in the history
…rsion is compatible with the latest tagged versions of all its dependencies

eg. "can I deploy Foo version 2 to production?"
  • Loading branch information
bethesque committed Nov 8, 2017
1 parent 8424e14 commit ba4a1cc
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 11 deletions.
6 changes: 6 additions & 0 deletions lib/pact_broker/matrix/parse_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ def self.call query
if params.key?('limit')
options[:limit] = params['limit']
end
if params.key?('latest')
options[:latest] = params['latest']
end
if params.key?('tag')
options[:tag] = params['tag']
end
return selectors, options
end
end
Expand Down
60 changes: 49 additions & 11 deletions lib/pact_broker/matrix/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Repository
def find selectors, options = {}
# The group with the nil provider_version_numbers will be the results of the left outer join
# that don't have verifications, so we need to include them all.
lines = find_all(selectors, options)
lines = find_all(resolve_selectors(selectors, options), options)
lines = apply_scope(options, selectors, lines)

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

def find_for_consumer_and_provider pacticipant_1_name, pacticipant_2_name
selectors = [{ pacticipant_name: pacticipant_1_name }, { pacticipant_name: pacticipant_2_name }]
find_all(selectors, {latestby: 'cvpv'}).sort.collect(&:values)
options = { latestby: 'cvpv' }
find_all(resolve_selectors(selectors, options), options).sort.collect(&:values)
end

def find_compatible_pacticipant_versions selectors
Expand All @@ -59,15 +60,8 @@ def find_compatible_pacticipant_versions selectors
# If the version is nil, it means all versions for that pacticipant are to be included
#
def find_all selectors, options
selectors = look_up_versions_for_tags(selectors)
query = base_table(options).select_all

if selectors.size == 1
query = where_consumer_or_provider_is(selectors.first, query)
else
query = where_consumer_and_provider_in(selectors, query)
end

query = where_row_matches_selectors selectors, query
query = query.limit(options[:limit]) if options[:limit]
query.order(
Sequel.asc(:consumer_name),
Expand All @@ -83,11 +77,22 @@ def base_table(options)
return LatestRow
end

def look_up_versions_for_tags(selectors)
def resolve_selectors(selectors, options)
selectors = look_up_versions_for_tags(selectors, options)

if options[:latest]
apply_latest_and_tag_to_inferred_selectors(selectors, options)
else
selectors
end
end

def look_up_versions_for_tags(selectors, options)
selectors.collect do | selector |
# resource validation currently stops tag being specified without latest=true
if selector[:tag] && selector[:latest]
version = version_repository.find_by_pacticpant_name_and_latest_tag(selector[:pacticipant_name], selector[:tag])
raise "Could not find version with tag #{selector[:tag].inspect} for #{selector[:pacticipant_name]}" unless version
# validation in resource should ensure we always have a version
{
pacticipant_name: selector[:pacticipant_name],
Expand All @@ -105,6 +110,39 @@ def look_up_versions_for_tags(selectors)
end
end

def apply_latest_and_tag_to_inferred_selectors(selectors, options)
all_pacticipant_names = all_pacticipant_names_in_specified_matrix(selectors, options)
specified_names = selectors.collect{ |s| s[:pacticipant_name] }
inferred_names = all_pacticipant_names - specified_names

inferred_selectors = inferred_names.collect do | pacticipant_name |
{
pacticipant_name: pacticipant_name,
latest: options[:latest]
}.tap { |it| it[:tag] = options[:tag] if options[:tag] }
end

selectors + look_up_versions_for_tags(inferred_selectors, options)
end

def all_pacticipant_names_in_specified_matrix(selectors, options)
query = base_table(options).select(:consumer_name, :provider_name)
query = where_row_matches_selectors(selectors, query)
query
.all
.collect{ | row | [row.consumer_name, row.provider_name] }
.flatten
.uniq
end

def where_row_matches_selectors selectors, query
if selectors.size == 1
where_consumer_or_provider_is(selectors.first, query)
else
where_consumer_and_provider_in(selectors, query)
end
end

def where_consumer_and_provider_in selectors, query
query.where{
Sequel.&(
Expand Down
133 changes: 133 additions & 0 deletions spec/lib/pact_broker/matrix/repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,139 @@ def shorten_rows rows
end
end

describe "find with global latest and tag specified" do
subject { shorten_rows(Repository.new.find(selectors, options)) }

context "with one consumer/version and latest tag specified for all the other pacticipants" do
before do
td.create_pact_with_hierarchy("A", "1", "B")
.create_verification(provider_version: "1")
.create_verification(provider_version: "2", number: 2)
.use_provider_version("1")
.create_provider_version_tag("prod")
.create_provider("C")
.create_pact
.create_verification(provider_version: "3")
.use_provider_version("3")
.create_provider_version_tag("prod")
.create_verification(provider_version: "4", number: 2)
end

let(:selectors) { build_selectors('A'=> '1') }
let(:options) { { tag: 'prod', latest: true } }

it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
expect(subject).to include "A1 B1 n1"
expect(subject).to include "A1 C3 n1"
expect(subject.size).to eq 2
end
end

context "with one consumer/version and latest specified for all the other pacticipants" do
before do
td.create_pact_with_hierarchy("A", "1", "B")
.create_verification(provider_version: "1")
.create_verification(provider_version: "2", number: 2)
.use_provider_version("1")
.create_provider("C")
.create_pact
.create_verification(provider_version: "3")
.create_verification(provider_version: "4", number: 2)
end

let(:selectors) { build_selectors('A'=> '1') }
let(:options) { { latest: true } }

it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
expect(subject).to include "A1 B2 n2"
expect(subject).to include "A1 C4 n2"
expect(subject.size).to eq 2
end
end

context "with one pacticipant without a version and latest tag specified for all the other pacticipants" do
before do
td.create_pact_with_hierarchy("A", "1", "B")
.create_verification(provider_version: "1")
.create_verification(provider_version: "2", number: 2)
.use_provider_version("1")
.create_provider_version_tag("prod")
.create_provider("C")
.create_pact
.create_verification(provider_version: "3")
.use_provider_version("3")
.create_provider_version_tag("prod")
.create_verification(provider_version: "4", number: 2)
.create_consumer_version("2")
.create_pact
end

let(:selectors) { build_selectors('A'=> nil) }
let(:options) { { tag: 'prod', latest: true } }

it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
expect(subject).to include "A1 B1 n1"
expect(subject).to include "A1 C3 n1"
expect(subject).to include "A2 C? n?"
expect(subject.size).to eq 3
end
end

context "with one pacticipant/version that is both a consumer and provider and latest tag specified for all the other pacticipants" do
before do
td.create_pact_with_hierarchy("A", "1", "B")
.create_consumer_version_tag("prod")
.create_verification(provider_version: "1")
.use_provider_version("1")
.use_consumer("B")
.use_consumer_version("1")
.create_provider("C")
.create_pact
.create_verification(provider_version: "3")
.use_provider_version("3")
.create_provider_version_tag("prod")
.create_verification(provider_version: "4", number: 2)
end

let(:selectors) { build_selectors('B'=> '1') }
let(:options) { { tag: 'prod', latest: true } }

it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
expect(subject).to include "A1 B1 n1"
expect(subject).to include "B1 C3 n1"
expect(subject.size).to eq 2
end
end

context "with one pacticipant/latest tag and latest tag specified for all the other pacticipants" do
before do
td.create_pact_with_hierarchy("A", "1", "B")
.create_consumer_version_tag("dev")
.create_verification(provider_version: "1")
.use_provider_version("1")
.create_provider_version_tag("prod")
.create_provider("C")
.create_pact
.create_verification(provider_version: "3")
.use_provider_version("3")
.create_provider_version_tag("prod")
.create_verification(provider_version: "4", number: 2)
end

let(:selectors) { [{ pacticipant_name: 'A', latest: true, tag: 'dev' } ] }
let(:options) { { tag: 'prod', latest: true } }

it "finds the matrix for the latest tagged versions of each of the other other pacticipants" do
expect(subject).to include "A1 B1 n1"
expect(subject).to include "A1 C3 n1"
expect(subject).to_not include "A1 C4 n2"
expect(subject.size).to eq 2
end
end


end

describe "#find_for_consumer_and_provider" do
before do
TestDataBuilder.new
Expand Down

0 comments on commit ba4a1cc

Please sign in to comment.