diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index be5612cd3..b53227b36 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -93,7 +93,7 @@ def self.build_api(application_context = PactBroker::ApplicationContext.default_ add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "tags", :tag_name], Api::Resources::Tag, {resource_name: "pacticipant_version_tag"} 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" } + add ["pacticipants", :pacticipant_name, "branches", :branch_name, "latest-version", "can-i-deploy", "to-environment", :environment_name, "badge"], Api::Resources::CanIDeployPacticipantVersionByBranchToEnvironmentBadge, { 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"} diff --git a/lib/pact_broker/api/resources/badge.rb b/lib/pact_broker/api/resources/badge.rb index 805f56eb7..e9ed53331 100644 --- a/lib/pact_broker/api/resources/badge.rb +++ b/lib/pact_broker/api/resources/badge.rb @@ -42,10 +42,6 @@ def pseudo_branch_verification_status @pseudo_branch_verification_status ||= PactBroker::Verifications::PseudoBranchStatus.new(pact, latest_verification).to_sym end - def label - request.query["label"] - end - def initials request.query["initials"] == "true" end diff --git a/lib/pact_broker/api/resources/badge_methods.rb b/lib/pact_broker/api/resources/badge_methods.rb index d00eb6a6c..7fd386283 100644 --- a/lib/pact_broker/api/resources/badge_methods.rb +++ b/lib/pact_broker/api/resources/badge_methods.rb @@ -40,6 +40,17 @@ def moved_temporarily? badge_service.error_badge_url("error", ErrorResponseBodyGenerator.display_message(e, "reference: #{PactBroker::Errors.generate_error_reference}")) end end + + def badge_url + raise NotImplementedError + end + + private + + def label + lab = request.query["label"] + lab && !lab.empty? ? lab : nil + end end end end diff --git a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb index 0f143a104..00ac609f7 100644 --- a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb +++ b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb @@ -1,21 +1,30 @@ require "pact_broker/api/resources/matrix" require "pact_broker/matrix/can_i_deploy_query_schema" require "pact_broker/matrix/parse_can_i_deploy_query" +require "pact_broker/api/decorators/matrix_decorator" +require "pact_broker/api/decorators/matrix_text_decorator" module PactBroker module Api module Resources - class CanIDeployPacticipantVersionByBranchToEnvironment < Matrix - def resource_exists? - !!(version && environment) + class CanIDeployPacticipantVersionByBranchToEnvironment < BaseResource + def allowed_methods + ["GET", "OPTIONS"] end - def malformed_request? - false + def content_types_provided + [ + ["application/hal+json", :to_json], + ["text/plain", :to_text] + ] + end + + def resource_exists? + !!(version && environment) end def policy_name - :'matrix::can_i_deploy' + :'versions::version' end private @@ -37,6 +46,14 @@ def options } end + def to_json + decorator_class(:matrix_decorator).new(results).to_json(decorator_options) + end + + def results + @results ||= matrix_service.can_i_deploy(selectors, options) + 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 diff --git a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb new file mode 100644 index 000000000..89c77da55 --- /dev/null +++ b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb @@ -0,0 +1,29 @@ +require "pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment" + +module PactBroker + module Api + module Resources + class CanIDeployPacticipantVersionByBranchToEnvironmentBadge < CanIDeployPacticipantVersionByBranchToEnvironment + include BadgeMethods + + private + + def badge_url + if pacticipant && version && environment + badge_service.can_i_deploy_badge_url(identifier_from_path[:branch_name], identifier_from_path[:environment_name], label, results.deployable?) + elsif pacticipant.nil? + badge_service.error_badge_url("pacticipant", "not found") + elsif version.nil? + if branch_service.find_branch(identifier_from_path.slice(:pacticipant_name, :branch_name)).nil? + badge_service.error_badge_url("branch", "not found") + else + badge_service.error_badge_url("version", "not found") + end + else + badge_service.error_badge_url("environment", "not found") + end + end + end + end + end +end diff --git a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag.rb b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag.rb index 7e7a140ce..34d944210 100644 --- a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag.rb +++ b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag.rb @@ -11,7 +11,7 @@ def resource_exists? end def policy_name - :'matrix::can_i_deploy' + :'versions::version' end def malformed_request? diff --git a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge.rb b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge.rb index 2e0bd7317..487b06981 100644 --- a/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge.rb +++ b/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge.rb @@ -5,18 +5,8 @@ module PactBroker module Api module Resources - class CanIDeployPacticipantVersionByTagToTagBadge < BaseResource + class CanIDeployPacticipantVersionByTagToTagBadge < CanIDeployPacticipantVersionByTagToTag include BadgeMethods - def initialize - super - selector = PactBroker::Matrix::UnresolvedSelector.new(pacticipant_name: pacticipant_name, latest: true, tag: identifier_from_path[:tag]) - @options = { - latestby: "cvp", - latest: true, - tag: identifier_from_path[:to] - } - @selectors = [selector] - end def badge_url if pacticipant @@ -26,26 +16,9 @@ def badge_url badge_service.error_badge_url("version", "not found") end else - badge_service.error_badge_url(selectors.first.pacticipant_name, "not found") + badge_service.error_badge_url("pacticipant", "not found") end end - - private - - attr_reader :selectors, :options - - def results - @results ||= matrix_service.can_i_deploy(selectors, options) - end - - def version - @version ||= version_service.find_by_pacticipant_name_and_latest_tag(identifier_from_path[:pacticipant_name], identifier_from_path[:tag]) - end - - def label - lab = request.query["label"] - lab && !lab.empty? ? lab : nil - end end end end diff --git a/lib/pact_broker/versions/branch.rb b/lib/pact_broker/versions/branch.rb index d4f0ee3b3..214412130 100644 --- a/lib/pact_broker/versions/branch.rb +++ b/lib/pact_broker/versions/branch.rb @@ -8,6 +8,10 @@ class Branch < Sequel::Model(:branches) plugin :insert_ignore, identifying_columns: [:name, :pacticipant_id] associate(:many_to_one, :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id) + + dataset_module do + include PactBroker::Repositories::Helpers + end end end end diff --git a/lib/pact_broker/versions/branch_service.rb b/lib/pact_broker/versions/branch_service.rb index 826825bb0..cc16f5fe3 100644 --- a/lib/pact_broker/versions/branch_service.rb +++ b/lib/pact_broker/versions/branch_service.rb @@ -19,6 +19,16 @@ def self.find_or_create_branch_version(pacticipant_name:, branch_name:, version_ version = version_repository.find_by_pacticipant_id_and_number_or_create(pacticipant.id, version_number) branch_version_repository.add_branch(version, branch_name) end + + def self.find_branch(pacticipant_name:, branch_name:) + Branch + .select_all_qualified + .join(:pacticipants, { Sequel[:branches][:pacticipant_id] => Sequel[:pacticipants][:id] }) do + PactBroker::Repositories::Helpers.name_like(Sequel[:pacticipants][:name], pacticipant_name) + end + .where(Sequel[:branches][:name] => branch_name) + .single_record + end end end end diff --git a/spec/features/can_i_deploy_spec.rb b/spec/features/can_i_deploy_spec.rb index e197f97dd..b2325b29a 100644 --- a/spec/features/can_i_deploy_spec.rb +++ b/spec/features/can_i_deploy_spec.rb @@ -28,6 +28,17 @@ expect(subject).to be_a_hal_json_success_response expect(response_body[:matrix]).to be_instance_of(Array) end + + context "the badge" do + subject { get("/pacticipants/Foo/latest-version/dev/can-i-deploy/to/prod/badge") } + + it "returns a redirect URL" do + expect(subject.status).to eq 307 + expect(subject.headers["Location"]).to start_with("https://img.shields.io/badge/") + expect(subject.headers["Location"]).to match(/dev/) + expect(subject.headers["Location"]).to match(/prod/) + end + end end context "using the URL format for branch/environment" do @@ -37,6 +48,17 @@ expect(subject).to be_a_hal_json_success_response expect(response_body[:matrix]).to be_instance_of(Array) end + + context "the badge" do + subject { get("/pacticipants/Foo/branches/main/latest-version/can-i-deploy/to-environment/prod/badge") } + + it "returns a redirect URL" do + expect(subject.status).to eq 307 + expect(subject.headers["Location"]).to start_with("https://img.shields.io/badge/") + expect(subject.headers["Location"]).to match(/main/) + expect(subject.headers["Location"]).to match(/prod/) + end + end end context "with a validation error" do diff --git a/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge_spec.rb b/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge_spec.rb new file mode 100644 index 000000000..c77c78b9b --- /dev/null +++ b/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge_spec.rb @@ -0,0 +1,80 @@ +require "pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge" + +module PactBroker + module Api + module Resources + describe CanIDeployPacticipantVersionByBranchToEnvironmentBadge do + before do + allow_any_instance_of(described_class).to receive(:branch_service).and_return(branch_service) + allow_any_instance_of(described_class).to receive(:badge_service).and_return(badge_service) + + allow(branch_service).to receive(:find_branch).and_return(branch) + allow(badge_service). to receive(:can_i_deploy_badge_url).and_return("http://badge_url") + allow(badge_service). to receive(:error_badge_url).and_return("http://error_badge_url") + + allow_any_instance_of(CanIDeployPacticipantVersionByBranchToEnvironmentBadge).to receive(:pacticipant).and_return(pacticipant) + allow_any_instance_of(CanIDeployPacticipantVersionByBranchToEnvironmentBadge).to receive(:version).and_return(version) + allow_any_instance_of(CanIDeployPacticipantVersionByBranchToEnvironmentBadge).to receive(:environment).and_return(environment) + allow_any_instance_of(CanIDeployPacticipantVersionByBranchToEnvironmentBadge).to receive(:results).and_return(results) + end + + let(:branch_service) { class_double("PactBroker::Versions::BranchService").as_stubbed_const } + let(:badge_service) { class_double("PactBroker::Badges::Service").as_stubbed_const } + + let(:pacticipant) { double("pacticipant") } + let(:version) { double("version") } + let(:environment) { double("environment") } + let(:branch) { double("branch") } + let(:results) { instance_double("PactBroker::Matrix::QueryResultsWithDeploymentStatusSummary", deployable?: true )} + + let(:path) { "/pacticipants/Foo/branches/main/latest-version/can-i-deploy/to-environment/dev/badge" } + + subject { get(path, { label: "custom-label" }) } + + context "when everything is found" do + it "return the badge URL" do + expect(badge_service). to receive(:can_i_deploy_badge_url).with("main", "dev", "custom-label", true) + expect(subject.headers["Location"]).to eq "http://badge_url" + end + end + + context "when the pacticipant is not found" do + let(:pacticipant) { nil } + + it "returns an error badge URL" do + expect(badge_service).to receive(:error_badge_url).with("pacticipant", "not found") + expect(subject.headers["Location"]).to eq "http://error_badge_url" + end + end + + context "when the version is not found and the branch is not found" do + let(:version) { nil } + let(:branch) { nil } + + it "returns an error badge URL" do + expect(badge_service).to receive(:error_badge_url).with("branch", "not found") + expect(subject.headers["Location"]).to eq "http://error_badge_url" + end + end + + context "when the version is not found and the branch is found" do + let(:version) { nil } + + it "returns an error badge URL" do + expect(badge_service).to receive(:error_badge_url).with("version", "not found") + expect(subject.headers["Location"]).to eq "http://error_badge_url" + end + end + + context "when the environment is not found" do + let(:environment) { nil } + + it "returns an error badge URL" do + expect(badge_service).to receive(:error_badge_url).with("environment", "not found") + expect(subject.headers["Location"]).to eq "http://error_badge_url" + end + end + end + end + end +end diff --git a/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge_spec.rb b/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge_spec.rb index 5649de639..b237de76e 100644 --- a/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge_spec.rb +++ b/spec/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge_spec.rb @@ -6,72 +6,50 @@ module Resources describe CanIDeployPacticipantVersionByTagToTagBadge do before do allow_any_instance_of(described_class).to receive(:badge_service).and_return(badge_service) - allow_any_instance_of(described_class).to receive(:matrix_service).and_return(matrix_service) - allow_any_instance_of(described_class).to receive(:pacticipant_service).and_return(pacticipant_service) - allow_any_instance_of(described_class).to receive(:version_service).and_return(version_service) - allow(badge_service).to receive(:can_i_deploy_badge_url).and_return("http://badge") - allow(badge_service).to receive(:error_badge_url).and_return("http://error") - allow(matrix_service).to receive(:can_i_deploy).and_return(results) - 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.configuration).to receive(:show_backtrace_in_error_response?).and_return(false) - allow(PactBroker::Errors).to receive(:generate_error_reference).and_return("abcd") + + allow(badge_service). to receive(:can_i_deploy_badge_url).and_return("http://badge_url") + allow(badge_service). to receive(:error_badge_url).and_return("http://error_badge_url") + + allow_any_instance_of(CanIDeployPacticipantVersionByTagToTagBadge).to receive(:pacticipant).and_return(pacticipant) + allow_any_instance_of(CanIDeployPacticipantVersionByTagToTagBadge).to receive(:version).and_return(version) + allow_any_instance_of(CanIDeployPacticipantVersionByTagToTagBadge).to receive(:results).and_return(results) end - let(:pacticipant_service) { class_double("PactBroker::Pacticipant::Service").as_stubbed_const } + let(:branch_service) { class_double("PactBroker::Versions::BranchService").as_stubbed_const } let(:badge_service) { class_double("PactBroker::Badges::Service").as_stubbed_const } - let(:matrix_service) { class_double("PactBroker::Matrix::Service").as_stubbed_const } - let(:version_service) { class_double("PactBroker::Version::Service").as_stubbed_const } - let(:results) { double("results", deployable?: true) } + let(:pacticipant) { double("pacticipant") } let(:version) { double("version") } + let(:environment) { double("environment") } + let(:branch) { double("branch") } + let(:results) { instance_double("PactBroker::Matrix::QueryResultsWithDeploymentStatusSummary", deployable?: true )} let(:path) { "/pacticipants/Foo/latest-version/main/can-i-deploy/to/prod/badge"} - subject { get(path) } + subject { get(path, { label: "custom-label" }) } - context "when the pacticipant exists" do - it "returns a redirect to the badge" do - expect(badge_service).to receive(:can_i_deploy_badge_url).with("main", "prod", nil, true) - expect(subject.status).to eq 307 + context "when everything is found" do + it "return the badge URL" do + expect(badge_service). to receive(:can_i_deploy_badge_url).with("main", "prod", "custom-label", true) + expect(subject.headers["Location"]).to eq "http://badge_url" end end - context "when the pacticipant does not exist" do + context "when the pacticipant is not found" do let(:pacticipant) { nil } - it "returns a redirect to a 'not found' badge" do - expect(badge_service).to receive(:error_badge_url).with("Foo", "not found") - expect(subject.status).to eq 307 + it "returns an error badge URL" do + expect(badge_service).to receive(:error_badge_url).with("pacticipant", "not found") + expect(subject.headers["Location"]).to eq "http://error_badge_url" end end - context "when the version does not exist" do + context "when the version is not found" do let(:version) { nil } - it "returns a redirect to a 'not found' badge" do + it "returns an error badge URL" do expect(badge_service).to receive(:error_badge_url).with("version", "not found") - expect(subject.status).to eq 307 - end - end - - context "with a custom label" do - subject { get(path, label: "some custom label") } - - it "returns a redirect to a badge with a custom label" do - expect(badge_service).to receive(:can_i_deploy_badge_url).with("main", "prod", "some custom label", true) - subject - end - end - - context "when there is an error" do - before do - allow(matrix_service).to receive(:can_i_deploy).and_raise("foo error") - end - - it "returns a redirect to a badge with an error message" do - expect(badge_service).to receive(:error_badge_url).with("error", "reference: abcd") - expect(subject.status).to eq 307 + expect(subject.headers["Location"]).to eq "http://error_badge_url" end end end diff --git a/spec/lib/pact_broker/versions/branch_service_spec.rb b/spec/lib/pact_broker/versions/branch_service_spec.rb index 33a6cb6bf..e3176e822 100644 --- a/spec/lib/pact_broker/versions/branch_service_spec.rb +++ b/spec/lib/pact_broker/versions/branch_service_spec.rb @@ -3,6 +3,31 @@ module PactBroker module Versions describe BranchService do + describe ".find_branch" do + subject { BranchService.find_branch(pacticipant_name: "Foo", branch_name: "main") } + + context "when it exists" do + before do + td.create_consumer("Foo") + .create_consumer_version("1", branch: "main") + .create_consumer_version("1", branch: "not-the-main") + .create_consumer("Bar") + .create_consumer_version("2", branch: "main") + end + + it "is returned" do + expect(subject.pacticipant.name).to eq "Foo" + expect(subject.name).to eq "main" + end + end + + context "when it does not exist" do + it "returns nil" do + expect(subject).to be nil + end + end + end + describe ".find_branch_version" do before do td.create_consumer("Foo")