diff --git a/lib/pact_broker/api.rb b/lib/pact_broker/api.rb index 73bea53e5..6b39a4d79 100644 --- a/lib/pact_broker/api.rb +++ b/lib/pact_broker/api.rb @@ -29,6 +29,8 @@ module PactBroker # Latest pacts add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest'], Api::Resources::LatestPact, {resource_name: "latest_pact_publication"} add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest', :tag], Api::Resources::LatestPact, {resource_name: "latest_tagged_pact_publication"} + add ['pacts', 'provider', :provider_name], Api::Resources::ProviderPacts, {resource_name: "provider_pact_publications"} + add ['pacts', 'provider', :provider_name, 'tag', :tag], Api::Resources::ProviderPacts, {resource_name: "tagged_provider_pact_publications"} add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest-untagged'], Api::Resources::LatestPact, {resource_name: "latest_untagged_pact_publication", tag: :untagged} add ['pacts', 'provider', :provider_name, 'latest'], Api::Resources::LatestProviderPacts, {resource_name: "latest_provider_pact_publications"} add ['pacts', 'provider', :provider_name, 'latest', :tag], Api::Resources::LatestProviderPacts, {resource_name: "latest_tagged_provider_pact_publications"} diff --git a/lib/pact_broker/api/decorators/provider_pacts_decorator.rb b/lib/pact_broker/api/decorators/provider_pacts_decorator.rb index 19ac6fd19..881b901a8 100644 --- a/lib/pact_broker/api/decorators/provider_pacts_decorator.rb +++ b/lib/pact_broker/api/decorators/provider_pacts_decorator.rb @@ -2,19 +2,15 @@ require_relative 'pact_version_decorator' module PactBroker - module Api - module Decorators - - class ProviderPactsDecorator < BaseDecorator link :self do | context | suffix = context[:tag] ? " with tag '#{context[:tag]}'" : "" { href: context[:resource_url], - title: "Latest pact versions for the provider #{context[:provider_name]}#{suffix}" + title: context[:title] } end @@ -25,16 +21,25 @@ class ProviderPactsDecorator < BaseDecorator } end - links :'pacts' do | context | + links :'pb:pacts' do | context | represented.collect do | pact | { :href => pact_url(context[:base_url], pact), :title => pact.name, - :name => pact.consumer.name + :name => pact.consumer_name } end end + links :'pacts' do | context | + represented.collect do | pact | + { + :href => pact_url(context[:base_url], pact), + :title => 'DEPRECATED - please use the pb:pacts relation', + :name => pact.consumer_name + } + end + end end end end diff --git a/lib/pact_broker/api/resources/index.rb b/lib/pact_broker/api/resources/index.rb index 04156d270..07af5707e 100644 --- a/lib/pact_broker/api/resources/index.rb +++ b/lib/pact_broker/api/resources/index.rb @@ -50,7 +50,19 @@ def to_json 'pb:latest-provider-pacts-with-tag' => { href: base_url + '/pacts/provider/{provider}/latest/{tag}', - title: 'Latest pacts by provider with the specified tag', + title: 'Latest pacts for provider with the specified tag', + templated: true + }, + 'pb:provider-pacts-with-tag' => + { + href: base_url + '/pacts/provider/{provider}/tag/{tag}', + title: 'All pact versions for the provider with the specified consumer version tag', + templated: true + }, + 'pb:provider-pacts' => + { + href: base_url + '/pacts/provider/{provider}', + title: 'All pact versions for the specified provider', templated: true }, 'pb:latest-version' => { diff --git a/lib/pact_broker/api/resources/latest_provider_pacts.rb b/lib/pact_broker/api/resources/latest_provider_pacts.rb index 724805935..2873aa031 100644 --- a/lib/pact_broker/api/resources/latest_provider_pacts.rb +++ b/lib/pact_broker/api/resources/latest_provider_pacts.rb @@ -1,33 +1,21 @@ -require 'pact_broker/api/resources/base_resource' +require 'pact_broker/api/resources/provider_pacts' require 'pact_broker/configuration' require 'pact_broker/api/decorators/provider_pacts_decorator' module PactBroker module Api module Resources - - class LatestProviderPacts < BaseResource - - def content_types_provided - [["application/hal+json", :to_json]] - end - - def allowed_methods - ["GET"] - end - - def resource_exists? - pacticipant_service.find_pacticipant_by_name(provider_name) - end - - def to_json - PactBroker::Api::Decorators::ProviderPactsDecorator.new(pacts).to_json(user_options: decorator_context(identifier_from_path)) - end + class LatestProviderPacts < ProviderPacts + private def pacts pact_service.find_latest_pact_versions_for_provider provider_name, tag: identifier_from_path[:tag] end + def resource_title + suffix = identifier_from_path[:tag] ? " with consumer version tag '#{identifier_from_path[:tag]}'" : "" + "Latest pact versions for the provider #{identifier_from_path[:provider_name]}#{suffix}" + end end end end diff --git a/lib/pact_broker/api/resources/provider_pacts.rb b/lib/pact_broker/api/resources/provider_pacts.rb new file mode 100644 index 000000000..d418e3f98 --- /dev/null +++ b/lib/pact_broker/api/resources/provider_pacts.rb @@ -0,0 +1,45 @@ +require 'pact_broker/api/resources/base_resource' +require 'pact_broker/configuration' +require 'pact_broker/api/decorators/provider_pacts_decorator' + +module PactBroker + module Api + module Resources + class ProviderPacts < BaseResource + + def content_types_provided + [["application/hal+json", :to_json]] + end + + def allowed_methods + ["GET"] + end + + def resource_exists? + pacticipant_service.find_pacticipant_by_name(provider_name) + end + + def to_json + PactBroker::Api::Decorators::ProviderPactsDecorator.new(pacts).to_json(to_json_options) + end + + private + + def pacts + pact_service.find_pact_versions_for_provider provider_name, tag: identifier_from_path[:tag] + end + + def to_json_options + { + user_options: decorator_context(identifier_from_path.merge(title: resource_title)) + } + end + + def resource_title + suffix = identifier_from_path[:tag] ? " with consumer version tag '#{identifier_from_path[:tag]}'" : "" + "All pact versions for the provider #{identifier_from_path[:provider_name]}#{suffix}" + end + end + end + end +end diff --git a/lib/pact_broker/pacts/repository.rb b/lib/pact_broker/pacts/repository.rb index b0c2af498..873415f2d 100644 --- a/lib/pact_broker/pacts/repository.rb +++ b/lib/pact_broker/pacts/repository.rb @@ -70,6 +70,24 @@ def find_latest_pact_versions_for_provider provider_name, tag = nil end end + def find_pact_versions_for_provider provider_name, tag = nil + if tag + LatestPactPublicationsByConsumerVersion + .join(:tags, {version_id: :consumer_version_id}) + .provider(provider_name) + .order_ignore_case(:consumer_name) + .order_append(:consumer_version_order) + .where(Sequel[:tags][:name] => tag) + .collect(&:to_domain) + else + LatestPactPublicationsByConsumerVersion + .provider(provider_name) + .order_ignore_case(:consumer_name) + .order_append(:consumer_version_order) + .collect(&:to_domain) + end + end + # Returns latest pact version for the consumer_version_number def find_by_consumer_version consumer_name, consumer_version_number LatestPactPublicationsByConsumerVersion diff --git a/lib/pact_broker/pacts/service.rb b/lib/pact_broker/pacts/service.rb index e1064a712..320fcad65 100644 --- a/lib/pact_broker/pacts/service.rb +++ b/lib/pact_broker/pacts/service.rb @@ -68,6 +68,10 @@ def find_latest_pact_versions_for_provider provider_name, options = {} pact_repository.find_latest_pact_versions_for_provider provider_name, options[:tag] end + def find_pact_versions_for_provider provider_name, options = {} + pact_repository.find_pact_versions_for_provider provider_name, options[:tag] + end + def find_previous_distinct_pact_version params pact = find_pact params return nil if pact.nil? diff --git a/spec/features/get_provider_pacts_spec.rb b/spec/features/get_provider_pacts_spec.rb index b5d53f810..4c4297e11 100644 --- a/spec/features/get_provider_pacts_spec.rb +++ b/spec/features/get_provider_pacts_spec.rb @@ -1,18 +1,16 @@ -require 'spec/support/test_data_builder' - describe "Get provider pacts" do - - let(:path) { "/pacts/provider/Provider/latest" } let(:last_response_body) { JSON.parse(subject.body, symbolize_names: true) } - + let(:pact_links) { last_response_body[:_links][:'pb:pacts'] } subject { get path; last_response } context "when the provider exists" do - before do TestDataBuilder.new .create_provider("Provider") .create_consumer("Consumer") + .create_consumer_version("0.0.1") + .create_consumer_version_tag("prod") + .create_pact .create_consumer_version("1.0.0") .create_consumer_version_tag("prod") .create_pact @@ -20,22 +18,23 @@ .create_pact .create_consumer("Consumer 2") .create_consumer_version("4.5.6") + .create_consumer_version_tag("prod") .create_pact end context "with no tag specified" do + let(:path) { "/pacts/provider/Provider/latest" } it "returns a 200 HAL JSON response" do expect(subject).to be_a_hal_json_success_response end it "returns a list of links to the pacts" do - expect(last_response_body[:_links][:pacts].size).to eq 2 + expect(pact_links.size).to eq 2 end end context "with a tag specified" do - let(:path) { "/pacts/provider/Provider/latest/prod" } it "returns a 200 HAL JSON response" do @@ -43,12 +42,11 @@ end it "returns a list of links to the pacts" do - expect(last_response_body[:_links][:pacts].size).to eq 1 + expect(pact_links.size).to eq 2 end end context "with a tag with no pacts" do - let(:path) { "/pacts/provider/Provider/latest/foo" } it "returns a 200 HAL JSON response" do @@ -56,17 +54,40 @@ end it "returns a list of links to the pacts" do - expect(last_response_body[:_links][:pacts].size).to eq 0 + expect(pact_links.size).to eq 0 end end + context "with a tag for all pacts" do + let(:path) { "/pacts/provider/Provider/tag/prod" } + + it "returns a 200 HAL JSON response" do + expect(subject).to be_a_hal_json_success_response + end + + it "returns a list of links to the pacts" do + expect(pact_links.size).to eq 3 + end + end + + context "with no tag for all pacts" do + let(:path) { "/pacts/provider/Provider" } + + it "returns a 200 HAL JSON response" do + expect(subject).to be_a_hal_json_success_response + end + + it "returns a list of links to the pacts" do + expect(last_response_body[:_links][:'pb:pacts'].size).to eq 4 + end + end end context "when the provider does not exist" do + let(:path) { "/pacts/provider/Provider" } it "returns a 404 response" do expect(subject).to be_a_404_response end - end end diff --git a/spec/lib/pact_broker/api/resources/latest_provider_pacts_spec.rb b/spec/lib/pact_broker/api/resources/latest_provider_pacts_spec.rb new file mode 100644 index 000000000..69c08df52 --- /dev/null +++ b/spec/lib/pact_broker/api/resources/latest_provider_pacts_spec.rb @@ -0,0 +1,52 @@ +require 'pact_broker/api/resources/latest_provider_pacts' + +module PactBroker + module Api + module Resources + describe LatestProviderPacts do + before do + allow(PactBroker::Pacts::Service).to receive(:find_latest_pact_versions_for_provider).and_return(pacts) + allow(PactBroker::Api::Decorators::ProviderPactsDecorator).to receive(:new).and_return(decorator) + allow_any_instance_of(LatestProviderPacts).to receive(:resource_exists?).and_return(provider) + end + + let(:provider) { double('provider') } + let(:pacts) { double('pacts') } + let(:path) { '/pacts/provider/Bar/latest' } + let(:decorator) { instance_double('PactBroker::Api::Decorators::ProviderPactsDecorator') } + + subject { get path; last_response } + + context "with no tag" do + it "finds the pacts" do + expect(PactBroker::Pacts::Service).to receive(:find_latest_pact_versions_for_provider).with("Bar", tag: nil) + subject + end + + it "sets the correct resource title" do + expect(decorator).to receive(:to_json) do | options | + expect(options[:user_options][:title]).to eq "Latest pact versions for the provider Bar" + end + subject + end + end + + context "with a tag" do + let(:path) { '/pacts/provider/Bar/latest/prod' } + + it "finds the pacts with a tag" do + expect(PactBroker::Pacts::Service).to receive(:find_latest_pact_versions_for_provider).with("Bar", tag: "prod") + subject + end + + it "sets the correct resource title" do + expect(decorator).to receive(:to_json) do | options | + expect(options[:user_options][:title]).to eq "Latest pact versions for the provider Bar with consumer version tag 'prod'" + end + subject + end + end + end + end + end +end diff --git a/spec/lib/pact_broker/api/resources/provider_pacts_spec.rb b/spec/lib/pact_broker/api/resources/provider_pacts_spec.rb new file mode 100644 index 000000000..5d9a1ea8d --- /dev/null +++ b/spec/lib/pact_broker/api/resources/provider_pacts_spec.rb @@ -0,0 +1,75 @@ +require 'pact_broker/api/resources/provider_pacts' + +module PactBroker + module Api + module Resources + describe ProviderPacts do + before do + allow(PactBroker::Pacts::Service).to receive(:find_latest_pact_versions_for_provider).and_return(pacts) + allow(PactBroker::Api::Decorators::ProviderPactsDecorator).to receive(:new).and_return(decorator) + allow(PactBroker::Pacticipants::Service).to receive(:find_pacticipant_by_name).and_return(provider) + end + + let(:provider) { double('provider') } + let(:pacts) { double('pacts') } + let(:decorator) { instance_double('PactBroker::Api::Decorators::ProviderPactsDecorator', to_json: json) } + let(:json) { {some: 'json'}.to_json } + let(:path) { '/pacts/provider/Bar' } + + subject { get path; last_response } + + it "finds the pacticipant" do + expect(PactBroker::Pacticipants::Service).to receive(:find_pacticipant_by_name).with("Bar") + subject + end + + it "returns a 200 response status" do + expect(subject.status).to eq 200 + end + + it "returns a json response" do + expect(subject.headers['Content-Type']).to eq "application/hal+json;charset=utf-8" + expect(subject.body).to eq json + end + + context "with no tag" do + it "finds all the pacts for the given provider" do + expect(PactBroker::Pacts::Service).to receive(:find_pact_versions_for_provider).with("Bar", tag: nil) + subject + end + + it "sets the correct resource title" do + expect(decorator).to receive(:to_json) do | options | + expect(options[:user_options][:title]).to eq "All pact versions for the provider Bar" + end + subject + end + end + + context "with a tag" do + let(:path) { '/pacts/provider/Bar/tag/prod' } + + it "finds all the pacts with the given tag for the provider" do + expect(PactBroker::Pacts::Service).to receive(:find_pact_versions_for_provider).with("Bar", tag: "prod") + subject + end + + it "sets the correct resource title" do + expect(decorator).to receive(:to_json) do | options | + expect(options[:user_options][:title]).to eq "All pact versions for the provider Bar with consumer version tag 'prod'" + end + subject + end + end + + context "with the pacticipant does not exist" do + let(:provider) { nil } + + it "returns a 404" do + expect(subject.status).to eq 404 + end + end + end + end + end +end diff --git a/spec/lib/pact_broker/pacts/repository_spec.rb b/spec/lib/pact_broker/pacts/repository_spec.rb index c9ef907dc..3ef33adbc 100644 --- a/spec/lib/pact_broker/pacts/repository_spec.rb +++ b/spec/lib/pact_broker/pacts/repository_spec.rb @@ -244,7 +244,6 @@ module Pacts end describe "#find_latest_pact_versions_for_provider" do - context "with no tag specified" do before do TestDataBuilder.new @@ -308,7 +307,72 @@ module Pacts expect(subject.last.consumer_version.number).to eq "5.0.0" end end + end + + describe "#find_pact_versions_for_provider" do + context "with no tag specified" do + before do + TestDataBuilder.new + .create_consumer(consumer_name) + .create_consumer_version("1.0.0") + .create_provider(provider_name) + .create_pact + .create_consumer_version("1.2.3") + .create_pact + .create_consumer("wiffle consumer") + .create_consumer_version("4.0.0") + .create_pact + .create_consumer_version("4.5.6") + .create_pact + .create_provider("not the provider") + .create_pact + end + + subject { Repository.new.find_pact_versions_for_provider provider_name } + + it "returns all the pact versions for the specified provider" do + expect(subject.size).to eq 4 + expect(subject.first.provider.name).to eq provider_name + end + end + + context "with a tag specified" do + before do + TestDataBuilder.new + .create_consumer(consumer_name) + .create_consumer_version("1") + .create_consumer_version_tag("prod") + .create_provider(provider_name) + .create_pact + .create_consumer_version("2") + .create_consumer_version_tag("prod") + .create_pact + .create_consumer_version("3") + .create_pact + .create_consumer("wiffle consumer") + .create_consumer_version("10") + .create_pact + .create_consumer_version("11") + .create_consumer_version_tag("prod") + .create_consumer_version_tag("test") + .create_pact + .create_provider("not the provider") + .create_pact + end + + subject { Repository.new.find_pact_versions_for_provider provider_name, "prod" } + it "returns the pacts between the specified consumer and provider with the given tag" do + expect(subject.size).to eq 3 + expect(subject.first.provider.name).to eq provider_name + expect(subject.first.consumer.name).to eq consumer_name + expect(subject.first.consumer_version.number).to eq "1" + expect(subject[1].consumer_version.number).to eq "2" + expect(subject.first.json_content).to be nil + expect(subject.last.consumer.name).to eq "wiffle consumer" + expect(subject.last.consumer_version.number).to eq "11" + end + end end describe "find_pact" do