diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md index ab68cbde..e497b3c3 100644 --- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md @@ -24,6 +24,8 @@ * [A request for the index resource](#a_request_for_the_index_resource_given_the_pacticipant_relations_are_present) given the pacticipant relations are present +* [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:environments_relation_exists_in_the_index_resource) given the pb:environments relation exists in the index resource + * [A request for the index resource](#a_request_for_the_index_resource_given_the_pb:latest-tagged-version_relation_exists_in_the_index_resource) given the pb:latest-tagged-version relation exists in the index resource * [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 @@ -66,6 +68,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 create an environment](#a_request_to_create_an_environment) + * [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 * [A request to get the Pricing Service](#a_request_to_get_the_Pricing_Service_given_the_'Pricing_Service'_does_not_exist_in_the_pact-broker) given the 'Pricing Service' does not exist in the pact-broker @@ -602,6 +606,33 @@ Pact Broker will respond with: } } ``` + +Given **the pb:environments 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:environments": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-ENVIRONMENTS" + } + } + } +} +``` Given **the pb:latest-tagged-version relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, with ```json @@ -1454,6 +1485,53 @@ Pact Broker will respond with: } } ``` + +Upon receiving **a request to create an environment** from Pact Broker Client, with +```json +{ + "method": "POST", + "path": "/HAL-REL-PLACEHOLDER-PB-ENVIRONMENTS", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "name": "test", + "displayName": "Test", + "production": false, + "contacts": [ + { + "name": "Foo team", + "details": { + "emailAddress": "foo@bar.com" + } + } + ] + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "name": "test", + "displayName": "Test", + "production": false, + "contacts": [ + { + "name": "Foo team", + "details": { + "emailAddress": "foo@bar.com" + } + } + ] + } +} +``` Given **the 'Pricing Service' already exists in the pact-broker**, upon receiving **a request to get the Pricing Service** from Pact Broker Client, with ```json diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index 457ec483..04da4eef 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -180,8 +180,25 @@ def list_latest_pact_versions(*required_but_ignored) exit(1) unless result.success end - if ENV.fetch("PACT_BROKER_FEATURES", "").include?("deployments") + ignored_and_hidden_potential_options_from_environment_variables + desc "create-environment", "Create an environment resource in the Pact Broker to represent a real world deployment or release environment." + method_option :name, required: true, desc: "The uniquely identifying name of the environment as used in deployment code" + method_option :display_name, desc: "The display name of the environment" + method_option :production, type: :boolean, default: false, desc: "Whether or not this environment is a production environment. Default: false" + method_option :contact_name, required: false, desc: "The name of the team/person responsible for this environment" + method_option :contact_email_address, required: false, desc: "The email address of the team/person responsible for this environment" + shared_authentication_options + + def create_environment + require 'pact_broker/client/environments/create_environment' + param_names = [:name, :display_name, :production, :contact_name, :contact_email_address] + params = param_names.each_with_object({}) { | key, p | p[key] = options[key] } + result = PactBroker::Client::Environments::CreateEnvironment.call(params, options.broker_base_url, pact_broker_client_options) + $stdout.puts result.message + exit(1) unless result.success + end + if ENV.fetch("PACT_BROKER_FEATURES", "").include?("deployments") ignored_and_hidden_potential_options_from_environment_variables desc "record-deployment", "Record deployment of a pacticipant version to an environment. See https://docs.pact.io/go/record_deployment for more information." method_option :pacticipant, required: true, aliases: "-a", desc: "The name of the pacticipant that was deployed." diff --git a/lib/pact_broker/client/environments/create_environment.rb b/lib/pact_broker/client/environments/create_environment.rb new file mode 100644 index 00000000..e2521ec8 --- /dev/null +++ b/lib/pact_broker/client/environments/create_environment.rb @@ -0,0 +1,78 @@ +require 'pact_broker/client/hal_client_methods' +require 'pact_broker/client/error' +require 'pact_broker/client/command_result' +require 'term/ansicolor' + +module PactBroker + module Client + module Environments + class CreateEnvironment + include PactBroker::Client::HalClientMethods + + NOT_SUPPORTED_MESSAGE = "This version of the Pact Broker does not support creation of environments. Please upgrade to version 2.80.0 or later." + + def self.call(params, pact_broker_base_url, pact_broker_client_options) + new(params, pact_broker_base_url, pact_broker_client_options).call + end + + def initialize(params, pact_broker_base_url, pact_broker_client_options) + @params = params + @pact_broker_base_url = pact_broker_base_url + @pact_broker_client_options = pact_broker_client_options + end + + def call + check_if_command_supported + create_environment + rescue PactBroker::Client::Error => e + PactBroker::Client::CommandResult.new(false, ::Term::ANSIColor.red(e.message)) + end + + private + + attr_reader :params + attr_reader :pact_broker_base_url, :pact_broker_client_options + + def create_environment + index_resource + ._link!("pb:environments") + .post!(request_body) + PactBroker::Client::CommandResult.new(true, result_message) + end + + def request_body + { + name: params[:name], + displayName: params[:display_name], + production: params[:production], + contacts: contacts + }.compact + end + + def contacts + if params[:contact_name] || params[:contact_email_address] + contact = {} + contact[:name] = params[:contact_name] || "unknown" + if params[:contact_email_address] + contact[:details] = { emailAddress: params[:contact_email_address] } + end + [contact] + else + nil + end + end + + def result_message + prod_label = params[:production] ? "production" : "non-production" + ::Term::ANSIColor.green("Created #{prod_label} environment #{params[:name]} in #{pact_broker_name}") + end + + def check_if_command_supported + unless index_resource.can?("pb:environments") + raise PactBroker::Client::Error.new(NOT_SUPPORTED_MESSAGE) + end + end + end + end + end +end \ No newline at end of file diff --git a/spec/pacts/pact_broker_client-pact_broker.json b/spec/pacts/pact_broker_client-pact_broker.json index 881fa327..02527fba 100644 --- a/spec/pacts/pact_broker_client-pact_broker.json +++ b/spec/pacts/pact_broker_client-pact_broker.json @@ -6,6 +6,79 @@ "name": "Pact Broker" }, "interactions": [ + { + "description": "a request for the index resource", + "providerState": "the pb:environments 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:environments": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-ENVIRONMENTS" + } + } + }, + "matchingRules": { + "$.body._links.pb:environments.href": { + "match": "regex", + "regex": "http:\\/\\/.*" + } + } + } + }, + { + "description": "a request to create an environment", + "request": { + "method": "POST", + "path": "/HAL-REL-PLACEHOLDER-PB-ENVIRONMENTS", + "headers": { + "Content-Type": "application/json", + "Accept": "application/hal+json" + }, + "body": { + "name": "test", + "displayName": "Test", + "production": false, + "contacts": [ + { + "name": "Foo team", + "details": { + "emailAddress": "foo@bar.com" + } + } + ] + } + }, + "response": { + "status": 201, + "headers": { + "Content-Type": "application/hal+json;charset=utf-8" + }, + "body": { + "name": "test", + "displayName": "Test", + "production": false, + "contacts": [ + { + "name": "Foo team", + "details": { + "emailAddress": "foo@bar.com" + } + } + ] + } + } + }, { "description": "a request to list the latest pacts", "providerState": "a pact between Condor and the Pricing Service exists", diff --git a/spec/service_providers/create_environment_spec.rb b/spec/service_providers/create_environment_spec.rb new file mode 100644 index 00000000..6f264fa8 --- /dev/null +++ b/spec/service_providers/create_environment_spec.rb @@ -0,0 +1,78 @@ +require 'service_providers/pact_helper' +require 'pact_broker/client/environments/create_environment' + +RSpec.describe "create an environment", pact: true do + include_context "pact broker" + include PactBrokerPactHelperMethods + + let(:params) do + { + name: "test", + display_name: "Test", + production: false, + contact_name: "Foo team", + contact_email_address: "foo@bar.com" + + } + end + let(:pact_broker_client_options) { {} } + let(:request_body) do + { + name: "test", + displayName: "Test", + production: false, + contacts: [{ + name: "Foo team", + details: { + emailAddress: "foo@bar.com" + } + }] + } + end + + subject { PactBroker::Client::Environments::CreateEnvironment.call(params, broker_base_url, pact_broker_client_options) } + + def mock_index + pact_broker + .given("the pb:environments 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:environments' => { + href: placeholder_url_term("pb:environments") + } + } + } + ) + end + + def mock_environment_creation_request + pact_broker + .upon_receiving("a request to create an environment") + .with( + method: "POST", + path: "/HAL-REL-PLACEHOLDER-PB-ENVIRONMENTS", + headers: post_request_headers, + body: request_body + ) + .will_respond_with( + status: 201, + headers: pact_broker_response_headers, + body: request_body + ) + end + + it "returns a success result" do + mock_index + mock_environment_creation_request + expect(subject.success).to be true + end +end +