diff --git a/README.md b/README.md
index b6ec8072..2adcb446 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,32 @@ Description:
```
+### create-webhook
+
+```
+Usage:
+ pact-broker create-webhook URL --consumer=CONSUMER --provider=PROVIDER -X, --request=REQUEST -b, --broker-base-url=BROKER_BASE_URL
+
+Options:
+ -X, --request=REQUEST # HTTP method
+ -H, [--header=one two three] # Header
+ -d, [--data=DATA] # Data
+ -u, [--user=USER] # Basic auth username and password eg. username:password
+ --consumer=CONSUMER # Consumer name
+ --provider=PROVIDER # Provider name
+ -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
+ [--contract-content-changed], [--no-contract-content-changed] # Trigger this webhook when the pact content changes
+ [--provider-verification-published], [--no-provider-verification-published] # Trigger this webhook when a provider verification result is published
+ -v, [--verbose], [--no-verbose] # Verbose output. Default: false
+
+Description:
+ Create a curl command that executes the request that you want your webhook to execute, then replace "curl" with "pact-broker
+ create-webhook" and add the consumer, provider, event types and broker details. Note that the URL must be the first parameter
+ when executing create-webhook.
+```
+
## Usage - Ruby
### Consumer
diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
index b5c47fde..6be11b73 100644
--- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
+++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md
@@ -32,6 +32,12 @@
* [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 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 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 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
* [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
@@ -70,6 +76,8 @@
* [A request to tag the production version of Condor](#a_request_to_tag_the_production_version_of_Condor_given_'Condor'_exists_in_the_pact-broker) given 'Condor' exists in the pact-broker
+* [An invalid request to create a webhook for a consumer and provider](#an_invalid_request_to_create_a_webhook_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
+
#### Interactions
@@ -640,6 +648,143 @@ Pact Broker will respond with:
}
}
```
+
+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
+{
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "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": 404,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ }
+}
+```
+
+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
+{
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "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 **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
+{
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "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": "",
+ "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 **the 'Pricing Service' already exists in the pact-broker**, upon receiving **a request to get the Pricing Service** from Pact Broker Client, with
```json
@@ -1247,3 +1392,51 @@ Pact Broker will respond with:
}
}
```
+
+Given **the 'Pricing Service' and 'Condor' already exist in the pact-broker**, upon receiving **an invalid request to create a webhook for a consumer and provider** from Pact Broker Client, with
+```json
+{
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": null,
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ }
+ }
+}
+```
+Pact Broker will respond with:
+```json
+{
+ "status": 400,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "errors": {
+ "request.url": [
+ "Some error"
+ ]
+ }
+ }
+}
+```
diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb
index c5b731f0..e7dd5e0d 100644
--- a/lib/pact_broker/client/cli/broker.rb
+++ b/lib/pact_broker/client/cli/broker.rb
@@ -99,6 +99,63 @@ def describe_version
exit(1) unless result.success
end
+ method_option :request, aliases: "-X", desc: "HTTP method", required: true
+ 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 :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"
+ method_option :contract_content_changed, type: :boolean, desc: "Trigger this webhook when the pact content changes"
+ method_option :provider_verification_published, type: :boolean, desc: "Trigger this webhook when a provider verification result is published"
+ method_option :verbose, aliases: "-v", type: :boolean, default: false, required: false, desc: "Verbose output. Default: false"
+
+ desc 'create-webhook URL', 'Creates a webhook using the same switches as a curl request.'
+ long_desc File.read(File.join(File.dirname(__FILE__), 'create_webhook_long_desc.txt'))
+ def create_webhook webhook_url
+ require 'pact_broker/client/webhooks/create'
+
+ if !(options.contract_content_changed || options.provider_verification_published)
+ raise PactBroker::Client::Error.new("You must select at least one of --contract-content-changed or --provider-verification-published")
+ end
+
+ username = options.user ? options.user.split(":", 2).first : nil
+ password = options.user ? options.user.split(":", 2).last : nil
+
+ headers = options.header.each_with_object({}) { | header, headers | headers[header.split(":", 2).first.strip] = header.split(":", 2).last.strip }
+
+ body = options.data
+ if body.start_with?("@")
+ filepath = body[1..-1]
+ begin
+ body = File.read(filepath)
+ rescue StandardError => e
+ raise PactBroker::Client::Error.new("Couldn't read data from file \"#{filepath}\" due to #{e.class} #{e.message}")
+ end
+ end
+
+ events = []
+ events << 'contract_content_changed' if options.contract_content_changed
+ events << 'provider_verification_published' if options.provider_verification_published
+
+ params = {
+ http_method: options.request,
+ url: webhook_url,
+ headers: headers,
+ username: username,
+ password: password,
+ body: body,
+ consumer: options.consumer,
+ 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
+ end
+
desc 'version', "Show the pact_broker-client gem version"
def version
$stdout.puts PactBroker::Client::VERSION
diff --git a/lib/pact_broker/client/cli/create_webhook_long_desc.txt b/lib/pact_broker/client/cli/create_webhook_long_desc.txt
new file mode 100644
index 00000000..9f8dac75
--- /dev/null
+++ b/lib/pact_broker/client/cli/create_webhook_long_desc.txt
@@ -0,0 +1 @@
+Create a curl command that executes the request that you want your webhook to execute, then replace "curl" with "pact-broker create-webhook" and add the consumer, provider, event types and broker details. Note that the URL must be the first parameter when executing create-webhook.
diff --git a/lib/pact_broker/client/hal.rb b/lib/pact_broker/client/hal.rb
new file mode 100644
index 00000000..0415b39f
--- /dev/null
+++ b/lib/pact_broker/client/hal.rb
@@ -0,0 +1,3 @@
+require 'pact_broker/client/hal/link'
+require 'pact_broker/client/hal/http_client'
+require 'pact_broker/client/hal/entity'
diff --git a/lib/pact_broker/client/webhooks/create.rb b/lib/pact_broker/client/webhooks/create.rb
new file mode 100644
index 00000000..0f093453
--- /dev/null
+++ b/lib/pact_broker/client/webhooks/create.rb
@@ -0,0 +1,53 @@
+require 'uri'
+require 'pact_broker/client/hal'
+require 'ostruct'
+require 'json'
+require 'pact_broker/client/command_result'
+
+module PactBroker
+ module Client
+ module Webhooks
+ class Create
+ attr_reader :params, :pact_broker_base_url, :basic_auth_options, :verbose
+
+ 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 = OpenStruct.new(params)
+ @pact_broker_base_url = pact_broker_base_url
+ @basic_auth_options = pact_broker_client_options[:basic_auth] || {}
+ @verbose = pact_broker_client_options[:verbose]
+ end
+
+ def call
+ request_body = JSON.parse(params.body) rescue params.body
+
+ body = {
+ events: params.events.collect{ | event | { "name" => event }},
+ request: {
+ url: params.url,
+ method: params.http_method,
+ headers: params.headers,
+ body: request_body,
+ username: params.username,
+ password: params.password
+ }
+ }
+
+ #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")
+ else
+ CommandResult.new(false, "Error creating webhook. response status=#{entity.response.code} body=#{entity.response.body}")
+ end
+ end
+ 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
new file mode 100644
index 00000000..d8b7ddca
--- /dev/null
+++ b/spec/lib/pact_broker/client/cli/broker_create_webhook_spec.rb
@@ -0,0 +1,155 @@
+require 'pact_broker/client/cli/broker'
+require 'pact_broker/client/webhooks/create'
+
+module PactBroker
+ module Client
+ module CLI
+ describe Broker do
+ describe "create_webhook" do
+ before do
+ allow($stdout).to receive(:puts)
+ allow(PactBroker::Client::Webhooks::Create).to receive(:call).and_return(command_result)
+ broker.options = OpenStruct.new(options_hash)
+ end
+
+ let(:broker) { Broker.new }
+ let(:data) { 'data' }
+ let(:user) { "username:password" }
+ let(:command_result) { double('command result', success: success, message: 'message') }
+ let(:success) { true }
+
+ let(:options_hash) do
+ {
+ request: "POST",
+ header: ["Foo: bar", "Bar: foo"],
+ data: data,
+ user: user,
+ consumer: "consumer",
+ provider: "provider",
+ broker_base_url: "http://broker",
+ broker_username: "username",
+ broker_password: "password",
+ contract_content_changed: true,
+ verbose: true
+ }
+ end
+
+ let(:expected_params) do
+ {
+ http_method: "POST",
+ url: "http://webhook",
+ headers: { "Foo" => "bar", "Bar" => "foo"},
+ username: "username",
+ password: "password",
+ body: "data",
+ consumer: "consumer",
+ provider: "provider",
+ events: ["contract_content_changed"]
+ }.tap { |it| Pact::Fixture.add_fixture(:create_webhook_params, it) }
+ end
+
+ subject { broker.create_webhook "http://webhook" }
+
+ it "calls PactBroker::Client::Webhooks::Create with the webhook params" do
+ expect(PactBroker::Client::Webhooks::Create).to receive(:call) do | params, _, _ |
+ expect(params).to eq expected_params
+ command_result
+ end
+ subject
+ end
+
+ it "calls PactBroker::Client::Webhooks::Create with pact broker details" do
+ expect(PactBroker::Client::Webhooks::Create).to receive(:call) do | _, broker_base_url, pact_broker_client_options |
+ expect(broker_base_url).to eq "http://broker"
+ expect(pact_broker_client_options).to eq(basic_auth: { username: "username", password: "password"}, verbose: true)
+ command_result
+ end
+ subject
+ end
+
+ context "when neither event type is selected" do
+ before do
+ options_hash.delete(:contract_content_changed)
+ broker.options = OpenStruct.new(options_hash)
+ end
+
+ it "raises an error" do
+ expect { subject }.to raise_error PactBroker::Client::Error, /You must select at least one/
+ end
+ end
+
+ context "with no basic auth" do
+ before do
+ options_hash.delete(:broker_username)
+ options_hash.delete(:broker_password)
+ broker.options = OpenStruct.new(options_hash)
+ end
+
+ it "calls Webhooks::Create without basic auth" do
+ expect(PactBroker::Client::Webhooks::Create).to receive(:call) do | _, _, pact_broker_client_options |
+ expect(pact_broker_client_options).to eq(verbose: true)
+ command_result
+ end
+ subject
+ end
+ end
+
+ context "when a file reference is passed for the data" do
+ before do
+ FileUtils.mkdir_p "tmp"
+ File.open("tmp/body.json", "w") { |file| file << "file" }
+ end
+
+ let(:data) { "@tmp/body.json" }
+
+ it "reads the file and passes it in to the Webhooks::Create call" do
+ expect(PactBroker::Client::Webhooks::Create).to receive(:call) do | params, _, _ |
+ expect(params[:body]).to eq "file"
+ command_result
+ end
+ subject
+ end
+
+ context "when the file is not found" do
+
+ let(:data) { "@doesnotexist.json" }
+
+ it "raises a PactBroker::Client::Error" do
+ expect { subject }.to raise_error PactBroker::Client::Error, /Couldn't read data from file/
+ end
+ end
+
+ context "when successful" do
+ it "prints the message to stdout" do
+ expect($stdout).to receive(:puts).with('message')
+ subject
+ end
+ end
+
+ context "when not successful" do
+ let(:success) { false }
+
+ it "prints the message to stderr" do
+ expect($stdout).to receive(:puts).with('message')
+ begin
+ subject
+ rescue SystemExit
+ end
+ end
+
+ it "exits with code 1" do
+ exited_with_error = false
+ begin
+ subject
+ rescue SystemExit
+ exited_with_error = true
+ end
+ expect(exited_with_error).to be true
+ end
+ 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 5e641699..f7846774 100644
--- a/spec/pacts/pact_broker_client-pact_broker.json
+++ b/spec/pacts/pact_broker_client-pact_broker.json
@@ -1243,6 +1243,205 @@
}
}
}
+ },
+ {
+ "description": "a request to create a webhook with a JSON body for a consumer and provider",
+ "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker",
+ "request": {
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "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"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to create a webhook with a non-JSON body for a consumer and provider",
+ "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker",
+ "request": {
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "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": "",
+ "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"
+ }
+ }
+ }
+ },
+ {
+ "description": "an invalid request to create a webhook for a consumer and provider",
+ "providerState": "the 'Pricing Service' and 'Condor' already exist in the pact-broker",
+ "request": {
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "headers": {
+ "Content-Type": "application/json",
+ "Accept": "application/hal+json"
+ },
+ "body": {
+ "events": [
+ {
+ "name": "contract_content_changed"
+ }
+ ],
+ "request": {
+ "url": null,
+ "method": "POST",
+ "headers": {
+ "Foo": "bar",
+ "Bar": "foo"
+ },
+ "body": {
+ "some": "body"
+ },
+ "username": "username",
+ "password": "password"
+ }
+ }
+ },
+ "response": {
+ "status": 400,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ },
+ "body": {
+ "errors": {
+ "request.url": [
+ "Some error"
+ ]
+ }
+ },
+ "matchingRules": {
+ "$.body.errors['request.url']": {
+ "min": 1
+ },
+ "$.body.errors['request.url'][*].*": {
+ "match": "type"
+ }
+ }
+ }
+ },
+ {
+ "description": "a request to create a webhook for a consumer and provider",
+ "providerState": "'Condor' does not exist in the pact-broker",
+ "request": {
+ "method": "post",
+ "path": "/webhooks/provider/Pricing%20Service/consumer/Condor",
+ "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": 404,
+ "headers": {
+ "Content-Type": "application/hal+json;charset=utf-8"
+ }
+ }
}
],
"metadata": {
diff --git a/spec/service_providers/webhooks_create_spec.rb b/spec/service_providers/webhooks_create_spec.rb
new file mode 100644
index 00000000..3309c669
--- /dev/null
+++ b/spec/service_providers/webhooks_create_spec.rb
@@ -0,0 +1,166 @@
+require_relative 'pact_helper'
+require 'pact_broker/client/webhooks/create'
+
+RSpec.describe "creating a webhook", pact: true do
+
+ include_context "pact broker"
+
+ let(:params) do
+ {
+ http_method: "POST",
+ url: "https://webhook",
+ headers: { "Foo" => "bar", "Bar" => "foo"},
+ username: "username",
+ password: "password",
+ body: body,
+ consumer: "Condor",
+ provider: "Pricing Service",
+ events: ["contract_content_changed"]
+ }.tap { |it| Pact::Fixture.add_fixture(:create_webhook_params, it) }
+ end
+
+ let(:body) { { some: "body" }.to_json }
+
+ let(:request_body) do
+ {
+ "events" => [
+ {
+ "name" => "contract_content_changed"
+ }
+ ],
+ "request" => {
+ "url" => "https://webhook",
+ "method" => "POST",
+ "headers" => {
+ "Foo" => "bar",
+ "Bar" => "foo"
+ },
+ "body" => {
+ "some" => "body"
+ },
+ "username" => "username",
+ "password" => "password"
+ }
+ }
+ end
+
+ let(:pact_broker_client_options) { {} }
+
+ subject { PactBroker::Client::Webhooks::Create.call(params, broker_base_url, pact_broker_client_options) }
+
+ context "when a valid webhook with a JSON body is submitted" do
+ before do
+ 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 and provider")
+ .with(
+ 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")
+ }
+ }
+ }
+ )
+ end
+
+ it "returns a CommandResult with success = true" do
+ expect(subject).to be_a PactBroker::Client::CommandResult
+ expect(subject.success).to be true
+ end
+ end
+
+ context "when a valid webhook with an XML body is submitted" do
+ before do
+ request_body["request"]["body"] = body
+
+ pact_broker
+ .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")
+ .with(
+ 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")
+ }
+ }
+ }
+ )
+ end
+
+ let(:body) { "" }
+
+ it "returns a CommandResult with success = true" do
+ expect(subject.success).to be true
+ end
+ end
+
+ context "when an invalid webhook is submitted" do
+ before do
+ params[:url] = nil
+ request_body["request"]["url"] = nil
+
+ pact_broker
+ .given("the 'Pricing Service' and 'Condor' already exist in the pact-broker")
+ .upon_receiving("an invalid request to create a webhook for a consumer and provider")
+ .with(
+ method: :post,
+ path: '/webhooks/provider/Pricing%20Service/consumer/Condor',
+ headers: post_request_headers,
+ body: request_body).
+ will_respond_with(
+ status: 400,
+ headers: pact_broker_response_headers,
+ body: {
+ errors: {
+ "request.url" => Pact.each_like("Some error")
+ }
+ }
+ )
+ end
+
+ it "returns a CommandResult 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
+
+ context "when one of the pacticipants does not exist" do
+ before do
+ pact_broker
+ .given("'Condor' does not exist in the pact-broker")
+ .upon_receiving("a request to create a webhook for a consumer and provider")
+ .with(
+ method: :post,
+ path: '/webhooks/provider/Pricing%20Service/consumer/Condor',
+ headers: post_request_headers,
+ body: request_body).
+ will_respond_with(
+ status: 404,
+ headers: pact_broker_response_headers
+ )
+ end
+
+ it "returns a CommandResult with success = false" do
+ expect(subject.success).to be false
+ expect(subject.message).to match /404/
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4912a79d..47779572 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -12,5 +12,31 @@
ENV.delete('PACT_BROKER_BASE_URL')
end
+ config.after(:all) do
+ Pact::Fixture.check_fixtures
+ end
+
config.filter_run_excluding :skip_windows => is_windows
end
+
+module Pact
+ module Fixture
+
+ def self.add_fixture key, value
+ fixtures[key] ||= []
+ fixtures[key] << value
+ end
+
+ def self.fixtures
+ @fixtures ||= {}
+ end
+
+ def self.check_fixtures
+ fixtures.each do | fixture_group |
+ if fixture_group.size > 1
+ #TODO compare fixtures to ensure they match
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_context.rb b/spec/support/shared_context.rb
index d01daea6..f1a6deae 100644
--- a/spec/support/shared_context.rb
+++ b/spec/support/shared_context.rb
@@ -5,7 +5,8 @@
let(:consumer_contract) { Pact::ConsumerContract.from_hash pact_hash }
let(:pact_json) { pact_hash.to_json }
let(:pact_broker_client) { PactBroker::Client::PactBrokerClient.new(client_config) }
- let(:client_config) { { base_url: 'http://localhost:1234' } }
+ let(:broker_base_url) { 'http://localhost:1234' }
+ let(:client_config) { { base_url: broker_base_url } }
let(:consumer_version) { '1.3.0' }
let(:version) { '1.3.0' }
let(:pact_broker_version) { Pact::Term.new(:matcher => /\d+\.\d+\.\d+/, :generate => '1.0.0') }
@@ -13,6 +14,7 @@
let(:default_request_headers) { { 'Content-Type' => 'application/json'} }
let(:patch_request_headers) { { 'Content-Type' => 'application/json'} }
let(:put_request_headers) { { 'Content-Type' => 'application/json', 'Accept' => 'application/hal+json'} }
+ let(:post_request_headers) { { 'Content-Type' => 'application/json', 'Accept' => 'application/hal+json'} }
let(:get_request_headers) { { 'Accept' => 'application/hal+json'} }
end