diff --git a/lib/pact_broker/matrix/parse_query.rb b/lib/pact_broker/matrix/parse_query.rb index 262aec948..3446f4c85 100644 --- a/lib/pact_broker/matrix/parse_query.rb +++ b/lib/pact_broker/matrix/parse_query.rb @@ -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 diff --git a/lib/pact_broker/matrix/repository.rb b/lib/pact_broker/matrix/repository.rb index 21e206de4..10a4bb163 100644 --- a/lib/pact_broker/matrix/repository.rb +++ b/lib/pact_broker/matrix/repository.rb @@ -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) @@ -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 @@ -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), @@ -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], @@ -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.&( diff --git a/spec/lib/pact_broker/matrix/repository_spec.rb b/spec/lib/pact_broker/matrix/repository_spec.rb index 5a21c543b..a315bcfdc 100644 --- a/spec/lib/pact_broker/matrix/repository_spec.rb +++ b/spec/lib/pact_broker/matrix/repository_spec.rb @@ -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