Skip to content

Commit

Permalink
feat: add can-i-deploy endpoint for checking if the latest version fo…
Browse files Browse the repository at this point in the history
…r a branch can be deployed to a particular environment
  • Loading branch information
bethesque committed Feb 21, 2022
1 parent 5b86ac8 commit 34b145e
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 23 deletions.
10 changes: 7 additions & 3 deletions lib/pact_broker/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,19 @@ def self.build_api(application_context = PactBroker::ApplicationContext.default_
add ["pacticipants"], Api::Resources::Pacticipants, {resource_name: "pacticipants"}
add ["pacticipants", "label", :label_name], PactBroker::Api::Resources::PacticipantsForLabel, {resource_name: "pacticipants_for_label"}
add ["pacticipants", :pacticipant_name], Api::Resources::Pacticipant, {resource_name: "pacticipant"}
add ["pacticipants", :pacticipant_name, "labels", :label_name], Api::Resources::Label, {resource_name: "pacticipant_label"}

# Versions
add ["pacticipants", :pacticipant_name, "versions"], Api::Resources::Versions, {resource_name: "pacticipant_versions"}
add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number], Api::Resources::Version, {resource_name: "pacticipant_version"}
add ["pacticipants", :pacticipant_name, "latest-version", :tag], Api::Resources::LatestVersion, {resource_name: "latest_tagged_pacticipant_version"}
add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to], Api::Resources::CanIDeployPacticipantVersion, { resource_name: "can_i_deploy_latest_tagged_version" }
add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to, "badge"], Api::Resources::CanIDeployBadge, { resource_name: "can_i_deploy_badge" }
add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to], Api::Resources::CanIDeployPacticipantVersionByTagToTag, { resource_name: "can_i_deploy_latest_tagged_version_to_tag" }
add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to, "badge"], Api::Resources::CanIDeployBadge, { resource_name: "can_i_deploy_latest_tagged_version_to_tag_badge" }
add ["pacticipants", :pacticipant_name, "latest-version"], Api::Resources::LatestVersion, {resource_name: "latest_pacticipant_version"}
add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "tags", :tag_name], Api::Resources::Tag, {resource_name: "pacticipant_version_tag"}
add ["pacticipants", :pacticipant_name, "labels", :label_name], Api::Resources::Label, {resource_name: "pacticipant_label"}
add ["pacticipants", :pacticipant_name, "branches", :branch_name, "versions", :version_number], Api::Resources::BranchVersion, { resource_name: "branch_version" }
add ["pacticipants", :pacticipant_name, "branches", :branch_name, "latest-version", "can-i-deploy", "to-environment", :environment_name], Api::Resources::CanIDeployPacticipantVersionByBranchToEnvironment, { resource_name: "can_i_deploy_latest_branch_version_to_environment" }
#add ["pacticipants", :pacticipant_name, "branches", :branch_name, "latest-version", "can-i-deploy", "to-environment", :environment_name, "badge"], Api::Resources::CanIDeployPacticipantVersionByBranchToEnvironment, { resource_name: "can_i_deploy_latest_branch_version_to_environment_badge" }

# Webhooks
add ["webhooks", "provider", :provider_name, "consumer", :consumer_name ], Api::Resources::PacticipantWebhooks, {resource_name: "pacticipant_webhooks"}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
require "pact_broker/api/resources/matrix"
require "pact_broker/matrix/can_i_deploy_query_schema"
require "pact_broker/matrix/parse_can_i_deploy_query"

module PactBroker
module Api
module Resources
class CanIDeployPacticipantVersionByBranchToEnvironment < Matrix
def resource_exists?
!!(version && environment)
end

def malformed_request?
false
end

def policy_name
:'matrix::can_i_deploy'
end

private

def selectors
@selectors ||= [
PactBroker::Matrix::UnresolvedSelector.new(
pacticipant_name: pacticipant_name,
latest: true,
branch: identifier_from_path[:branch_name]
)
]
end

def options
@options ||= {
latestby: "cvp",
environment_name: identifier_from_path[:environment_name]
}
end

def version
@version ||= version_service.find_latest_by_pacticipant_name_and_branch_name(identifier_from_path[:pacticipant_name], identifier_from_path[:branch_name])
end

def environment
@environment ||= environment_service.find_by_name(identifier_from_path[:environment_name])
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
module PactBroker
module Api
module Resources
class CanIDeployPacticipantVersion < Matrix
class CanIDeployPacticipantVersionByTagToTag < Matrix
def resource_exists?
!!version
end
Expand All @@ -14,26 +14,29 @@ def policy_name
:'matrix::can_i_deploy'
end

def malformed_request?
false
end

private

def selectors
@selectors ||= begin
[
PactBroker::Matrix::UnresolvedSelector.new(
pacticipant_name: pacticipant_name,
latest: true,
tag: identifier_from_path[:tag]
)
]
end
@selectors ||= [
PactBroker::Matrix::UnresolvedSelector.new(
pacticipant_name: pacticipant_name,
latest: true,
tag: identifier_from_path[:tag],
)
]

end

def options
@options ||= {
latestby: "cvp",
latest: true,
tag: identifier_from_path[:to]
}
latestby: "cvp",
latest: true,
tag: identifier_from_path[:to]
}
end

def version
Expand Down
1 change: 1 addition & 0 deletions lib/pact_broker/matrix/unresolved_selector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def initialize(params = {})
merge!(params)
end

# 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))
end
Expand Down
8 changes: 8 additions & 0 deletions lib/pact_broker/versions/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ def find_by_pacticipant_name_and_latest_tag pacticipant_name, tag
.first
end

def find_latest_by_pacticipant_name_and_branch_name(pacticipant_name, branch_name)
branch_heads_join = { Sequel[:versions][:id] => Sequel[:branch_heads][:version_id], Sequel[:branch_heads][:branch_name] => branch_name }
PactBroker::Domain::Version
.where_pacticipant_name(pacticipant_name)
.join(:branch_heads, branch_heads_join)
.single_record
end

def find_by_pacticipant_name_and_tag pacticipant_name, tag
PactBroker::Domain::Version
.select_all_qualified
Expand Down
4 changes: 4 additions & 0 deletions lib/pact_broker/versions/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def self.find_by_pacticipant_name_and_latest_tag(pacticipant_name, tag)
version_repository.find_by_pacticipant_name_and_latest_tag(pacticipant_name, tag)
end

def self.find_latest_by_pacticipant_name_and_branch_name(pacticipant_name, branch_name)
version_repository.find_latest_by_pacticipant_name_and_branch_name(pacticipant_name, branch_name)
end

def self.create_or_overwrite(pacticipant_name, version_number, version)
pacticipant = pacticipant_repository.find_by_name_or_create(pacticipant_name)
version = version_repository.create_or_overwrite(pacticipant, version_number, version)
Expand Down
21 changes: 20 additions & 1 deletion spec/features/can_i_deploy_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
RSpec.describe "can i deploy" do
before do
td.create_pact_with_hierarchy("Foo", "1.2.3", "Bar")
td.publish_pact(consumer_name: "Foo", provider_name: "Bar", consumer_version_number: "1.2.3", tags: ["dev"], branch: "main")
.create_environment("prod")
end

let(:query) do
Expand All @@ -20,6 +21,24 @@
expect(response_body[:matrix]).to be_instance_of(Array)
end

context "using the URL format for tags" do
subject { get("/pacticipants/Foo/latest-version/dev/can-i-deploy/to/prod", nil, { "HTTP_ACCEPT" => "application/hal+json"}) }

it "returns the matrix response" do
expect(subject).to be_a_hal_json_success_response
expect(response_body[:matrix]).to be_instance_of(Array)
end
end

context "using the URL format for branch/environment" do
subject { get("/pacticipants/Foo/branches/main/latest-version/can-i-deploy/to-environment/prod", nil, { "HTTP_ACCEPT" => "application/hal+json"}) }

it "returns the matrix response" do
expect(subject).to be_a_hal_json_success_response
expect(response_body[:matrix]).to be_instance_of(Array)
end
end

context "with a validation error" do
let(:query) { {} }

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require "pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag"
require "pact_broker/matrix/service"

module PactBroker
module Api
module Resources
describe CanIDeployPacticipantVersionByBranchToEnvironment do
include_context "stubbed services"

before do
allow(PactBroker::Deployments::EnvironmentService).to receive(:find_by_name).and_return(environment)
allow(PactBroker::Matrix::Service).to receive(:can_i_deploy).and_return([])
allow(pacticipant_service).to receive(:find_pacticipant_by_name).and_return(pacticipant)
allow(PactBroker::Api::Decorators::MatrixDecorator).to receive(:new).and_return(decorator)
allow(version_service).to receive(:find_latest_by_pacticipant_name_and_branch_name).and_return(version)
end

let(:pacticipant) { double("pacticipant") }
let(:version) { double("version") }
let(:json_response_body) { JSON.parse(subject.body, symbolize_names: true) }
let(:decorator) { double("decorator", to_json: "response_body") }
let(:selectors) { double("selectors") }
let(:options) { double("options") }
let(:environment) { double("environment") }
let(:path) { "/pacticipants/Foo/branches/main/latest-version/can-i-deploy/to-environment/dev" }

subject { get(path, nil, "Content-Type" => "application/hal+json") }

it "looks up the by branch" do
expect(version_service).to receive(:find_latest_by_pacticipant_name_and_branch_name).with("Foo", "main")
subject
end

it "checks if the version can be deployed to the environment" do
expect(PactBroker::Matrix::Service).to receive(:can_i_deploy).with(anything, hash_including(environment_name: "dev"))
subject
end

it { is_expected.to be_a_hal_json_success_response }

context "when the environment does not exist" do
let(:environment) { nil }

its(:status) { is_expected.to eq 404 }
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
require "pact_broker/api/resources/can_i_deploy_pacticipant_version"
require "pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag"
require "pact_broker/matrix/service"

module PactBroker
module Api
module Resources
describe CanIDeployPacticipantVersion do
describe CanIDeployPacticipantVersionByTagToTag do
include_context "stubbed services"

before do
allow(PactBroker::Matrix::Service).to receive(:can_i_deploy).and_return([])
allow(pacticipant_service).to receive(:find_pacticipant_by_name).and_return(pacticipant)
allow(version_service).to receive(:find_by_pacticipant_name_and_latest_tag).and_return(version)
allow(PactBroker::Api::Decorators::MatrixDecorator).to receive(:new).and_return(decorator)
end

let(:pacticipant) { double("pacticipant") }
let(:version) { double("version") }
let(:path) { "/pacticipants/Foo/latest-version/main/can-i-deploy/to/prod" }
let(:json_response_body) { JSON.parse(subject.body, symbolize_names: true) }
let(:decorator) { double("decorator", to_json: "response_body") }
let(:selectors) { double("selectors") }
let(:options) { double("options") }

subject { get(path, nil, "Content-Type" => "application/hal+json") }

it { is_expected.to be_a_hal_json_success_response }
context "with tags" do
before do
allow(version_service).to receive(:find_by_pacticipant_name_and_latest_tag).and_return(version)
end

let(:path) { "/pacticipants/Foo/latest-version/main/can-i-deploy/to/prod" }

it "looks up the by tag" do
expect(version_service).to receive(:find_by_pacticipant_name_and_latest_tag).with("Foo", "main")
subject
end

it { is_expected.to be_a_hal_json_success_response }

context "when the version does not exist" do
let(:version) { nil }

its(:status) { is_expected.to eq 404 }
end
end
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions spec/lib/pact_broker/versions/repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ module Versions
let(:pacticipant_name) { "test_pacticipant" }
let(:version_number) { "1.2.3" }

describe "#find_latest_by_pacticipant_name_and_branch_name" do
before do
td.create_consumer("Bar")
.create_consumer_version("2.3.4", branch: "prod")
.create_consumer("Foo")
.create_consumer_version("1.2.3", branch: "prod")
.create_consumer_version("2.3.4", branch: "prod")
.create_consumer_version("5.6.7")
end

subject { Repository.new.find_latest_by_pacticipant_name_and_branch_name("Foo", "prod") }

it "returns the most recent version from the specified branch for the specified pacticipant" do
expect(subject.number).to eq "2.3.4"
expect(subject.pacticipant.name).to eq "Foo"
end
end

describe "#find_by_pacticipant_name_and_latest_tag" do
before do
td.create_consumer("Bar")
Expand Down

0 comments on commit 34b145e

Please sign in to comment.