diff --git a/README.md b/README.md
index 664aaae..e1719f4 100644
--- a/README.md
+++ b/README.md
@@ -859,6 +859,38 @@ Options:
Test the execution of a webhook
+### Branches
+
+#### delete-branch
+
+```
+Usage:
+ pact-broker delete-branch --branch=BRANCH -a, --pacticipant=PACTICIPANT -b, --broker-base-url=BROKER_BASE_URL
+
+Options:
+ -a, --pacticipant=PACTICIPANT
+ # The name of the pacticipant that the branch belongs to.
+ --branch=BRANCH
+ # The pacticipant branch name.
+ [--error-when-not-found], [--no-error-when-not-found]
+ # Raise an error if the branch that is to be deleted is not
+ found.
+ # Default: true
+ -b, --broker-base-url=BROKER_BASE_URL
+ # The base URL of the Pact Broker
+ -u, [--broker-username=BROKER_USERNAME]
+ # Pact Broker basic auth username
+ -p, [--broker-password=BROKER_PASSWORD]
+ # Pact Broker basic auth password
+ -k, [--broker-token=BROKER_TOKEN]
+ # Pact Broker bearer token
+ -v, [--verbose], [--no-verbose]
+ # Verbose output.
+ # Default: false
+```
+
+Deletes a pacticipant branch. Does not delete the versions or pacts/verifications associated with the branch, but does make the pacts inaccessible for verification via consumer versions selectors or WIP pacts.
+
### Tags
#### create-version-tag
diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
index 3580889..39cb014 100644
--- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
+++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
@@ -38,6 +38,8 @@
* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:latest-version_relation_exists_in_the_index_resource) given the pb:latest-version relation exists in the index resource
+* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:pacticipant-branch_relation_exists_in_the_index_resource) given the pb:pacticipant-branch relation exists in the index resource
+
* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:pacticipant-version_and_pb:environments_relations_exist_in_the_index_resource) given the pb:pacticipant-version and pb:environments relations exist in the index resource
* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:publish-contracts_relations_exists_in_the_index_resource) given the pb:publish-contracts relations exists in the index resource
@@ -76,6 +78,8 @@
* [A request to create a webhook with every possible event type](#a_request_to_create_a_webhook_with_every_possible_event_type_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker) given the 'Pricing Service' and 'Condor' already exist in the pact-broker
+* [A request to delete a pacticipant branch](#a_request_to_delete_a_pacticipant_branch_given_a_branch_named_main_exists_for_pacticipant_Foo) given a branch named main exists for pacticipant Foo
+
* [A request to determine if Bar can be deployed with all Foo tagged prod, ignoring the verification for Foo version 3.4.5](#a_request_to_determine_if_Bar_can_be_deployed_with_all_Foo_tagged_prod,_ignoring_the_verification_for_Foo_version_3.4.5_given_provider_Bar_version_4.5.6_has_a_successful_verification_for_Foo_version_1.2.3_tagged_prod_and_a_failed_verification_for_version_3.4.5_tagged_prod) given provider Bar version 4.5.6 has a successful verification for Foo version 1.2.3 tagged prod and a failed verification for version 3.4.5 tagged prod
* [A request to get the Pricing Service](#a_request_to_get_the_Pricing_Service_given_the_'Pricing_Service'_already_exists_in_the_pact-broker) given the 'Pricing Service' already exists in the pact-broker
@@ -827,6 +831,33 @@ Pact Broker will respond with:
}
}
```
+
+Given **the pb:pacticipant-branch relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, with
+```json
+{
+ "method": "GET",
+ "path": "/",
+ "headers": {
+ "Accept": "application/hal+json"
+ }
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "pb:pacticipant-branch": {
+ "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-PACTICIPANT-BRANCH-{pacticipant}-{branch}"
+ }
+ }
+ }
+}
+```
Given **the pb:pacticipant-version and pb:environments relations exist in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, with
```json
@@ -1766,6 +1797,20 @@ Pact Broker will respond with:
}
}
```
+
+Given **a branch named main exists for pacticipant Foo**, upon receiving **a request to delete a pacticipant branch** from Pact Broker Client, with
+```json
+{
+ "method": "DELETE",
+ "path": "/HAL-REL-PLACEHOLDER-PB-PACTICIPANT-BRANCH-Foo-main"
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 204
+}
+```
Given **provider Bar version 4.5.6 has a successful verification for Foo version 1.2.3 tagged prod and a failed verification for version 3.4.5 tagged prod**, upon receiving **a request to determine if Bar can be deployed with all Foo tagged prod, ignoring the verification for Foo version 3.4.5** from Pact Broker Client, with
```json
diff --git a/lib/pact_broker/client/branches/delete_branch.rb b/lib/pact_broker/client/branches/delete_branch.rb
new file mode 100644
index 0000000..a57c5a2
--- /dev/null
+++ b/lib/pact_broker/client/branches/delete_branch.rb
@@ -0,0 +1,64 @@
+require "pact_broker/client/base_command"
+
+module PactBroker
+ module Client
+ module Branches
+ class DeleteBranch < PactBroker::Client::BaseCommand
+
+ NOT_SUPPORTED_MESSAGE_PACT_BROKER = "This version of the Pact Broker does not support deleting branches. Please upgrade to version 2.108.0 or later."
+ NOT_SUPPORTED_MESSAGE_PACTFLOW = "This version of PactFlow does not support deleting branches. Please upgrade to the latest version."
+
+ def initialize(params, options, pact_broker_client_options)
+ super
+ @pacticipant_name = params.fetch(:pacticipant)
+ @branch_name = params.fetch(:branch)
+ @error_when_not_found = params.fetch(:error_when_not_found)
+ end
+
+ def do_call
+ check_if_command_supported
+ @deleted_resource = branch_link.delete
+ PactBroker::Client::CommandResult.new(success?, result_message)
+ end
+
+ private
+
+ attr_reader :pacticipant_name, :branch_name, :error_when_not_found, :deleted_resource
+
+ def branch_link
+ index_resource._link("pb:pacticipant-branch").expand(pacticipant: pacticipant_name, branch: branch_name)
+ end
+
+ def check_if_command_supported
+ unless index_resource.can?("pb:pacticipant-branch")
+ raise PactBroker::Client::Error.new(is_pactflow? ? NOT_SUPPORTED_MESSAGE_PACTFLOW : NOT_SUPPORTED_MESSAGE_PACT_BROKER)
+ end
+ end
+
+ def success?
+ if deleted_resource.success?
+ true
+ elsif deleted_resource.response.status == 404 && !error_when_not_found
+ true
+ else
+ false
+ end
+ end
+
+ def result_message
+ if deleted_resource.success?
+ green("Successfully deleted branch #{branch_name} of pacticipant #{pacticipant_name}")
+ elsif deleted_resource.response.status == 404
+ if error_when_not_found
+ red("Could not delete branch #{branch_name} of pacticipant #{pacticipant_name} as it was not found")
+ else
+ green("Branch #{branch_name} of pacticipant #{pacticipant_name} not found")
+ end
+ else
+ red(deleted_resource.response.raw_body)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pact_broker/client/cli/branch_commands.rb b/lib/pact_broker/client/cli/branch_commands.rb
new file mode 100644
index 0000000..a0b17f6
--- /dev/null
+++ b/lib/pact_broker/client/cli/branch_commands.rb
@@ -0,0 +1,40 @@
+module PactBroker
+ module Client
+ module CLI
+ module BranchCommands
+ def self.included(thor)
+ thor.class_eval do
+ method_option :pacticipant, required: true, aliases: "-a", desc: "The name of the pacticipant that the branch belongs to."
+ method_option :branch, required: true, desc: "The pacticipant branch name."
+ method_option :error_when_not_found, type: :boolean, default: true, desc: "Raise an error if the branch that is to be deleted is not found."
+ shared_authentication_options
+
+ desc "delete-branch", "Deletes a pacticipant branch. Does not delete the versions or pacts/verifications associated with the branch, but does make the pacts inaccessible for verification via consumer versions selectors or WIP pacts."
+
+ def delete_branch
+ require "pact_broker/client/branches/delete_branch"
+
+ validate_credentials
+ params = {
+ pacticipant: options.pacticipant,
+ branch: options.branch,
+ error_when_not_found: options.error_when_not_found
+ }
+
+ result = PactBroker::Client::Branches::DeleteBranch.call(params, {}, pact_broker_client_options)
+ $stdout.puts result.message
+ exit(1) unless result.success
+ end
+
+ no_commands do
+ def validate_delete_branch_params
+ raise ::Thor::RequiredArgumentMissingError, "Pacticipant name cannot be blank" if options.pacticipant.strip.size == 0
+ raise ::Thor::RequiredArgumentMissingError, "Pacticipant branch name cannot be blank" if options.branch.strip.size == 0
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb
index 827c4fc..962a784 100644
--- a/lib/pact_broker/client/cli/broker.rb
+++ b/lib/pact_broker/client/cli/broker.rb
@@ -8,7 +8,7 @@
require "pact_broker/client/cli/version_commands"
require "pact_broker/client/cli/webhook_commands"
require "pact_broker/client/cli/matrix_commands"
-
+require "pact_broker/client/cli/branch_commands"
module PactBroker
module Client
module CLI
@@ -19,6 +19,7 @@ class Broker < CustomThor
include PactBroker::Client::CLI::MatrixCommands
include PactBroker::Client::CLI::PacticipantCommands
include PactBroker::Client::CLI::VersionCommands
+ include PactBroker::Client::CLI::BranchCommands
include PactBroker::Client::CLI::WebhookCommands
ignored_and_hidden_potential_options_from_environment_variables
diff --git a/script/update-cli-usage-in-readme.rb b/script/update-cli-usage-in-readme.rb
index b69b1c5..f074ef0 100755
--- a/script/update-cli-usage-in-readme.rb
+++ b/script/update-cli-usage-in-readme.rb
@@ -23,6 +23,7 @@
[PactBroker::Client::CLI::Broker, "Matrix", %w[can-i-deploy can-i-merge]],
[PactBroker::Client::CLI::Broker, "Pacticipants", %w[create-or-update-pacticipant describe-pacticipant list-pacticipants]],
[PactBroker::Client::CLI::Broker, "Webhooks", %w[create-webhook create-or-update-webhook test-webhook]],
+ [PactBroker::Client::CLI::Broker, "Branches", %w[delete-branch]],
[PactBroker::Client::CLI::Broker, "Tags", %w[create-version-tag]],
[PactBroker::Client::CLI::Broker, "Versions", %w[describe-version create-or-update-version]],
[PactBroker::Client::CLI::Broker, "Miscellaneous", %w[generate-uuid]]
diff --git a/spec/lib/pact_broker/client/branches/delete_branch_spec.rb b/spec/lib/pact_broker/client/branches/delete_branch_spec.rb
new file mode 100644
index 0000000..c6b8008
--- /dev/null
+++ b/spec/lib/pact_broker/client/branches/delete_branch_spec.rb
@@ -0,0 +1,103 @@
+require "pact_broker/client/branches/delete_branch"
+
+module PactBroker
+ module Client
+ module Branches
+ describe DeleteBranch do
+ before do
+ allow_any_instance_of(PactBroker::Client::Hal::HttpClient).to receive(:sleep)
+ allow_any_instance_of(PactBroker::Client::Hal::HttpClient).to receive(:default_max_tries).and_return(1)
+ end
+
+ let(:params) do
+ {
+ pacticipant: "Foo",
+ branch: "main",
+ error_when_not_found: error_when_not_found
+ }
+ end
+ let(:options) do
+ {
+ verbose: verbose
+ }
+ end
+ let(:error_when_not_found) { true }
+ let(:pact_broker_base_url) { "http://example.org" }
+ let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } }
+ let(:response_headers) { { "Content-Type" => "application/hal+json"} }
+ let(:verbose) { false }
+
+ before do
+ stub_request(:get, "http://example.org/").to_return(status: 200, body: index_response_body, headers: response_headers)
+ stub_request(:delete, "http://example.org/pacticipants/Foo/branches/main").to_return(status: delete_response_status, body: delete_response_body, headers: response_headers)
+ end
+ let(:delete_response_status) { 200 }
+
+ let(:index_response_body) do
+ {
+ "_links" => {
+ "pb:pacticipant-branch" => {
+ "href" => "http://example.org/pacticipants/{pacticipant}/branches/{branch}"
+ }
+ }
+ }.to_json
+ end
+
+ let(:delete_response_body) do
+ { "some" => "error message" }.to_json
+ end
+
+ subject { DeleteBranch.call(params, options, pact_broker_client_options) }
+
+ context "when the branch is deleted" do
+ it "returns a success result" do
+ expect(subject.success).to be true
+ expect(subject.message).to include "Successfully deleted branch main of pacticipant Foo"
+ end
+ end
+
+ context "when there is a non-404 error" do
+ let(:delete_response_status) { 403 }
+
+ it "returns an error result with the response body" do
+ expect(subject.success).to be false
+ expect(subject.message).to include "error message"
+ end
+ end
+
+ context "when the branch is not found" do
+ let(:delete_response_status) { 404 }
+
+ context "when error_when_not_found is true" do
+ it "returns an error" do
+ expect(subject.success).to be false
+ expect(subject.message).to include "Could not delete branch main of pacticipant Foo as it was not found"
+ end
+ end
+
+ context "when error_when_not_found is false" do
+ let(:error_when_not_found) { false }
+
+ it "return a success" do
+ expect(subject.success).to be true
+ expect(subject.message).to include "Branch main of pacticipant Foo not found"
+ end
+ end
+ end
+
+ context "when deleting branches is not supported" do
+ let(:index_response_body) do
+ {
+ _links: {}
+ }.to_json
+ end
+
+ it "returns an error" do
+ expect(subject.success).to be false
+ expect(subject.message).to include "not support"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/pacts/pact_broker_client-pact_broker.json b/spec/pacts/pact_broker_client-pact_broker.json
index f569dca..768bc4f 100644
--- a/spec/pacts/pact_broker_client-pact_broker.json
+++ b/spec/pacts/pact_broker_client-pact_broker.json
@@ -6,6 +6,49 @@
"name": "Pact Broker"
},
"interactions": [
+ {
+ "description": "a request for the index resource",
+ "providerState": "the pb:pacticipant-branch relation exists in the index resource",
+ "request": {
+ "method": "GET",
+ "path": "/",
+ "headers": {
+ "Accept": "application/hal+json"
+ }
+ },
+ "response": {
+ "status": 200,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "pb:pacticipant-branch": {
+ "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-PACTICIPANT-BRANCH-{pacticipant}-{branch}"
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body._links.pb:pacticipant-branch.href": {
+ "match": "regex",
+ "regex": "http:\\/\\/.*{pacticipant}.*{branch}"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to delete a pacticipant branch",
+ "providerState": "a branch named main exists for pacticipant Foo",
+ "request": {
+ "method": "DELETE",
+ "path": "/HAL-REL-PLACEHOLDER-PB-PACTICIPANT-BRANCH-Foo-main"
+ },
+ "response": {
+ "status": 204,
+ "headers": {
+ }
+ }
+ },
{
"description": "a request to list the latest pacts",
"providerState": "a pact between Condor and the Pricing Service exists",
diff --git a/spec/service_providers/delete_branch_spec.rb b/spec/service_providers/delete_branch_spec.rb
new file mode 100644
index 0000000..d085d00
--- /dev/null
+++ b/spec/service_providers/delete_branch_spec.rb
@@ -0,0 +1,68 @@
+require "service_providers/pact_helper"
+require "pact_broker/client/branches/delete_branch"
+
+RSpec.describe "delete a branch", pact: true do
+ include_context "pact broker"
+ include PactBrokerPactHelperMethods
+
+ let(:params) do
+ {
+ pacticipant: "Foo",
+ branch: "main",
+ error_when_not_found: true
+ }
+ end
+
+ let(:options) do
+ {
+ verbose: verbose
+ }
+ end
+
+ let(:pact_broker_base_url) { pact_broker.mock_service_base_url }
+ let(:pact_broker_client_options) { { pact_broker_base_url: pact_broker_base_url } }
+ let(:response_headers) { { "Content-Type" => "application/hal+json"} }
+ let(:verbose) { false }
+
+ subject { PactBroker::Client::Branches::DeleteBranch.call(params, options, pact_broker_client_options) }
+
+ def mock_index
+ pact_broker
+ .given("the pb:pacticipant-branch relation exists in the index resource")
+ .upon_receiving("a request for the index resource")
+ .with(
+ method: "GET",
+ path: '/',
+ headers: get_request_headers).
+ will_respond_with(
+ status: 200,
+ headers: pact_broker_response_headers,
+ body: {
+ _links: {
+ :'pb:pacticipant-branch' => {
+ href: placeholder_url_term("pb:pacticipant-branch", ["pacticipant", "branch"], pact_broker)
+ }
+ }
+ }
+ )
+ end
+
+ def mock_branch_delete_request
+ pact_broker
+ .given("a branch named main exists for pacticipant Foo")
+ .upon_receiving("a request to delete a pacticipant branch")
+ .with(
+ method: "DELETE",
+ path: placeholder_path("pb:pacticipant-branch", ["Foo", "main"])
+ )
+ .will_respond_with(
+ status: 204
+ )
+ end
+
+ it "returns a success result" do
+ mock_index
+ mock_branch_delete_request
+ expect(subject.success).to be true
+ end
+end
diff --git a/spec/service_providers/pact_helper.rb b/spec/service_providers/pact_helper.rb
index eb3d710..587e136 100644
--- a/spec/service_providers/pact_helper.rb
+++ b/spec/service_providers/pact_helper.rb
@@ -25,6 +25,7 @@
module PactBrokerPactHelperMethods
+ # Use this for the path in the Pact request expectation.
# @param [String] relation eg "pb:pacticipant"
# @param [Array] params eg ["Foo"]
def placeholder_path(relation, params = [])