diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
index de702c11..d3f609b4 100644
--- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
+++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
@@ -20,6 +20,8 @@
* [A request for the compatibility matrix where only the version of Foo is specified](#a_request_for_the_compatibility_matrix_where_only_the_version_of_Foo_is_specified_given_the_pact_for_Foo_version_1.2.3_has_been_verified_by_Bar_version_4.5.6_and_version_5.6.7) given the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6 and version 5.6.7
+* [A request for the index resource](#a_request_for_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
@@ -32,10 +34,18 @@
* [A request retrieve a pact for a specific version](#a_request_retrieve_a_pact_for_a_specific_version_given_the_'Pricing_Service'_and_'Condor'_already_exist_in_the_pact-broker,_and_Condor_already_has_a_pact_published_for_version_1.3.0) given the 'Pricing Service' and 'Condor' already exist in the pact-broker, and Condor already has a pact published for version 1.3.0
+* [A request to create a global webhook with a JSON body](#a_request_to_create_a_global_webhook_with_a_JSON_body)
+
* [A request to create a webhook for a consumer and provider](#a_request_to_create_a_webhook_for_a_consumer_and_provider_given_'Condor'_does_not_exist_in_the_pact-broker) given 'Condor' does not exist in the pact-broker
+* [A request to create a webhook with a JSON body for a consumer](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_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 a webhook with a JSON body for a consumer and provider](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_and_provider_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 a webhook with a JSON body for a consumer that does not exist](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_consumer_that_does_not_exist)
+
+* [A request to create a webhook with a JSON body for a provider](#a_request_to_create_a_webhook_with_a_JSON_body_for_a_provider_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 a webhook with a non-JSON body for a consumer and provider](#a_request_to_create_a_webhook_with_a_non-JSON_body_for_a_consumer_and_provider_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 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
@@ -461,6 +471,33 @@ Pact Broker will respond with:
}
}
```
+
+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:webhooks": {
+ "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-WEBHOOKS"
+ }
+ }
+ }
+}
+```
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
@@ -648,6 +685,55 @@ Pact Broker will respond with:
}
}
```
+
+Upon receiving **a request to create a global webhook with a JSON body** from Pact Broker Client, with
+```json
+{
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ }
+ }
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 201,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "self": {
+ "href": "http://localhost:1234/some-url",
+ "title": "A title"
+ }
+ }
+ }
+}
+```
Given **'Condor' does not exist in the pact-broker**, upon receiving **a request to create a webhook for a consumer and provider** from Pact Broker Client, with
```json
@@ -689,6 +775,58 @@ Pact Broker will respond with:
}
}
```
+
+Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body for a consumer** from Pact Broker Client, with
+```json
+{
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ },
+ "consumer": {
+ "name": "Condor"
+ }
+ }
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 201,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "self": {
+ "href": "http://localhost:1234/some-url",
+ "title": "A title"
+ }
+ }
+ }
+}
+```
Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body for a consumer and provider** from Pact Broker Client, with
```json
@@ -738,6 +876,109 @@ Pact Broker will respond with:
}
}
```
+
+Upon receiving **a request to create a webhook with a JSON body for a consumer that does not exist** from Pact Broker Client, with
+```json
+{
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ },
+ "consumer": {
+ "name": "Condor"
+ }
+ }
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 400,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "errors": {
+ "consumer.name": [
+ "Some error"
+ ]
+ }
+ }
+}
+```
+
+Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a JSON body for a provider** from Pact Broker Client, with
+```json
+{
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ },
+ "provider": {
+ "name": "Pricing Service"
+ }
+ }
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 201,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "self": {
+ "href": "http://localhost:1234/some-url",
+ "title": "A title"
+ }
+ }
+ }
+}
+```
Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **a request to create a webhook with a non-JSON body for a consumer and provider** 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 b96b62b7..70456df6 100644
--- a/lib/pact_broker/client/cli/broker.rb
+++ b/lib/pact_broker/client/cli/broker.rb
@@ -14,6 +14,7 @@ module Client
module CLI
# Thor::Error will have its backtrace hidden
class PactPublicationError < ::Thor::Error; end
+ class WebhookCreationError < ::Thor::Error; end
class Broker < CustomThor
desc 'can-i-deploy', ''
@@ -103,8 +104,8 @@ def describe_version
method_option :header, aliases: "-H", type: :array, desc: "Header"
method_option :data, aliases: "-d", desc: "Data"
method_option :user, aliases: "-u", desc: "Basic auth username and password eg. username:password"
- method_option :consumer, desc: "Consumer name", required: true
- method_option :provider, desc: "Provider name", required: true
+ method_option :consumer, desc: "Consumer name"
+ method_option :provider, desc: "Provider name"
method_option :broker_base_url, required: true, aliases: "-b", desc: "The base URL of the Pact Broker"
method_option :broker_username, aliases: "-u", desc: "Pact Broker basic auth username"
method_option :broker_password, aliases: "-p", desc: "Pact Broker basic auth password"
@@ -151,9 +152,14 @@ def create_webhook webhook_url
provider: options.provider,
events: events
}
- result = PactBroker::Client::Webhooks::Create.call(params, options.broker_base_url, pact_broker_client_options)
- $stdout.puts result.message
- exit(1) unless result.success
+
+ begin
+ result = PactBroker::Client::Webhooks::Create.call(params, options.broker_base_url, pact_broker_client_options)
+ $stdout.puts result.message
+ exit(1) unless result.success
+ rescue PactBroker::Client::Error => e
+ raise WebhookCreationError, "#{e.class} - #{e.message}"
+ end
end
desc 'version', "Show the pact_broker-client gem version"
diff --git a/lib/pact_broker/client/hal.rb b/lib/pact_broker/client/hal.rb
index 0415b39f..0c3e20fb 100644
--- a/lib/pact_broker/client/hal.rb
+++ b/lib/pact_broker/client/hal.rb
@@ -1,3 +1,4 @@
require 'pact_broker/client/hal/link'
+require 'pact_broker/client/hal/entry_point'
require 'pact_broker/client/hal/http_client'
require 'pact_broker/client/hal/entity'
diff --git a/lib/pact_broker/client/hal/entry_point.rb b/lib/pact_broker/client/hal/entry_point.rb
new file mode 100644
index 00000000..77de0899
--- /dev/null
+++ b/lib/pact_broker/client/hal/entry_point.rb
@@ -0,0 +1,13 @@
+require 'pact_broker/client/hal/link'
+
+module PactBroker
+ module Client
+ module Hal
+ class EntryPoint < Link
+ def initialize(url, http_client)
+ super({ "href" => url }, http_client)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/pact_broker/client/hal/http_client.rb b/lib/pact_broker/client/hal/http_client.rb
index 732ddf2d..f671778a 100644
--- a/lib/pact_broker/client/hal/http_client.rb
+++ b/lib/pact_broker/client/hal/http_client.rb
@@ -30,7 +30,7 @@ def post href, body = nil, headers = {}
end
def create_request uri, http_method, body = nil, headers = {}
- request = Net::HTTP.const_get(http_method).new(uri.to_s)
+ request = Net::HTTP.const_get(http_method).new(uri.request_uri)
request['Content-Type'] = "application/json" if ['Post', 'Put', 'Patch'].include?(http_method)
request['Accept'] = "application/hal+json"
headers.each do | key, value |
@@ -43,10 +43,10 @@ def create_request uri, http_method, body = nil, headers = {}
end
def perform_request request, uri
- options = {:use_ssl => uri.scheme == 'https'}
response = Retry.until_true do
- http = Net::HTTP.new(uri.host, uri.port, :ENV, options)
+ http = Net::HTTP.new(uri.host, uri.port, :ENV)
http.set_debug_output($stderr) if verbose
+ http.use_ssl = (uri.scheme == 'https')
http.start do |http|
http.request request
end
@@ -64,6 +64,14 @@ def body
end
end
+ def raw_body
+ __getobj__().body
+ end
+
+ def status
+ code.to_i
+ end
+
def success?
__getobj__().code.start_with?("2")
end
diff --git a/lib/pact_broker/client/webhooks/create.rb b/lib/pact_broker/client/webhooks/create.rb
index 0f093453..c9966467 100644
--- a/lib/pact_broker/client/webhooks/create.rb
+++ b/lib/pact_broker/client/webhooks/create.rb
@@ -8,6 +8,9 @@ module PactBroker
module Client
module Webhooks
class Create
+
+ WEBHOOKS_WITH_OPTIONAL_PACTICICPANTS_NOT_SUPPORTED = "Your version of the Pact Broker requires that both consumer and provider are specified for a webhook. Please upgrade your broker to >= 2.22.0 to create a webhook with optional consumer and provider."
+
attr_reader :params, :pact_broker_base_url, :basic_auth_options, :verbose
def self.call(params, pact_broker_base_url, pact_broker_client_options)
@@ -19,34 +22,92 @@ def initialize(params, pact_broker_base_url, pact_broker_client_options)
@pact_broker_base_url = pact_broker_base_url
@basic_auth_options = pact_broker_client_options[:basic_auth] || {}
@verbose = pact_broker_client_options[:verbose]
+ @http_client = PactBroker::Client::Hal::HttpClient.new(basic_auth_options.merge(verbose: verbose))
end
def call
- request_body = JSON.parse(params.body) rescue params.body
+ if params.consumer && params.provider
+ create_webhook_with_consumer_and_provider
+ else
+ create_webhook_with_optional_consumer_and_provider
+ end
+ end
+
+ private
+
+ attr_reader :http_client
+
+ def create_webhook_with_consumer_and_provider
+ webhook_entity = webhook_link.expand(consumer: params.consumer, provider: params.provider).post(request_body)
+ handle_response(webhook_entity)
+ end
+
+ def create_webhook_with_optional_consumer_and_provider
+ webhook_entity = index_link.get._link("pb:webhooks").post(request_body_with_optional_consumer_and_provider)
+
+ if webhook_entity.response.status == 405
+ raise PactBroker::Client::Error.new(WEBHOOKS_WITH_OPTIONAL_PACTICICPANTS_NOT_SUPPORTED)
+ end
- body = {
+ handle_response(webhook_entity)
+ end
+
+ def request_body
+ webhook_request_body = JSON.parse(params.body) rescue params.body
+ {
events: params.events.collect{ | event | { "name" => event }},
request: {
url: params.url,
method: params.http_method,
headers: params.headers,
- body: request_body,
+ body: webhook_request_body,
username: params.username,
password: params.password
}
}
+ end
+
+ def request_body_with_optional_consumer_and_provider
+ body = request_body
- #TODO look for relation
- create_webhook_url = "#{pact_broker_base_url.chomp("/")}/webhooks/provider/{provider}/consumer/{consumer}"
- http_client = PactBroker::Client::Hal::HttpClient.new(basic_auth_options.merge(verbose: verbose))
- link = PactBroker::Client::Hal::Link.new({"href" => create_webhook_url}, http_client)
- entity = link.expand(consumer: params.consumer, provider: params.provider).post(body)
- if entity.success?
- CommandResult.new(true, "Webhook #{entity._link('self').title_or_name.inspect} created")
+ if params.consumer
+ body[:consumer] = { name: params.consumer }
+ end
+
+ if params.provider
+ body[:provider] = { name: params.provider }
+ end
+
+ body
+ end
+
+ def webhook_for_consumer_and_provider_url
+ "#{pact_broker_base_url.chomp("/")}/webhooks/provider/{provider}/consumer/{consumer}"
+ end
+
+ def handle_response(webhook_entity)
+ if webhook_entity.success?
+ success_result(webhook_entity)
else
- CommandResult.new(false, "Error creating webhook. response status=#{entity.response.code} body=#{entity.response.body}")
+ error_result(webhook_entity)
end
end
+
+ def success_result(webhook_entity)
+ CommandResult.new(true, "Webhook #{webhook_entity._link('self').title_or_name.inspect} created")
+ end
+
+ def error_result(webhook_entity)
+ CommandResult.new(false, "Error creating webhook. response status=#{webhook_entity.response.status} body=#{webhook_entity.response.raw_body}")
+ end
+
+ def index_link
+ PactBroker::Client::Hal::EntryPoint.new(pact_broker_base_url, http_client)
+ end
+
+ def webhook_link
+ PactBroker::Client::Hal::EntryPoint.new(webhook_for_consumer_and_provider_url, http_client)
+ end
end
end
end
diff --git a/spec/lib/pact_broker/client/cli/broker_create_webhook_spec.rb b/spec/lib/pact_broker/client/cli/broker_create_webhook_spec.rb
index e3158722..fb57d781 100644
--- a/spec/lib/pact_broker/client/cli/broker_create_webhook_spec.rb
+++ b/spec/lib/pact_broker/client/cli/broker_create_webhook_spec.rb
@@ -172,6 +172,16 @@ module CLI
expect(exited_with_error).to be true
end
end
+
+ context "when a PactBroker::Client::Error is raised" do
+ before do
+ allow(PactBroker::Client::Webhooks::Create).to receive(:call).and_raise(PactBroker::Client::Error, "foo")
+ end
+
+ it "raises a WebhookCreationError which does not show an ugly stack trace" do
+ expect { subject }.to raise_error(WebhookCreationError, /foo/)
+ end
+ end
end
end
end
diff --git a/spec/lib/pact_broker/client/webhooks/create_spec.rb b/spec/lib/pact_broker/client/webhooks/create_spec.rb
new file mode 100644
index 00000000..df9355ab
--- /dev/null
+++ b/spec/lib/pact_broker/client/webhooks/create_spec.rb
@@ -0,0 +1,61 @@
+require 'pact_broker/client/webhooks/create'
+
+module PactBroker
+ module Client
+ module Webhooks
+ describe Create do
+ describe ".call" do
+ let(:index_body) do
+ {
+ "_links" => {
+ "pb:webhooks" => {
+ "href" => "http://broker/webhooks"
+ }
+ }
+ }.to_json
+ end
+ let!(:index_request) do
+ stub_request(:get, "http://broker").to_return(status: 200, body: index_body, headers: { "Content-Type" => "application/hal+json" } )
+ end
+
+ let!(:webhook_request) do
+ stub_request(:post, "http://broker/webhooks").to_return(status: 405)
+ end
+
+ let(:params) do
+ {
+ http_method: "POST",
+ url: "https://webhook",
+ headers: { "Foo" => "bar", "Bar" => "foo"},
+ username: "username",
+ password: "password",
+ body: "body",
+ events: ["contract_content_changed"]
+ }.tap { |it| Pact::Fixture.add_fixture(:create_webhook_params, it) }
+ end
+
+ subject { Create.call(params, "http://broker", {}) }
+
+ context "when a 405 is returned from the webhook creation request" do
+
+ it "raises an error with a message to upgrade the Pact Broker" do
+ expect { subject }.to raise_error PactBroker::Client::Error, /Your version of the Pact Broker/
+ end
+ end
+
+ context "when a 400 is returned from the webhook creation request" do
+ let!(:webhook_request) do
+ stub_request(:post, "http://broker/webhooks").to_return(status: 400, body: '{"some":"error"}', headers: { "Content-Type" => "application/hal+json" })
+ end
+
+ it "returns a result with success=false" do
+ expect(subject.success).to be false
+ expect(subject.message).to match /400/
+ expect(subject.message).to match /"some":"error"/
+ end
+ 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 9e66ed44..644a4bf6 100644
--- a/spec/pacts/pact_broker_client-pact_broker.json
+++ b/spec/pacts/pact_broker_client-pact_broker.json
@@ -1444,6 +1444,260 @@
"Content-Type": "application/hal+json;charset=utf-8"
}
}
+ },
+ {
+ "description": "a request for 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:webhooks": {
+ "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-PB-WEBHOOKS"
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body._links.pb:webhooks.href": {
+ "match": "regex",
+ "regex": "http:\\/\\/.*"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to create a webhook with a JSON body for a consumer",
+ "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker",
+ "request": {
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ },
+ "consumer": {
+ "name": "Condor"
+ }
+ }
+ },
+ "response": {
+ "status": 201,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "self": {
+ "href": "http://localhost:1234/some-url",
+ "title": "A title"
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body._links.self.href": {
+ "match": "regex",
+ "regex": "http:\\/\\/.*"
+ },
+ "$.body._links.self.title": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to create a webhook with a JSON body for a consumer that does not exist",
+ "request": {
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ },
+ "consumer": {
+ "name": "Condor"
+ }
+ }
+ },
+ "response": {
+ "status": 400,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "errors": {
+ "consumer.name": [
+ "Some error"
+ ]
+ }
+ },
+ "matchingRules": {
+ "$.body.errors['consumer.name']": {
+ "min": 1
+ },
+ "$.body.errors['consumer.name'][*].*": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to create a webhook with a JSON body for a provider",
+ "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker",
+ "request": {
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ },
+ "provider": {
+ "name": "Pricing Service"
+ }
+ }
+ },
+ "response": {
+ "status": 201,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "self": {
+ "href": "http://localhost:1234/some-url",
+ "title": "A title"
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body._links.self.href": {
+ "match": "regex",
+ "regex": "http:\\/\\/.*"
+ },
+ "$.body._links.self.title": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to create a global webhook with a JSON body",
+ "request": {
+ "method": "post",
+ "path": "/HAL-REL-PLACEHOLDER-PB-WEBHOOKS",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": "https://webhook",
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ }
+ }
+ },
+ "response": {
+ "status": 201,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "_links": {
+ "self": {
+ "href": "http://localhost:1234/some-url",
+ "title": "A title"
+ }
+ }
+ },
+ "matchingRules": {
+ "$.body._links.self.href": {
+ "match": "regex",
+ "regex": "http:\\/\\/.*"
+ },
+ "$.body._links.self.title": {
+ "match": "type"
+ }
+ }
+ }
}
],
"metadata": {
diff --git a/spec/service_providers/pact_helper.rb b/spec/service_providers/pact_helper.rb
index 3b0e813b..0abed329 100644
--- a/spec/service_providers/pact_helper.rb
+++ b/spec/service_providers/pact_helper.rb
@@ -18,3 +18,49 @@
end
+module PactBrokerPactHelperMethods
+
+ def placeholder_path(relation, params = [])
+ path = "/HAL-REL-PLACEHOLDER-#{relation.gsub(':', '-').upcase}"
+ if params.any?
+ joined_params = params.collect{ |param| "{#{param}}"}.join("-")
+ path = "#{path}-#{joined_params}"
+ end
+
+ path
+ end
+
+ def placeholder_url(relation, params = [])
+ "#{pact_broker.mock_service_base_url}#{placeholder_path(relation, params)}"
+ end
+
+ def placeholder_url_term(relation, params = [])
+ regexp = "http:\/\/.*"
+ if params.any?
+ joined_params_for_regexp = params.collect{ |param| "{#{param}}"}.join(".*")
+ regexp = "#{regexp}#{joined_params_for_regexp}"
+ end
+
+ Pact.term(placeholder_url(relation, params), /#{regexp}/)
+ end
+
+ def mock_pact_broker_index(context)
+ pact_broker
+ .upon_receiving("a request for the index resource")
+ .with(
+ method: :get,
+ path: '/',
+ headers: context.get_request_headers).
+ will_respond_with(
+ status: 200,
+ headers: context.pact_broker_response_headers,
+ body: {
+ _links: {
+ :'pb:webhooks' => {
+ href: placeholder_url_term('pb:webhooks')
+ }
+ }
+ }
+ )
+ end
+end
diff --git a/spec/service_providers/webhooks_create_spec.rb b/spec/service_providers/webhooks_create_spec.rb
index 3309c669..8f45cd7a 100644
--- a/spec/service_providers/webhooks_create_spec.rb
+++ b/spec/service_providers/webhooks_create_spec.rb
@@ -4,6 +4,7 @@
RSpec.describe "creating a webhook", pact: true do
include_context "pact broker"
+ include PactBrokerPactHelperMethods
let(:params) do
{
@@ -44,6 +45,21 @@
}
end
+ let(:success_response) do
+ {
+ status: 201,
+ headers: pact_broker_response_headers,
+ body: {
+ _links: {
+ self: {
+ href: Pact.term('http://localhost:1234/some-url', %r{http://.*}),
+ title: Pact.like("A title")
+ }
+ }
+ }
+ }
+ end
+
let(:pact_broker_client_options) { {} }
subject { PactBroker::Client::Webhooks::Create.call(params, broker_base_url, pact_broker_client_options) }
@@ -57,24 +73,14 @@
method: :post,
path: '/webhooks/provider/Pricing%20Service/consumer/Condor',
headers: post_request_headers,
- body: request_body).
- will_respond_with(
- status: 201,
- headers: pact_broker_response_headers,
- body: {
- _links: {
- self: {
- href: Pact.term('http://localhost:1234/some-url', %r{http://.*}),
- title: Pact.like("A title")
- }
- }
- }
- )
+ body: request_body)
+ .will_respond_with(success_response)
end
it "returns a CommandResult with success = true" do
expect(subject).to be_a PactBroker::Client::CommandResult
expect(subject.success).to be true
+ expect(subject.message).to eq "Webhook \"A title\" created"
end
end
@@ -89,19 +95,8 @@
method: :post,
path: '/webhooks/provider/Pricing%20Service/consumer/Condor',
headers: post_request_headers,
- body: request_body).
- will_respond_with(
- status: 201,
- headers: pact_broker_response_headers,
- body: {
- _links: {
- self: {
- href: Pact.term('http://localhost:1234/some-url', %r{http://.*}),
- title: Pact.like("A title")
- }
- }
- }
- )
+ body: request_body)
+ .will_respond_with(success_response)
end
let(:body) { "" }
@@ -163,4 +158,98 @@
expect(subject.message).to match /404/
end
end
+
+ context "when only a consumer is specified" do
+ before do
+ params.delete(:provider)
+ request_body["consumer"] = { "name" => "Condor" }
+ mock_pact_broker_index(self)
+
+ pact_broker
+ .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker")
+ .upon_receiving("a request to create a webhook with a JSON body for a consumer")
+ .with(
+ method: :post,
+ path: placeholder_path('pb:webhooks'),
+ headers: post_request_headers,
+ body: request_body)
+ .will_respond_with(success_response)
+ end
+
+ it "returns a CommandResult with success = true" do
+ expect(subject.success).to be true
+ expect(subject.message).to eq "Webhook \"A title\" created"
+ end
+ end
+
+ context "when only a consumer is specified and it does not exist" do
+ before do
+ params.delete(:provider)
+ request_body["consumer"] = { "name" => "Condor" }
+ mock_pact_broker_index(self)
+
+ pact_broker
+ .upon_receiving("a request to create a webhook with a JSON body for a consumer that does not exist")
+ .with(
+ method: :post,
+ path: placeholder_path('pb:webhooks'),
+ headers: post_request_headers,
+ body: request_body)
+ .will_respond_with(
+ status: 400,
+ headers: pact_broker_response_headers,
+ body: {
+ errors: {
+ "consumer.name" => Pact.each_like("Some error")
+ }
+ })
+ end
+
+ it "returns a CommandResult with success = true" do
+ expect(subject.success).to be false
+ end
+ end
+
+ context "when only a provider is specified" do
+ before do
+ params.delete(:consumer)
+ request_body["provider"] = { "name" => "Pricing Service" }
+ mock_pact_broker_index(self)
+
+ pact_broker
+ .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker")
+ .upon_receiving("a request to create a webhook with a JSON body for a provider")
+ .with(
+ method: :post,
+ path: placeholder_path('pb:webhooks'),
+ headers: post_request_headers,
+ body: request_body)
+ .will_respond_with(success_response)
+ end
+
+ it "returns a CommandResult with success = true" do
+ expect(subject.success).to be true
+ end
+ end
+
+ context "when neither consumer nor provider are specified" do
+ before do
+ params.delete(:consumer)
+ params.delete(:provider)
+ mock_pact_broker_index(self)
+
+ pact_broker
+ .upon_receiving("a request to create a global webhook with a JSON body")
+ .with(
+ method: :post,
+ path: placeholder_path('pb:webhooks'),
+ headers: post_request_headers,
+ body: request_body)
+ .will_respond_with(success_response)
+ end
+
+ it "returns a CommandResult with success = true" do
+ expect(subject.success).to be true
+ end
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 47779572..cee33bda 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -10,6 +10,8 @@
config.before(:each) do
ENV.delete('PACT_BROKER_BASE_URL')
+ ENV.delete('PACT_BROKER_USERNAME')
+ ENV.delete('PACT_BROKER_PASSWORD')
end
config.after(:all) do
@@ -17,6 +19,7 @@
end
config.filter_run_excluding :skip_windows => is_windows
+ config.example_status_persistence_file_path = "./spec/examples.txt"
end
module Pact