Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: currently deployed selector #396

Merged
merged 4 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class VerifiablePactsJSONQuerySchema
optional(:latest).filled(included_in?: [true, false])
optional(:fallbackTag).filled(:str?)
optional(:consumer).filled(:str?, :not_blank?)
optional(:currentlyDeployed).filled(included_in?: [true])
optional(:environment).filled(:str?)

# rule(fallbackTagMustBeForLatest: [:fallbackTag, :latest]) do | fallback_tag, latest |
# fallback_tag.filled?.then(latest.eql?(true))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class VerifiablePactsQueryDecorator < BaseDecorator
}
property :fallback_tag
property :consumer
property :environment
property :currently_deployed
end

property :include_pending_status, default: false,
Expand Down
18 changes: 18 additions & 0 deletions lib/pact_broker/pacts/pact_publication_dataset_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def for_provider_and_consumer_version_selector provider, selector
# Do this last so that the provider/consumer criteria get included in the "latest" query before the join, rather than after
query = query.latest_for_consumer_branch(selector.branch) if selector.latest_for_branch?
query = query.latest_for_consumer_tag(selector.tag) if selector.latest_for_tag?
query = query.for_currently_deployed_versions(selector.environment) if selector.currently_deployed?
query = query.overall_latest if selector.overall_latest?
query
end
Expand Down Expand Up @@ -149,6 +150,23 @@ def latest_for_consumer_tag(tag_name)
.remove_overridden_revisions_from_complete_query
end

def for_currently_deployed_versions(environment_name)
deployed_versions_join = {
Sequel[:pact_publications][:consumer_version_id] => Sequel[:deployed_versions][:version_id],
Sequel[:deployed_versions][:currently_deployed] => true
}
environments_join = {
Sequel[:deployed_versions][:environment_id] => Sequel[:environments][:id],
Sequel[:environments][:name] => environment_name
}.compact

query = self
if no_columns_selected?
query = query.select_all_qualified.select_append(Sequel[:environments][:name].as(:environment_name))
end
query.join(:deployed_versions, deployed_versions_join).join(:environments, environments_join)
end

def successfully_verified_by_provider_branch(provider_id, provider_version_branch)
verifications_join = {
pact_version_id: :pact_version_id,
Expand Down
15 changes: 11 additions & 4 deletions lib/pact_broker/pacts/pacts_for_verification_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class PactsForVerificationRepository
include PactBroker::Repositories::Helpers

def find(provider_name, consumer_version_selectors)
selected_pacts = find_pacts_for_which_the_latest_version_of_something_is_required(provider_name, consumer_version_selectors) +
selected_pacts = find_pacts_by_selector(provider_name, consumer_version_selectors) +
find_pacts_for_which_all_versions_for_the_tag_are_required(provider_name, consumer_version_selectors)
selected_pacts = selected_pacts + find_pacts_for_fallback_tags(selected_pacts, provider_name, consumer_version_selectors)
merge_selected_pacts(selected_pacts)
Expand Down Expand Up @@ -101,23 +101,29 @@ def find_pacts_for_fallback_tags(selected_pacts, provider_name, consumer_version
end
end

def find_pacts_for_which_the_latest_version_of_something_is_required(provider_name, consumer_version_selectors)
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(&:latest_for_tag?) +
consumer_version_selectors.select(&:latest_for_branch?) +
consumer_version_selectors.select(&:overall_latest?)
consumer_version_selectors.select(&:overall_latest?) +
consumer_version_selectors.select(&:currently_deployed?)
end

selectors.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.resolve_for_environment(pact_publication.consumer_version, pact_publication.values.fetch(:environment_name))
else
selector.resolve(pact_publication.consumer_version)
end
SelectedPact.new(
pact_publication.to_domain,
Selectors.new(selector.resolve(pact_publication.consumer_version))
Selectors.new(resolved_selector)
)
end
end
Expand All @@ -141,6 +147,7 @@ def find_pacts_for_which_the_latest_version_for_the_fallback_tag_is_required(pro

def find_pacts_for_which_all_versions_for_the_tag_are_required(provider_name, consumer_version_selectors)
# The tags for which all versions are specified
# Need to move support for this into PactPublication.for_provider_and_consumer_version_selector
selectors = consumer_version_selectors.select(&:all_for_tag?)

selectors.flat_map do | selector |
Expand Down
62 changes: 61 additions & 1 deletion lib/pact_broker/pacts/selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ def resolve_for_fallback(consumer_version)
ResolvedSelector.new(self.to_h, consumer_version)
end

def resolve_for_environment(consumer_version, environment)
ResolvedSelector.new(self.to_h.merge(environment: environment), consumer_version)
end

# Only currently used to identify the currently_deployed from the others in
# verifiable_pact_messages, so don't need the "for_consumer" sub category
def type
if latest_for_branch?
:latest_for_branch
elsif currently_deployed?
:currently_deployed
elsif latest_for_tag?
:latest_for_tag
elsif all_for_tag?
:all_for_tag
elsif overall_latest?
:overall_latest
else
:undefined
end
end

def tag= tag
self[:tag] = tag
end
Expand Down Expand Up @@ -57,6 +79,26 @@ def consumer
self[:consumer]
end

def currently_deployed= currently_deployed
self[:currently_deployed] = currently_deployed
end

def currently_deployed
self[:currently_deployed]
end

def currently_deployed?
currently_deployed
end

def environment= environment
self[:environment] = environment
end

def environment
self[:environment]
end

def self.overall_latest
Selector.new(latest: true)
end
Expand Down Expand Up @@ -97,6 +139,18 @@ def self.latest_for_consumer(consumer)
Selector.new(latest: true, consumer: consumer)
end

def self.for_currently_deployed(environment = nil)
Selector.new( { currently_deployed: true, environment: environment }.compact )
end

def self.for_currently_deployed_and_consumer(consumer)
Selector.new(currently_deployed: true, consumer: consumer)
end

def self.for_currently_deployed_and_environment_and_consumer(environment, consumer)
Selector.new(currently_deployed: true, environment: environment, consumer: consumer)
end

def self.from_hash hash
Selector.new(hash)
end
Expand All @@ -118,7 +172,7 @@ def branch
end

def overall_latest?
!!(latest? && !tag && !branch)
!!(latest? && !tag && !branch && !currently_deployed && !environment)
end

# Not sure if the fallback_tag logic is needed
Expand Down Expand Up @@ -164,6 +218,12 @@ def <=> other
else
latest_for_branch? ? -1 : 1
end
elsif currently_deployed? || other.currently_deployed?
if currently_deployed? == other.currently_deployed?
environment <=> other.environment
else
currently_deployed? ? -1 : 1
end
elsif latest_for_tag? || other.latest_for_tag?
if latest_for_tag? == other.latest_for_tag?
tag <=> other.tag
Expand Down
28 changes: 23 additions & 5 deletions lib/pact_broker/pacts/verifiable_pact_messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ def pact_version_short_description
attr_reader :verifiable_pact, :pact_version_url

def join(list, last_joiner = " and ")
quoted_list = list.collect { | tag | "'#{tag}'" }
comma_joined = quoted_list[0..-3] || []
and_joined = quoted_list[-2..-1] || quoted_list
join_unquoted(list.collect { | word | "'#{word}'" }, last_joiner = " and ")
end

def join_unquoted(list, last_joiner = " and ")
comma_joined = list[0..-3] || []
and_joined = list[-2..-1] || list
if comma_joined.any?
"#{comma_joined.join(', ')}, #{and_joined.join(last_joiner)}"
else
Expand Down Expand Up @@ -153,11 +156,24 @@ def branches
end

def selector_descriptions
selectors.sort.collect do | selector |
selector_description(selector)
selectors.sort.group_by(&:type).values.flat_map do | selectors |
selectors_descriptions(selectors)
end.join(", ")
end

def selectors_descriptions(selectors)
if selectors.first.currently_deployed?
selectors.group_by(&:consumer).flat_map do | consumer_name, selectors |
display_name = consumer_name ? "the version(s) of #{consumer_name}" : "the consumer version(s)"
"pacts for #{display_name} currently deployed to #{join_unquoted(selectors.collect(&:environment))}"
end
else
selectors.collect do | selector |
selector_description(selector)
end
end
end

def selector_description selector
if selector.overall_latest?
consumer_label = selector.consumer ? selector.consumer : 'a consumer'
Expand All @@ -180,6 +196,8 @@ def selector_description selector
"pacts for all #{selector.consumer} versions tagged '#{selector.tag}'"
elsif selector.all_for_tag?
"pacts for all consumer versions tagged '#{selector.tag}'"
elsif selector.currently_deployed?
"pacts for consumer version(s) currently deployed to #{selector.environment}"
else
selector.to_json
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
require 'pact_broker/pacts/repository'

module PactBroker
module Pacts
describe Repository do
describe "#find_for_verification" do
def find_by_consumer_version_number(consumer_version_number)
subject.find{ |pact| pact.consumer_version_number == consumer_version_number }
end

def find_by_consumer_name_and_consumer_version_number(consumer_name, consumer_version_number)
subject.find{ |pact| pact.consumer_name == consumer_name && pact.consumer_version_number == consumer_version_number }
end

subject { Repository.new.find_for_verification("Bar", consumer_version_selectors) }

context "when currently_deployed is true" 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(currently_deployed: true)
.create_pact_with_hierarchy("Waffle", "3", "Bar")
.create_pact_with_hierarchy("Waffle", "4", "Bar")
.create_deployed_version_for_consumer_version(currently_deployed: true)
end

let(:consumer_version_selectors) do
PactBroker::Pacts::Selectors.new(
PactBroker::Pacts::Selector.for_currently_deployed
)
end

it "returns the pacts for the currently deployed versions" do
expect(subject.size).to eq 2
expect(subject.first.selectors).to eq [PactBroker::Pacts::Selector.for_currently_deployed.resolve_for_environment(td.find_version("Foo", "2"), "test")]
expect(subject.last.selectors).to eq [PactBroker::Pacts::Selector.for_currently_deployed.resolve_for_environment(td.find_version("Waffle", "4"), "test")]
end
end

context "when currently_deployed is true and an environment is specified" 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(currently_deployed: true)
.create_pact_with_hierarchy("Waffle", "3", "Bar")
.create_pact_with_hierarchy("Waffle", "4", "Bar")
.create_deployed_version_for_consumer_version(currently_deployed: true)
.create_environment("prod")
.create_pact_with_hierarchy("Foo", "5", "Bar")
.comment("not included, wrong environment")
.create_deployed_version_for_consumer_version(currently_deployed: true)
end

let(:consumer_version_selectors) do
PactBroker::Pacts::Selectors.new(
PactBroker::Pacts::Selector.for_currently_deployed("test")
)
end

it "returns the pacts for the currently deployed versions" do
expect(subject.size).to eq 2
expect(subject.first.selectors).to eq [PactBroker::Pacts::Selector.for_currently_deployed("test").resolve(td.find_version("Foo", "2"))]
expect(subject.last.selectors).to eq [PactBroker::Pacts::Selector.for_currently_deployed("test").resolve(td.find_version("Waffle", "4"))]
end
end

context "when currently_deployed is true and an environment is and consumer specified" 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(currently_deployed: true)
.create_pact_with_hierarchy("Waffle", "3", "Bar")
.create_pact_with_hierarchy("Waffle", "4", "Bar")
.create_deployed_version_for_consumer_version(currently_deployed: true)
.create_environment("prod")
.create_pact_with_hierarchy("Foo", "5", "Bar")
.comment("not included, wrong environment")
.create_deployed_version_for_consumer_version(currently_deployed: true)
end

let(:consumer_version_selectors) do
PactBroker::Pacts::Selectors.new(
PactBroker::Pacts::Selector.for_currently_deployed_and_environment_and_consumer("test", "Foo")
)
end

it "returns the pacts for the currently deployed versions" do
expect(subject.size).to eq 1
expect(subject.first.selectors).to eq [PactBroker::Pacts::Selector.for_currently_deployed_and_environment_and_consumer("test", "Foo").resolve(td.find_version("Foo", "2"))]
end
end

context "when the same version is deployed to multiple environments" do
before do
td.create_environment("test")
.create_environment("prod")
.create_pact_with_hierarchy("Foo", "1", "Bar")
.create_deployed_version_for_consumer_version(environment_name: "test")
.create_deployed_version_for_consumer_version(environment_name: "prod")
end

let(:consumer_version_selectors) do
PactBroker::Pacts::Selectors.new(
PactBroker::Pacts::Selector.for_currently_deployed
)
end

it "returns one pact_publication with multiple selectors" do
expect(subject.size).to eq 1
expect(subject.first.selectors.size).to eq 2
expect(subject.first.selectors.first.environment).to eq "test"
expect(subject.first.selectors.last.environment).to eq "prod"
end
end
end
end
end
end
6 changes: 4 additions & 2 deletions spec/lib/pact_broker/pacts/selector_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ module Pacts
let(:all_dev_for_consumer_1) { Selector.all_for_tag_and_consumer('dev', 'Bar') }
let(:all_prod) { Selector.all_for_tag('prod') }
let(:all_dev) { Selector.all_for_tag('dev') }
let(:currently_deployed_to_prod) { Selector.for_currently_deployed('prod') }
let(:currently_deployed_to_test) { Selector.for_currently_deployed('test') }

let(:unsorted_selectors) do
[all_prod, all_dev, all_dev_for_consumer_1, latest_for_branch_main, latest_for_tag_prod, overall_latest_1, overall_latest_1, latest_for_tag_dev, all_prod_for_consumer_2, all_prod_for_consumer_1]
[all_prod, all_dev, currently_deployed_to_prod, all_dev_for_consumer_1, latest_for_branch_main, latest_for_tag_prod, currently_deployed_to_test, overall_latest_1, overall_latest_1, latest_for_tag_dev, all_prod_for_consumer_2, all_prod_for_consumer_1]
end

let(:expected_sorted_selectors) do
[overall_latest_1, overall_latest_1, latest_for_branch_main, latest_for_tag_dev, latest_for_tag_prod, all_dev_for_consumer_1, all_prod_for_consumer_2, all_prod_for_consumer_1, all_dev, all_prod]
[overall_latest_1, overall_latest_1, latest_for_branch_main, currently_deployed_to_prod, currently_deployed_to_test, latest_for_tag_dev, latest_for_tag_prod, all_dev_for_consumer_1, all_prod_for_consumer_2, all_prod_for_consumer_1, all_dev, all_prod]
end

it "sorts the selectors" do
Expand Down
Loading