diff --git a/lib/pact_broker/integrations/integration.rb b/lib/pact_broker/integrations/integration.rb index 38ffe3312..3e5a7bafe 100644 --- a/lib/pact_broker/integrations/integration.rb +++ b/lib/pact_broker/integrations/integration.rb @@ -64,6 +64,10 @@ def latest_pact_or_verification_publication_date def latest_verification_publication_date latest_verification&.execution_date end + + def <=>(other) + [consumer.name.downcase, provider.name.downcase] <=> [other.consumer.name.downcase, other.provider.name.downcase] + end end end end diff --git a/lib/pact_broker/integrations/service.rb b/lib/pact_broker/integrations/service.rb index d20e930f0..201de8a95 100644 --- a/lib/pact_broker/integrations/service.rb +++ b/lib/pact_broker/integrations/service.rb @@ -4,6 +4,7 @@ require "pact_broker/integrations/integration" require "pact_broker/db/models" require "pact_broker/repositories/helpers" +require "pact_broker/repositories/scopes" module PactBroker module Integrations @@ -11,6 +12,7 @@ class Service extend PactBroker::Repositories extend PactBroker::Services include PactBroker::Logging + extend PactBroker::Repositories::Scopes def self.find_all # The only reason the pact_version needs to be loaded is that @@ -60,8 +62,8 @@ def self.delete_all end end - def self.scope_for(scope) - PactBroker.policy_scope!(scope) + def self.find_for_provider(provider) + scope_for(PactBroker::Integrations::Integration).where(provider_id: provider.id).eager(:consumer).eager(:provider).all.sort end end end diff --git a/lib/pact_broker/pacts/pacts_for_verification_repository.rb b/lib/pact_broker/pacts/pacts_for_verification_repository.rb index 03a7f9dcc..b6848c96d 100644 --- a/lib/pact_broker/pacts/pacts_for_verification_repository.rb +++ b/lib/pact_broker/pacts/pacts_for_verification_repository.rb @@ -99,39 +99,58 @@ def find_pacts_for_fallback_tags(selected_pacts, provider_name, consumer_version end end - # rubocop: disable Metrics/CyclomaticComplexity def find_pacts_by_selector(provider_name, consumer_version_selectors) provider = pacticipant_repository.find_by_name(provider_name) - selectors = if consumer_version_selectors.empty? - Selectors.create_for_overall_latest - else - consumer_version_selectors.select(&:all_for_tag?) + - consumer_version_selectors.select(&:latest_for_tag?) + - consumer_version_selectors.select(&:latest_for_branch?) + - consumer_version_selectors.select(&:latest_for_main_branch?) + - consumer_version_selectors.select(&:overall_latest?) + - consumer_version_selectors.select(&:currently_deployed?) + - consumer_version_selectors.select(&:currently_supported?) - end - - selectors.flat_map do | selector | + specified_selectors_or_defaults(consumer_version_selectors, provider).flat_map do | selector | query = scope_for(PactPublication).for_provider_and_consumer_version_selector(provider, selector) query.all.collect do | pact_publication | - resolved_selector = if selector.currently_deployed? || selector.currently_supported? - environment = environment_service.find_by_name(pact_publication.values.fetch(:environment_name)) - selector.resolve_for_environment(pact_publication.consumer_version, environment, pact_publication.values[:target]) - else - selector.resolve(pact_publication.consumer_version) - end - SelectedPact.new( - pact_publication.to_domain, - Selectors.new(resolved_selector) - ) + create_selected_pact(pact_publication, selector) + end + end + end + + def create_selected_pact(pact_publication, selector) + resolved_selector = if selector.currently_deployed? || selector.currently_supported? + environment = environment_service.find_by_name(pact_publication.values.fetch(:environment_name)) + selector.resolve_for_environment(pact_publication.consumer_version, environment, pact_publication.values[:target]) + else + selector.resolve(pact_publication.consumer_version) + end + SelectedPact.new(pact_publication.to_domain, Selectors.new(resolved_selector)) + end + + def specified_selectors_or_defaults(consumer_version_selectors, provider) + if consumer_version_selectors.empty? + default_selectors(provider) + else + consumer_version_selectors + end + end + + def default_selectors(provider) + selectors = selector_for_latest_main_version_or_overall_latest(provider) + selectors << Selector.for_currently_deployed + selectors << Selector.for_currently_supported + selectors + end + + def selector_for_latest_main_version_or_overall_latest(provider) + selectors = Selectors.new + consumers = integration_service.find_for_provider(provider).collect(&:consumer) + + consumers.collect do | consumer | + if consumer.main_branch && PactBroker::Domain::Version.for_selector(PactBroker::Matrix::UnresolvedSelector.new(branch: consumer.main_branch, latest: true)).any? + selectors << Selector.for_main_branch.for_consumer(consumer.name) + elsif consumer.main_branch && PactBroker::Domain::Version.for_selector(PactBroker::Matrix::UnresolvedSelector.new(tag: consumer.main_branch, latest: true)).any? + selectors << Selector.latest_for_tag(consumer.main_branch).for_consumer(consumer.name) + else + selectors << Selector.overall_latest.for_consumer(consumer.name) end end + + selectors end - # rubocop: enable Metrics/CyclomaticComplexity def find_pacts_for_which_the_latest_version_for_the_fallback_tag_is_required(provider_name, selectors) selectors.collect do | selector | diff --git a/lib/pact_broker/pacts/selector.rb b/lib/pact_broker/pacts/selector.rb index 2284ac872..2817a9025 100644 --- a/lib/pact_broker/pacts/selector.rb +++ b/lib/pact_broker/pacts/selector.rb @@ -101,7 +101,7 @@ def currently_deployed end def currently_deployed? - currently_deployed + !!currently_deployed end def currently_supported= currently_supported @@ -113,7 +113,7 @@ def currently_supported end def currently_supported? - currently_supported + !!currently_supported end def environment_name= environment_name @@ -204,6 +204,10 @@ def self.from_hash hash Selector.new(hash) end + def for_consumer(consumer) + Selector.new(to_h.merge(consumer: consumer)) + end + def latest_for_main_branch? !!main_branch end diff --git a/lib/pact_broker/versions/repository.rb b/lib/pact_broker/versions/repository.rb index 6804018f6..66d2d462f 100644 --- a/lib/pact_broker/versions/repository.rb +++ b/lib/pact_broker/versions/repository.rb @@ -133,7 +133,7 @@ def set_branch_if_unset(version, branch) version.update(branch: branch) if version.branch.nil? version end - + def find_latest_version_from_main_branch(pacticipant) if pacticipant.main_branch latest_from_main_branch = PactBroker::Domain::Version diff --git a/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb b/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb index 252b8c508..8716f93af 100644 --- a/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +++ b/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb @@ -15,26 +15,112 @@ def find_by_consumer_name_and_consumer_version_number(consumer_name, consumer_ve subject { Repository.new.find_for_verification("Bar", consumer_version_selectors) } context "when there are no selectors" do - before do - td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar") - .create_consumer_version_tag("prod") - .create_consumer_version("not-latest-dev-version", tag_names: ["dev"]) - .comment("next pact not selected") - .create_pact - .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"]) - .create_pact - .create_consumer("Baz") - .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"]) - .create_pact - end + + let(:foo_main_branch) { nil } let(:consumer_version_selectors) { Selectors.new } - it "returns the latest pact for each consumer" do - expect(subject.size).to eq 2 - expect(find_by_consumer_name_and_consumer_version_number("Foo", "foo-latest-dev-version")).to_not be nil - expect(find_by_consumer_name_and_consumer_version_number("Baz", "baz-latest-dev-version")).to_not be nil - expect(subject.all?(&:overall_latest?)).to be true + context "when there is no main branch version" do + before do + td.create_consumer("Foo") + .create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar") + .create_consumer_version_tag("prod") + .create_consumer_version("not-latest-dev-version", tag_names: ["dev"]) + .comment("next pact not selected") + .create_pact + .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"]) + .create_pact + .create_consumer("Baz") + .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"]) + .create_pact + end + + it "returns the latest pact for each consumer" do + expect(subject.size).to eq 2 + expect(find_by_consumer_name_and_consumer_version_number("Foo", "foo-latest-dev-version")).to_not be nil + expect(find_by_consumer_name_and_consumer_version_number("Baz", "baz-latest-dev-version")).to_not be nil + expect(subject.all?(&:overall_latest?)).to be true + end + end + + context "when there is a version from the main branch" do + before do + td.create_consumer("Foo", main_branch: "main") + .create_consumer_version("1", branch: "main") + .create_provider("Bar") + .create_pact + .create_pact_with_hierarchy("Foo", "2", "Bar") + end + + it "returns the latest version from the main branch" do + expect(subject.size).to eq 1 + expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to_not be_nil + expect(subject.first.selectors.first).to be_latest_for_main_branch + end + end + + context "when there is a version with a tag with the name of the main branch" do + before do + td.create_consumer("Foo", main_branch: "main") + .create_consumer_version("1", tag_name: "main") + .create_provider("Bar") + .create_pact + .create_pact_with_hierarchy("Foo", "2", "Bar") + end + + it "returns the latest version from the main branch" do + expect(subject.size).to eq 1 + expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to_not be_nil + expect(subject.first.selectors.first).to be_latest_for_tag + expect(subject.first.selectors.first.tag).to eq "main" + end + end + + context "when there is a not version from the main branch" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + .create_pact_with_hierarchy("Foo", "2", "Bar") + end + + it "returns the latest version from the main branch" do + expect(subject.size).to eq 1 + expect(find_by_consumer_name_and_consumer_version_number("Foo", "2")).to_not be_nil + expect(subject.first.selectors.first).to be_overall_latest + end + end + + context "when there are currently deployed versons" do + before do + td.create_environment("test") + .create_pact_with_hierarchy("Foo", "1", "Bar") + .create_deployed_version_for_consumer_version(currently_deployed: false) + .create_pact_with_hierarchy("Foo", "2", "Bar") + .create_deployed_version_for_consumer_version + .create_pact_with_hierarchy("Foo", "3", "Bar") + end + + it "returns the currently deployed pacts" do + expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to be_nil + expect(find_by_consumer_name_and_consumer_version_number("Foo", "2")).to_not be_nil + expect(find_by_consumer_name_and_consumer_version_number("Foo", "2").selectors.first).to be_currently_deployed + end + end + + context "when there are currently released+supported versions" do + before do + td.create_environment("test") + .create_pact_with_hierarchy("Foo", "1", "Bar") + .create_released_version_for_consumer_version(currently_supported: false) + .create_pact_with_hierarchy("Foo", "2", "Bar") + .create_released_version_for_consumer_version + .create_pact_with_hierarchy("Foo", "3", "Bar") + end + + it "returns the currently deployed pacts" do + expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to be_nil + expect(find_by_consumer_name_and_consumer_version_number("Foo", "2")).to_not be_nil + expect(find_by_consumer_name_and_consumer_version_number("Foo", "2").selectors.first).to be_currently_supported + end end end @@ -267,7 +353,7 @@ def find_by_consumer_name_and_consumer_version_number(consumer_name, consumer_ve end it "does not set the tag name" do - expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [ResolvedSelector.new({ latest: true }, PactBroker::Domain::Version.find(number: "foo-latest-dev-version"))] + expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [ResolvedSelector.new({ latest: true, consumer: "Foo" }, PactBroker::Domain::Version.find(number: "foo-latest-dev-version"))] expect(find_by_consumer_version_number("foo-latest-dev-version").overall_latest?).to be true end end