Skip to content

Commit

Permalink
feat: add support for "can i merge" matrix query
Browse files Browse the repository at this point in the history
  • Loading branch information
bethesque authored May 20, 2022
1 parent b06d447 commit bb108ed
Show file tree
Hide file tree
Showing 17 changed files with 496 additions and 83 deletions.
28 changes: 18 additions & 10 deletions lib/pact_broker/domain/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,41 +141,49 @@ def where_branch_name(branch_name)
where(id: PactBroker::Versions::BranchVersion.select(:version_id))
else
matching_branch_ids = PactBroker::Versions::Branch.select(:id).where(name: branch_name)
matching_branch_version_ids = PactBroker::Versions::BranchVersion
.select(:version_id)
branch_version_ids = PactBroker::Versions::BranchVersion
.select(:version_id, :branch_name)
.where(branch_id: matching_branch_ids)
where(id: matching_branch_version_ids)
select_append(:branch_name)
.join(branch_version_ids, { Sequel[first_source_alias][:id] => Sequel[:bv][:version_id] }, { table_alias: :bv})

end
end

def where_branch_head_name(branch_name)
if branch_name == true
where(id: PactBroker::Versions::BranchHead.select(:version_id))
else
where(id: PactBroker::Versions::BranchHead.select(:version_id).where(branch_name: branch_name))
branch_heads = PactBroker::Versions::BranchHead.select(:version_id, :branch_name).where(branch_name: branch_name)
select_append(:branch_name)
.join(branch_heads, { Sequel[first_source_alias][:id] => Sequel[:bh][:version_id] }, { table_alias: :bh })
end
end


def for_main_branches
matching_branch_version_ids = PactBroker::Versions::BranchVersion
.select(:version_id)
branch_version_ids = PactBroker::Versions::BranchVersion
.select(:version_id, :branch_name)
.join(:pacticipants, { Sequel[:branch_versions][:pacticipant_id] => Sequel[:pacticipants][:id] })
.join(:branches, { Sequel[:branches][:id] => Sequel[:branch_versions][:branch_id], Sequel[:branches][:name] => Sequel[:pacticipants][:main_branch] })

where(id: matching_branch_version_ids)
select_append(Sequel[:bv][:branch_name])
.join(branch_version_ids, { Sequel[first_source_alias][:id] => Sequel[:bv][:version_id] }, table_alias: :bv)

end

def latest_for_main_branches
pacticipants_join = {
Sequel[:branch_heads][:pacticipant_id] => Sequel[:pacticipants][:id],
Sequel[:branch_heads][:branch_name] => Sequel[:pacticipants][:main_branch]
}
matching_branch_version_ids = PactBroker::Versions::BranchHead
.select(:version_id)
branch_head_version_ids = PactBroker::Versions::BranchHead
.select(:version_id, :branch_name)
.join(:pacticipants, pacticipants_join)

where(id: matching_branch_version_ids)
select_append(Sequel[:bh][:branch_name])
.join(branch_head_version_ids, { Sequel[first_source_alias][:id] => Sequel[:bh][:version_id] }, table_alias: :bh)

end

def where_number(number)
Expand Down
1 change: 1 addition & 0 deletions lib/pact_broker/locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ en:
environment_name_must_be_unique: Another environment with name '%{name}' already exists.
cannot_specify_tag_and_environment: Cannot specify both a 'to' tag and an environment.
cannot_specify_latest_and_environment: Cannot specify both latest=true and an environment.
cannot_specify_more_than_one_destination_identifier: Cannot specify more than one of tag, environment and mainBranch.
environment_with_name_not_found: "Environment with name '%{name}' does not exist"
cannot_modify_version_branch: "The branch for a pacticipant version cannot be changed once set (currently '%{old_branch}', proposed value '%{new_branch}'). Your pact publication/verification publication configurations may be in conflict with each other if you are seeing this error. If the current branch value is incorrect, you must delete the pacticipant version resource at %{version_url} and publish the pacts/verification results again with a consistent branch name."
invalid_content_for_content_type: "The content could not be parsed as %{content_type}"
Expand Down
16 changes: 3 additions & 13 deletions lib/pact_broker/matrix/deployment_status_summary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def warning_messages
def bad_practice_warnings
warnings = []

if no_to_tag_or_environment_specified?
if resolved_selectors.count(&:specified?) == 1 && no_to_tag_or_branch_or_environment_specified?
warnings << NoEnvironmentSpecified.new
end

Expand Down Expand Up @@ -91,8 +91,8 @@ def more_than_one_selector_specified?
.any?
end

def no_to_tag_or_environment_specified?
!(query_results.options[:tag] || query_results.options[:environment_name])
def no_to_tag_or_branch_or_environment_specified?
!(query_results.options[:tag] || query_results.options[:environment_name] || query_results.options[:main_branch])
end

def considered_specified_selectors_that_do_not_exist
Expand Down Expand Up @@ -170,16 +170,6 @@ def missing_reasons
end.flatten
end

def selectors_without_a_version_for(integration)
selectors_with_non_existing_versions.select do | selector |
integration.involves_pacticipant_with_name?(selector.pacticipant_name)
end
end

def selectors_with_non_existing_versions
@selectors_with_non_existing_versions ||= resolved_selectors.select(&:latest_tagged_version_that_does_not_exist?)
end

def missing_specified_version_reasons(selectors)
selectors.collect(&:version_does_not_exist_description)
end
Expand Down
5 changes: 5 additions & 0 deletions lib/pact_broker/matrix/parse_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ def self.call query
options[:environment_name] = params["environment"]
end

if params.key?("mainBranch") && params["mainBranch"] != ""
options[:main_branch] = params["mainBranch"] == "true"
end

if params["ignore"].is_a?(Array)
options[:ignore_selectors] = params["ignore"].collect{ |i| parse_selector(i) }
else
Expand All @@ -65,6 +69,7 @@ def self.parse_selector(i)
p.branch = i["branch"] if i["branch"] && i["branch"] != ""
p.tag = i["tag"] if i["tag"] && i["tag"] != ""
p.environment_name = i["environment"] if i["environment"] && i["environment"] != ""
p.main_branch = true if i["mainBranch"] && i["mainBranch"] == "true"
p
end
# rubocop: enable Metrics/CyclomaticComplexity
Expand Down
6 changes: 3 additions & 3 deletions lib/pact_broker/matrix/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,9 @@ def build_resolved_selectors(pacticipant, versions, unresolved_selector, selecto
end

# The user has specified --to TAG or --to-environment ENVIRONMENT in the CLI
# (or nothing, which to defaults to "with the latest version of the other integrated applications")
# The branch isn't implemented in the CLI yet (March 2022), but the API supports it.
# (or nothing, which to defaults to latest=true - "with the latest version of the other integrated applications")
def infer_selectors_for_integrations?(options)
options[:latest] || options[:tag] || options[:branch] || options[:environment_name]
options[:latest] || options[:tag] || options[:branch] || options[:environment_name] || options[:main_branch]
end

# When only one selector is specified, (eg. checking to see if Foo version 2 can be deployed to prod),
Expand All @@ -282,6 +281,7 @@ def build_inferred_selectors(inferred_pacticipant_names, resolved_ignore_selecto
selector = UnresolvedSelector.new(pacticipant_name: pacticipant_name)
selector.tag = options[:tag] if options[:tag]
selector.branch = options[:branch] if options[:branch]
selector.main_branch = options[:main_branch] if options[:main_branch]
selector.latest = options[:latest] if options[:latest]
selector.environment_name = options[:environment_name] if options[:environment_name]
selector
Expand Down
34 changes: 26 additions & 8 deletions lib/pact_broker/matrix/resolved_selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def self.for_pacticipant_and_version(pacticipant, version, original_selector, ty
pacticipant_version_number: version.number,
latest: original_selector[:latest],
tag: original_selector[:tag],
branch: original_selector[:branch],
branch: original_selector[:branch] || (original_selector[:main_branch] ? version&.values[:branch_name] : nil),
main_branch: original_selector[:main_branch],
environment_name: original_selector[:environment_name],
type: type,
ignore: ignore,
Expand All @@ -64,6 +65,7 @@ def self.for_pacticipant_and_non_existing_version(pacticipant, original_selector
latest: original_selector[:latest],
tag: original_selector[:tag],
branch: original_selector[:branch],
main_branch: original_selector[:main_branch],
environment_name: original_selector[:environment_name],
type: type,
ignore: ignore,
Expand Down Expand Up @@ -104,6 +106,11 @@ def branch
self[:branch]
end

# @return [Boolean]
def main_branch?
self[:main_branch]
end

def environment_name
self[:environment_name]
end
Expand All @@ -117,7 +124,7 @@ def most_specific_criterion
end

def only_pacticipant_name_specified?
!!pacticipant_name && !branch && !tag && !latest? && !pacticipant_version_number
!!pacticipant_name && !branch && !tag && !latest? && !pacticipant_version_number && !main_branch?
end

def latest_tagged?
Expand All @@ -128,6 +135,10 @@ def latest_from_branch?
latest? && branch
end

def latest_from_main_branch?
latest? && main_branch?
end

def pacticipant_or_version_does_not_exist?
pacticipant_does_not_exist? || version_does_not_exist?
end
Expand All @@ -140,10 +151,6 @@ def version_does_not_exist?
!version_exists?
end

def latest_tagged_version_that_does_not_exist?
version_does_not_exist? && latest_tagged?
end

def specified_version_that_does_not_exist?
specified? && version_does_not_exist?
end
Expand Down Expand Up @@ -175,16 +182,25 @@ def consider?
!ignore?
end

# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/CyclomaticComplexity, Metrics/MethodLength
def description
if latest_tagged? && pacticipant_version_number
"the latest version of #{pacticipant_name} with tag #{tag} (#{pacticipant_version_number})"
elsif latest_tagged?
"the latest version of #{pacticipant_name} with tag #{tag} (no such version exists)"
elsif main_branch? && pacticipant_version_number.nil?
"a version of #{pacticipant_name} from the main branch (no such version exists)"
elsif latest_from_main_branch? && pacticipant_version_number.nil?
"the latest version of #{pacticipant_name} from the main branch (no such verison exists)"
elsif latest_from_branch? && pacticipant_version_number
"the latest version of #{pacticipant_name} from branch #{branch} (#{pacticipant_version_number})"
elsif latest_from_branch?
"the latest version of #{pacticipant_name} from branch #{branch} (no such version exists)"
elsif branch && pacticipant_version_number
prefix = one_of_many? ? "one of the versions " : "the version "
prefix + "of #{pacticipant_name} from branch #{branch} (#{pacticipant_version_number})"
elsif branch
"a version of #{pacticipant_name} from branch #{branch} (no such version exists)"
elsif latest? && pacticipant_version_number
"the latest version of #{pacticipant_name} (#{pacticipant_version_number})"
elsif latest?
Expand All @@ -208,14 +224,16 @@ def description
"any version of #{pacticipant_name}"
end
end
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/CyclomaticComplexity, Metrics/MethodLength

def version_does_not_exist_description
if version_does_not_exist?
if tag
"No version with tag #{tag} exists for #{pacticipant_name}"
elsif branch
"No version of #{pacticipant_name} from branch #{branch} exists"
elsif main_branch?
"No version of #{pacticipant_name} from the main branch exists"
elsif environment_name
"No version of #{pacticipant_name} is currently recorded as deployed or released in environment #{environment_name}"
elsif pacticipant_version_number
Expand Down
6 changes: 4 additions & 2 deletions lib/pact_broker/matrix/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,10 @@ def validate_selectors selectors, options = {}
error_messages << "Please provide 1 or more version selectors."
end

if options[:tag]&.not_blank? && options[:environment_name]&.not_blank?
error_messages << message("errors.validation.cannot_specify_tag_and_environment")
destination_identifiers = [options[:tag], options[:environment_name], options[:main_branch]&.to_s].compact

if destination_identifiers.size > 1
error_messages << message("errors.validation.cannot_specify_more_than_one_destination_identifier")
end

if options[:latest] && options[:environment_name]&.not_blank?
Expand Down
16 changes: 14 additions & 2 deletions lib/pact_broker/matrix/unresolved_selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(params = {})

# TODO rename branch to branch_name
def self.from_hash(hash)
new(hash.symbolize_keys.snakecase_keys.slice(:pacticipant_name, :pacticipant_version_number, :latest, :tag, :branch, :environment_name))
new(hash.symbolize_keys.snakecase_keys.slice(:pacticipant_name, :pacticipant_version_number, :latest, :tag, :branch, :environment_name, :main_branch))
end

def pacticipant_name
Expand Down Expand Up @@ -42,6 +42,11 @@ def branch
self[:branch]
end

# @return [Boolean]
def main_branch
self[:main_branch]
end

def environment_name
self[:environment_name]
end
Expand All @@ -58,6 +63,11 @@ def branch= branch
self[:branch] = branch
end

# @param [Boolean] main_branch
def main_branch= main_branch
self[:main_branch] = main_branch
end

def environment_name= environment_name
self[:environment_name] = environment_name
end
Expand All @@ -79,9 +89,11 @@ def max_age
self[:max_age]
end

# rubocop: disable Metrics/CyclomaticComplexity
def all_for_pacticipant?
!!pacticipant_name && !pacticipant_version_number && !tag && !branch && !latest && !environment_name && !max_age
!!pacticipant_name && !pacticipant_version_number && !tag && !branch && !latest && !environment_name && !max_age && !main_branch
end
# rubocop: enable Metrics/CyclomaticComplexity

def latest_for_pacticipant_and_tag?
!!(pacticipant_name && tag && latest)
Expand Down
14 changes: 14 additions & 0 deletions lib/pact_broker/test/http_test_data_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,20 @@ def can_i_deploy(pacticipant:, version:, to: nil, to_environment: nil)
self
end

def can_i_merge(pacticipant:, version:)
can_i_merge_response = client.get("matrix", { q: [pacticipant: pacticipant, version: version], latestby: "cvp", mainBranch: true, latest: true }.compact ).tap { |response| check_for_error(response) }
can = !!(can_i_merge_response.body["summary"] || {})["deployable"]
puts "can-i-merge #{pacticipant} version #{version}: #{can ? 'yes' : 'no'}"
summary = can_i_merge_response.body["summary"]
verification_result_urls = (can_i_merge_response.body["matrix"] || []).collect do | row |
row.dig("verificationResult", "_links", "self", "href")
end.compact
summary.merge!("verification_result_urls" => verification_result_urls)
puts summary.to_yaml
separate
self
end

def delete_integration(consumer:, provider:)
puts "Deleting all data for the integration between #{consumer} and #{provider}"
client.delete("integrations/provider/#{encode(provider)}/consumer/#{encode(consumer)}").tap { |response| check_for_error(response) }
Expand Down
7 changes: 5 additions & 2 deletions lib/pact_broker/ui/views/matrix/show.haml
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,22 @@
%input{name: 'q[]latest', value: 'true', hidden: true, class: 'latest-flag'}


- if options.latest || options.tag
%input{name: 'latest', value: options.latest.to_s, hidden: true }
%input{name: 'mainBranch', value: options.mainBranch.to_s, hidden: true}

- if options.tag
.selector
%label{for: 'to'}
= options.latest ? 'To (tag)' : 'With all (tagged)'
%input{name: 'tag', id: 'to', value: options.tag }
%input{name: 'latest', value: options.latest.to_s, hidden: true}

- if options.environment_name
.selector
%label{for: 'environment'}
To environment
%input{name: 'environment', id: 'environment', value: options.environment_name }


%div.top-of-group.form-check
%input{type: 'radio', name: "latestby", class: 'form-check-input', value: '', id: 'all_rows', checked: options.all_rows_checked}
%label{for: 'all_rows', class: "form-check-label"}
Expand Down
24 changes: 24 additions & 0 deletions script/data/can-i-merge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env ruby
begin

$LOAD_PATH << "#{Dir.pwd}/lib"
require "pact_broker/test/http_test_data_builder"
base_url = ENV["PACT_BROKER_BASE_URL"] || "http://localhost:9292"

td = PactBroker::Test::HttpTestDataBuilder.new(base_url)
td.delete_pacticipant("Foo")
.delete_pacticipant("Bar")
.create_pacticipant("Foo", main_branch: "main")
.create_pacticipant("Bar", main_branch: "main")
.publish_pact(consumer: "Foo", consumer_version: "1", provider: "Bar", content_id: "111", branch: "feat/x")
.get_pacts_for_verification(provider_version_branch: "main")
.verify_pact(provider_version_branch: "main", provider_version: "1", success: false)
.verify_pact(provider_version_branch: "main", provider_version: "2", success: false)
.can_i_merge(pacticipant: "Foo", version: "1")


rescue StandardError => e
puts "#{e.class} #{e.message}"
puts e.backtrace
exit 1
end
Loading

0 comments on commit bb108ed

Please sign in to comment.