From ca68c5487a5c510b19b42ac8d6d4e791e293649a Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Tue, 10 Oct 2017 11:17:59 +1100 Subject: [PATCH] feat: add 'pact-broker can-i-deploy' executable --- bin/pact-broker | 4 + lib/pact_broker/client/base_client.rb | 11 +- lib/pact_broker/client/can_i_deploy.rb | 49 ++++++++ lib/pact_broker/client/cli/broker.rb | 46 +++++++ lib/pact_broker/client/matrix.rb | 16 +++ lib/pact_broker/client/pact_broker_client.rb | 10 +- pact-broker-client.gemspec | 2 +- .../pacts/pact_broker_client-pact_broker.json | 61 +++++++++ .../pact_broker_client_matrix_spec.rb | 117 ++++++++++++++++++ 9 files changed, 309 insertions(+), 7 deletions(-) create mode 100755 bin/pact-broker create mode 100644 lib/pact_broker/client/can_i_deploy.rb create mode 100644 lib/pact_broker/client/cli/broker.rb create mode 100644 lib/pact_broker/client/matrix.rb create mode 100644 spec/service_providers/pact_broker_client_matrix_spec.rb diff --git a/bin/pact-broker b/bin/pact-broker new file mode 100755 index 00000000..7175cd40 --- /dev/null +++ b/bin/pact-broker @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require 'pact_broker/client/cli/broker' + +PactBroker::Client::CLI::Broker.start diff --git a/lib/pact_broker/client/base_client.rb b/lib/pact_broker/client/base_client.rb index a658b624..88f30873 100644 --- a/lib/pact_broker/client/base_client.rb +++ b/lib/pact_broker/client/base_client.rb @@ -1,9 +1,12 @@ require 'erb' require 'httparty' +require 'pact_broker/client/error' module PactBroker module Client + class Error < StandardError; end + module UrlHelpers def encode_param param ERB::Util.url_encode param @@ -57,7 +60,13 @@ def handle_response response elsif response.code == 404 nil else - raise response.body + error_message = nil + begin + error_message = JSON.parse(response.body)['errors'].join("\n") + rescue + raise Error.new(response.body) + end + raise Error.new(error_message) end end diff --git a/lib/pact_broker/client/can_i_deploy.rb b/lib/pact_broker/client/can_i_deploy.rb new file mode 100644 index 00000000..8b00ffd7 --- /dev/null +++ b/lib/pact_broker/client/can_i_deploy.rb @@ -0,0 +1,49 @@ +require 'pact_broker/client/error' +require 'pact_broker/client/pact_broker_client' + +module PactBroker + module Client + class CanIDeploy + + class Result + attr_reader :success, :message + + def initialize success, message = nil + @success = success + @message = message + end + end + + def self.call(pact_broker_base_url, version_selectors, pact_broker_client_options={}) + new(pact_broker_base_url, version_selectors, pact_broker_client_options).call + end + + def initialize(pact_broker_base_url, version_selectors, pact_broker_client_options) + @pact_broker_base_url = pact_broker_base_url + @version_selectors = version_selectors + @pact_broker_client_options = pact_broker_client_options + end + + def call + matrix = pact_broker_client.matrix.get(version_selectors) + if matrix[:matrix].any? + Result.new(true, 'Computer says yes \o/') + else + Result.new(false, 'Computer says no ¯\_(ツ)_/¯') + end + rescue PactBroker::Client::Error => e + Result.new(false, e.message) + rescue StandardError => e + Result.new(false, "Error retrieving matrix #{e.class} - #{e.message}\n#{e.backtrace.join("\n")}") + end + + private + + attr_reader :pact_broker_base_url, :version_selectors, :pact_broker_client_options + + def pact_broker_client + @pact_broker_client ||= PactBroker::Client::PactBrokerClient.new(base_url: pact_broker_base_url, client_options: pact_broker_client_options) + end + end + end +end diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb new file mode 100644 index 00000000..2f93da6d --- /dev/null +++ b/lib/pact_broker/client/cli/broker.rb @@ -0,0 +1,46 @@ +require 'pact_broker/client/can_i_deploy' +require 'pact_broker/client/version' +require 'thor' + +module PactBroker + module Client + module CLI + class Broker < Thor + desc 'can-i-deploy VERSION_SELECTOR_ONE VERSION_SELECTOR_TWO ...', "Returns exit code 0 or 1, indicating whether or not the specified application versions are compatible.\n\nThe VERSION_SELECTOR format is /version/." + + method_option :broker_base_url, required: true, aliases: "-b", desc: "The base URL of the Pact Broker" + method_option :broker_username, aliases: "-n", desc: "Pact Broker basic auth username" + method_option :broker_password, aliases: "-p", desc: "Pact Broker basic auth password" + method_option :verbose, aliases: "-v", desc: "Verbose output", :required => false + + def can_i_deploy(*selectors) + result = CanIDeploy.call(options.broker_base_url, selectors, pact_broker_client_options) + if result.success + $stdout.puts result.message + else + $stdout.puts result.message + exit(1) + end + end + + desc 'version', "Show the pact_broker-client gem version" + def version + $stdout.puts PactBroker::Client::VERSION + end + + no_commands do + def pact_broker_client_options + if options[:username] + { + username: options[:username], + password: options[:password] + } + else + {} + end + end + end + end + end + end +end diff --git a/lib/pact_broker/client/matrix.rb b/lib/pact_broker/client/matrix.rb new file mode 100644 index 00000000..2b04911c --- /dev/null +++ b/lib/pact_broker/client/matrix.rb @@ -0,0 +1,16 @@ +require_relative 'base_client' + +module PactBroker + module Client + class Matrix < BaseClient + def get selectors + query = {selectors: selectors} + response = self.class.get("/matrix", query: query, headers: default_get_headers) + + handle_response(response) do + JSON.parse(response.body, symbolize_names: true) + end + end + end + end +end diff --git a/lib/pact_broker/client/pact_broker_client.rb b/lib/pact_broker/client/pact_broker_client.rb index cfb7327a..e7cbf68f 100644 --- a/lib/pact_broker/client/pact_broker_client.rb +++ b/lib/pact_broker/client/pact_broker_client.rb @@ -1,11 +1,9 @@ require 'pact_broker/client/pacticipants' require 'pact_broker/client/versions' require 'pact_broker/client/pacts' - +require 'pact_broker/client/matrix' module PactBroker - - module Client DEFAULT_PACT_BROKER_BASE_URL = 'http://pact-broker' @@ -30,7 +28,9 @@ def pacts PactBroker::Client::Pacts.new base_url: base_url, client_options: client_options end + def matrix + PactBroker::Client::Matrix.new base_url: base_url, client_options: client_options + end end end - -end \ No newline at end of file +end diff --git a/pact-broker-client.gemspec b/pact-broker-client.gemspec index e785eaaa..268e240b 100644 --- a/pact-broker-client.gemspec +++ b/pact-broker-client.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency 'json' gem.add_runtime_dependency 'term-ansicolor' - gem.add_development_dependency 'pry' + gem.add_development_dependency 'pry-byebug' gem.add_development_dependency 'fakefs', '~> 0.4' gem.add_development_dependency 'rspec-fire' gem.add_development_dependency 'appraisal' diff --git a/spec/pacts/pact_broker_client-pact_broker.json b/spec/pacts/pact_broker_client-pact_broker.json index 6cee5123..c049b82f 100644 --- a/spec/pacts/pact_broker_client-pact_broker.json +++ b/spec/pacts/pact_broker_client-pact_broker.json @@ -198,6 +198,67 @@ } } }, + { + "description": "a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6", + "providerState": "the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6", + "request": { + "method": "get", + "path": "/matrix", + "query": "selectors[]=Foo%2Fversion%2F1.2.3&selectors[]=Bar%2Fversion%2F4.5.6" + }, + "response": { + "status": 200, + "headers": { + }, + "body": { + "matrix": [ + { + } + ] + } + } + }, + { + "description": "a request for the compatibility matrix where one or more versions does not exist", + "providerState": "the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6", + "request": { + "method": "get", + "path": "/matrix", + "query": "selectors[]=Foo%2Fversion%2F1.2.3&selectors[]=Bar%2Fversion%2F9.9.9" + }, + "response": { + "status": 400, + "headers": { + }, + "body": { + "errors": { + "json_class": "Pact::ArrayLike", + "contents": "an error message", + "min": 1 + } + } + } + }, + { + "description": "a request for the compatibility matrix for a pacticipant that does not exist", + "request": { + "method": "get", + "path": "/matrix", + "query": "selectors[]=Foo%2Fversion%2F1.2.3&selectors[]=Bar%2Fversion%2F9.9.9" + }, + "response": { + "status": 400, + "headers": { + }, + "body": { + "errors": { + "json_class": "Pact::ArrayLike", + "contents": "an error message", + "min": 1 + } + } + } + }, { "description": "a request to publish a pact", "providerState": "the 'Pricing Service' already exists in the pact-broker", diff --git a/spec/service_providers/pact_broker_client_matrix_spec.rb b/spec/service_providers/pact_broker_client_matrix_spec.rb new file mode 100644 index 00000000..74276d17 --- /dev/null +++ b/spec/service_providers/pact_broker_client_matrix_spec.rb @@ -0,0 +1,117 @@ +require_relative 'pact_helper' +require 'pact_broker/client' + +module PactBroker::Client + describe Matrix, :pact => true do + + include_context "pact broker" + + describe "retriving the compatibility matrix" do + let(:matrix_response_body) do + { + matrix: [{}] + } + end + + context "when results are found" do + before do + pact_broker. + given("the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix for Foo version 1.2.3 and Bar version 4.5.6"). + with( + method: :get, + path: "/matrix", + query: { + 'selectors[]' => ['Foo/version/1.2.3', 'Bar/version/4.5.6'] + } + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + + it 'a matrix of compatible versions' do + matrix = pact_broker_client.matrix.get(['Foo/version/1.2.3', 'Bar/version/4.5.6']) + expect(matrix[:matrix].size).to eq 1 + end + end + + context "with only one version selector" do + before do + pact_broker. + given("the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix where only the version of Foo is specified"). + with( + method: :get, + path: "/matrix", + query: { + 'selectors[]' => ['Foo/version/1.2.3'] + } + ). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: matrix_response_body + ) + end + end + + context "when one or more of the versions does not exist" do + before do + pact_broker. + given("the pact for Foo version 1.2.3 has been verified by Bar version 4.5.6"). + upon_receiving("a request for the compatibility matrix where one or more versions does not exist"). + with( + method: :get, + path: "/matrix", + query: { + 'selectors[]' => ['Foo/version/1.2.3', 'Bar/version/9.9.9'] + } + ). + will_respond_with( + status: 400, + headers: pact_broker_response_headers, + body: { + errors: Pact.each_like("an error message") + } + ) + end + + it 'returns a list of errors' do + expect { + pact_broker_client.matrix.get(['Foo/version/1.2.3', 'Bar/version/9.9.9']) + }.to raise_error PactBroker::Client::Error, "an error message" + end + end + + context "when results are not found" do + before do + pact_broker. + upon_receiving("a request for the compatibility matrix for a pacticipant that does not exist"). + with( + method: :get, + path: "/matrix", + query: { + 'selectors[]' => ['Foo/version/1.2.3', 'Bar/version/9.9.9'] + } + ). + will_respond_with( + status: 400, + headers: pact_broker_response_headers, + body: { + errors: Pact.each_like("an error message") + } + ) + end + + it 'returns a list of errors' do + expect { + pact_broker_client.matrix.get(['Foo/version/1.2.3', 'Bar/version/9.9.9']) + }.to raise_error PactBroker::Client::Error, "an error message" + end + end + end + end +end