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