diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index f3d29d381..dac2e9201 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -59,6 +59,9 @@ module PactBroker add ['relationships'], Api::Resources::Relationships, {resource_name: "relationships"} add ['groups', :pacticipant_name], Api::Resources::Group, {resource_name: "group"} + # matrix + add ['the-matrix', 'provider', :provider_name, 'consumer', :consumer_name], Api::Resources::Matrix, {resource_name: "matrix_consumer_provider"} + add [], Api::Resources::Index, {resource_name: "index"} end end diff --git a/lib/pact_broker/api/decorators/matrix_decorator.rb b/lib/pact_broker/api/decorators/matrix_decorator.rb new file mode 100644 index 000000000..0e137dcaa --- /dev/null +++ b/lib/pact_broker/api/decorators/matrix_decorator.rb @@ -0,0 +1,102 @@ +require 'ostruct' +require 'pact_broker/api/pact_broker_urls' + +module PactBroker + module Api + module Decorators + class MatrixPactDecorator + include PactBroker::Api::PactBrokerUrls + + def initialize(lines) + @lines = lines + end + + def to_json(options) + to_hash(options).to_json + end + + def to_hash(options) + { + matrix: matrix(lines, options[:user_options][:base_url]) + } + end + + private + + attr_reader :lines + + def matrix(lines, base_url) + provider = nil + consumer = nil + lines.collect do | line | + provider ||= OpenStruct.new(name: line[:provider_name]) + consumer ||= OpenStruct.new(name: line[:consumer_name]) + consumer_version = OpenStruct.new(number: line[:consumer_version_number], pacticipant: consumer) + line_hash(consumer, provider, consumer_version, line, base_url) + end + end + + def line_hash(consumer, provider, consumer_version, line, base_url) + { + consumer: consumer_hash(line, consumer_version, base_url), + provider: provider_hash(line, provider, base_url), + pact: pact_hash(line, base_url), + verificationResult: verification_hash(line, base_url) + } + end + + def consumer_hash(line, consumer_version, base_url) + { + version: { + number: line[:consumer_version_number], + _links: { + self: { + href: version_url(base_url, consumer_version) + } + } + } + } + end + + def provider_hash(line, provider, base_url) + if !line[:provider_version].nil? + { + version: { + number: line[:provider_version] + } + } + else + { + version: nil + } + end + end + + def pact_hash(line, base_url) + { + _links: { + self: { + href: pact_url_from_params(base_url, line) + } + } + } + end + + def verification_hash(line, base_url) + if !line[:success].nil? + { + success: line[:success], + _links: { + self: { + href: verification_url(OpenStruct.new(line), base_url) + } + } + } + else + nil + end + end + end + end + end +end diff --git a/lib/pact_broker/api/pact_broker_urls.rb b/lib/pact_broker/api/pact_broker_urls.rb index d4657a6ac..a3a5bd30d 100644 --- a/lib/pact_broker/api/pact_broker_urls.rb +++ b/lib/pact_broker/api/pact_broker_urls.rb @@ -80,7 +80,7 @@ def verification_url verification, base_url [ base_url, 'pacts', 'provider', url_encode(verification.provider_name), 'consumer', url_encode(verification.consumer_name), - 'pact-version', verification.pact_version.sha, + 'pact-version', verification.pact_version_sha, 'verification-results', verification.number ].join('/') end diff --git a/lib/pact_broker/api/resources/index.rb b/lib/pact_broker/api/resources/index.rb index 0957a1d8e..1db0da84c 100644 --- a/lib/pact_broker/api/resources/index.rb +++ b/lib/pact_broker/api/resources/index.rb @@ -58,7 +58,8 @@ def to_json href: base_url + '/webhooks', title: 'Webhooks', templated: false - },'curies' => + }, + 'curies' => [{ name: 'pb', href: base_url + '/doc/{rel}', @@ -67,10 +68,7 @@ def to_json } }.to_json end - - end end - end -end \ No newline at end of file +end diff --git a/lib/pact_broker/api/resources/matrix.rb b/lib/pact_broker/api/resources/matrix.rb new file mode 100644 index 000000000..f44f3e61d --- /dev/null +++ b/lib/pact_broker/api/resources/matrix.rb @@ -0,0 +1,42 @@ +require 'pact_broker/api/resources/base_resource' +require 'pact_broker/api/decorators/matrix_decorator' + +module PactBroker + module Api + module Resources + class Matrix < BaseResource + + def content_types_provided + [["application/hal+json", :to_json]] + end + + def allowed_methods + ["GET"] + end + + def resource_exists? + consumer && provider + end + + def to_json + lines = matrix_service.find(identifier_from_path) + PactBroker::Api::Decorators::MatrixPactDecorator.new(lines).to_json(user_options: { base_url: base_url }) + end + + def consumer + @consumer ||= find_pacticipant(identifier_from_path[:consumer_name], "consumer") + end + + def provider + @provider ||= find_pacticipant(identifier_from_path[:provider_name], "provider") + end + + def find_pacticipant name, role + pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant | + set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil? + end + end + end + end + end +end \ No newline at end of file diff --git a/lib/pact_broker/matrix/repository.rb b/lib/pact_broker/matrix/repository.rb new file mode 100644 index 000000000..3e3913ef1 --- /dev/null +++ b/lib/pact_broker/matrix/repository.rb @@ -0,0 +1,19 @@ +require 'pact_broker/repositories/helpers' + +module PactBroker + module Matrix + class Repository + include PactBroker::Repositories::Helpers + + def find consumer_name, provider_name + PactBroker::Pacts::LatestPactPublicationsByConsumerVersion + .left_outer_join(:latest_verifications, pact_version_id: :pact_version_id) + .consumer(consumer_name) + .provider(provider_name) + .reverse(:consumer_version_order) + .all + .collect(&:values) + end + end + end +end diff --git a/lib/pact_broker/matrix/service.rb b/lib/pact_broker/matrix/service.rb new file mode 100644 index 000000000..755e6aa0f --- /dev/null +++ b/lib/pact_broker/matrix/service.rb @@ -0,0 +1,15 @@ +require 'pact_broker/repositories' + +module PactBroker + module Matrix + module Service + extend self + + extend PactBroker::Repositories + + def find params + matrix_repository.find params[:consumer_name], params[:provider_name] + end + end + end +end diff --git a/lib/pact_broker/repositories.rb b/lib/pact_broker/repositories.rb index 83069ec31..164427ea4 100644 --- a/lib/pact_broker/repositories.rb +++ b/lib/pact_broker/repositories.rb @@ -37,6 +37,11 @@ def verification_repository Verifications::Repository.new end + def matrix_repository + require 'pact_broker/matrix/repository' + Matrix::Repository.new + end + extend self end end diff --git a/lib/pact_broker/services.rb b/lib/pact_broker/services.rb index c7fd8355e..cfb47f363 100644 --- a/lib/pact_broker/services.rb +++ b/lib/pact_broker/services.rb @@ -46,5 +46,10 @@ def badge_service require 'pact_broker/badges/service' Badges::Service end + + def matrix_service + require 'pact_broker/matrix/service' + Matrix::Service + end end end diff --git a/spec/features/get_matrix_spec.rb b/spec/features/get_matrix_spec.rb new file mode 100644 index 000000000..9e172cac3 --- /dev/null +++ b/spec/features/get_matrix_spec.rb @@ -0,0 +1,25 @@ +require 'spec/support/test_data_builder' + +describe "Get matrix" do + before do + TestDataBuilder.new + .create_pact_with_hierarchy('Consumer', '1.0.0', 'Provider') + .create_verification(provider_version: '4.5.6') + end + + let(:path) { "/the-matrix/provider/Provider/consumer/Consumer" } + let(:last_response_body) { JSON.parse(subject.body, symbolize_names: true) } + + subject { get path; last_response } + + it "returns a 200 HAL JSON response" do + expect(subject).to be_a_hal_json_success_response + end + + it "returns the JSON representation of the matrix" do + expect(last_response_body[:matrix][0][:consumer]).to be_instance_of(Hash) + expect(last_response_body[:matrix][0][:provider]).to be_instance_of(Hash) + expect(last_response_body[:matrix][0][:pact]).to be_instance_of(Hash) + expect(last_response_body[:matrix][0][:verificationResult]).to be_instance_of(Hash) + end +end diff --git a/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb b/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb new file mode 100644 index 000000000..fac26abb0 --- /dev/null +++ b/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb @@ -0,0 +1,121 @@ +require 'pact_broker/api/decorators/matrix_decorator' + +module PactBroker + module Api + module Decorators + describe MatrixPactDecorator do + describe "to_json" do + let(:line_1) do + { + consumer_name: "Consumer", + consumer_version_number: "1.0.0", + pact_version_sha: "1234", + provider_version: "4.5.6", + provider_name: "Provider", + success: true, + number: 1, + build_url: nil, + execution_date: DateTime.now + } + end + + let(:line_2) do + { + consumer_name: "Consumer", + consumer_version_number: "1.0.0", + pact_version_sha: "1234", + provider_version: nil, + provider_name: "Provider", + success: nil, + number: nil, + build_url: nil, + execution_date: nil + } + end + + let(:consumer_hash) do + { + version: { + number: '1.0.0', + _links: { + self: { + href: 'http://example.org/pacticipants/Consumer/versions/1.0.0' + } + } + } + } + end + + let(:provider_hash) do + { + version: { + number: '4.5.6' + } + } + end + + let(:verification_hash) do + { + success: true, + _links: { + self: { + href: "http://example.org/pacts/provider/Provider/consumer/Consumer/pact-version/1234/verification-results/1" + } + } + } + end + + let(:pact_hash) do + { + _links: { + self: { + href: "http://example.org/pacts/provider/Provider/consumer/Consumer/version/1.0.0" + } + } + } + end + + let(:lines){ [line_1, line_2]} + let(:json) { MatrixPactDecorator.new(lines).to_json(user_options: { base_url: 'http://example.org' }) } + let(:parsed_json) { JSON.parse(json, symbolize_names: true) } + + it "includes the consumer details" do + expect(parsed_json[:matrix][0][:consumer]).to eq consumer_hash + end + + it "includes the provider details" do + expect(parsed_json[:matrix][0][:provider]).to eq provider_hash + end + + it "includes the verification details" do + expect(parsed_json[:matrix][0][:verificationResult]).to eq verification_hash + end + + it "includes the pact details" do + expect(parsed_json[:matrix][0][:pact]).to eq pact_hash + end + + context "when the pact has not been verified" do + let(:provider_hash) do + { + version: nil + } + end + + let(:verification_hash) do + nil + end + + it "has empty provider details" do + expect(parsed_json[:matrix][1][:provider]).to eq provider_hash + end + + it "has a nil verificationResult" do + expect(parsed_json[:matrix][1][:verificationResult]).to eq verification_hash + end + end + end + end + end + end +end diff --git a/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb b/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb index 520acdbd5..01cc01c31 100644 --- a/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +++ b/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb @@ -13,17 +13,11 @@ module Decorators provider_name: 'Provider', consumer_name: 'Consumer', build_url: 'http://build-url', - pact_version: pact_version, + pact_version_sha: '1234', latest_pact_publication: pact_publication, execution_date: DateTime.now) end - let(:pact_version) do - instance_double('PactBroker::Pacts::PactVersion', - sha: '1234' - ) - end - let(:pact_publication) do instance_double('PactBroker::Pacts::PactPublication', name: 'A name', diff --git a/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb b/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb index 0074e0509..64c14204f 100644 --- a/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb +++ b/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb @@ -17,11 +17,12 @@ module Decorators provider_name: 'Provider', consumer_name: 'Consumer', pact_version: pact_version, + pact_version_sha: '1234', latest_pact_publication: pact, execution_date: DateTime.now) end let(:pact_version) do - instance_double("PactBroker::Pacts::PactVersion", sha: '1234', name: 'Name') + instance_double("PactBroker::Pacts::PactVersion", name: 'Name') end let(:pact) { instance_double("PactBroker::Domain::Pact", name: "Some pact", consumer_name: "Foo", provider_name: "Bar", consumer_version_number: "1.2.3") } diff --git a/spec/lib/pact_broker/matrix/repository_spec.rb b/spec/lib/pact_broker/matrix/repository_spec.rb new file mode 100644 index 000000000..2ed7db055 --- /dev/null +++ b/spec/lib/pact_broker/matrix/repository_spec.rb @@ -0,0 +1,31 @@ +require 'pact_broker/matrix/repository' + +module PactBroker + module Matrix + describe Repository do + describe "#find" do + before do + TestDataBuilder.new + .create_pact_with_hierarchy("Consumer", "1.2.3", "Provider") + .create_verification(provider_version: "6.7.8") + .revise_pact + .create_verification(provider_version: "4.5.6") + .create_consumer_version("2.0.0") + .create_pact + end + + subject { Repository.new.find "Consumer", "Provider" } + + it "returns the latest revision of each pact in reverse consumer_version_order" do + expect(subject.count).to eq 2 + expect(subject[0][:consumer_version_number]).to eq "2.0.0" + expect(subject[1][:consumer_version_number]).to eq "1.2.3" + end + + it "returns the latest verification for the pact version" do + expect(subject[1][:provider_version]).to eq "4.5.6" + end + end + end + end +end