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
+