diff --git a/lib/pact_broker/api/decorators/pact_decorator.rb b/lib/pact_broker/api/decorators/pact_decorator.rb index 58ab272a3..9d3d6ad36 100644 --- a/lib/pact_broker/api/decorators/pact_decorator.rb +++ b/lib/pact_broker/api/decorators/pact_decorator.rb @@ -42,6 +42,18 @@ def to_hash(options = {}) } end + links :'pb:consumer-versions' do | options | + if options[:consumer_versions] + options[:consumer_versions].collect do | consumer_version | + { + title: "Consumer version", + name: consumer_version.number, + href: version_url(options.fetch(:base_url), consumer_version) + } + end + end + end + link :'pb:provider' do | options | { title: "Provider", diff --git a/lib/pact_broker/api/resources/metadata_resource_methods.rb b/lib/pact_broker/api/resources/metadata_resource_methods.rb index 039a0ed59..17f92903f 100644 --- a/lib/pact_broker/api/resources/metadata_resource_methods.rb +++ b/lib/pact_broker/api/resources/metadata_resource_methods.rb @@ -11,13 +11,43 @@ def pact_params @pact_params ||= PactBroker::Pacts::PactParams.from_request(request, maybe_params_with_consumer_version_number.merge(path_info)) end - def maybe_params_with_consumer_version_number - metadata.slice(:consumer_version_number) + def maybe_consumer_version_number_param + if metadata[:consumer_version_number] + metadata.slice(:consumer_version_number) + elsif metadata_consumer_version_numbers&.any? + { + consumer_version_number: consumer_versions_from_metadata.last&.number + } + else + {} + end end def metadata @metadata ||= PactBroker::Pacts::Metadata.parse_metadata(PactBrokerUrls.decode_pact_metadata(identifier_from_path[:metadata])) end + + def metadata_consumer_version_numbers + @metadata_consumer_version_numbers ||= begin + if metadata[:consumer_version_selectors].is_a?(Array) + metadata[:consumer_version_selectors].collect{ | selector | selector[:consumer_version_number] }.compact.uniq + elsif metadata[:consumer_version_number] + [metadata[:consumer_version_number]] + else + nil + end + end + end + + def consumer_versions_from_metadata + @consumer_versions_from_metadata ||= begin + if metadata_consumer_version_numbers + metadata_consumer_version_numbers.collect do | consumer_version_number | + version_service.find_by_pacticipant_name_and_number(pacticipant_name: identifier_from_path[:consumer_name], pacticipant_version_number: consumer_version_number) + end.compact.sort_by(&:order) + end + end + end end end end diff --git a/lib/pact_broker/api/resources/pact_version.rb b/lib/pact_broker/api/resources/pact_version.rb index b7ec2dc5c..cf55b1fe9 100644 --- a/lib/pact_broker/api/resources/pact_version.rb +++ b/lib/pact_broker/api/resources/pact_version.rb @@ -10,6 +10,10 @@ class PactVersion < Pact def allowed_methods ["GET", "OPTIONS"] end + + def decorator_options(options) + super(options.merge(consumer_versions: consumer_versions_from_metadata&.reverse)) + end end end end diff --git a/lib/pact_broker/test/http_test_data_builder.rb b/lib/pact_broker/test/http_test_data_builder.rb index 90061f869..85bfbf87a 100644 --- a/lib/pact_broker/test/http_test_data_builder.rb +++ b/lib/pact_broker/test/http_test_data_builder.rb @@ -224,6 +224,7 @@ def create_webhook_for_event(uuid: nil, url: "https://postman-echo.com/post", bo puts "Creating #{webhook_prefix}webhook for contract changed event with uuid #{uuid}" uuid ||= SecureRandom.uuid default_body = { + "pactUrl" => "${pactbroker.pactUrl}", "eventName" => "${pactbroker.eventName}", "consumerName" => "${pactbroker.consumerName}", "consumerVersionNumber" => "${pactbroker.consumerVersionNumber}", diff --git a/script/data/branches.rb b/script/data/branches.rb index 5359f405d..da716434a 100755 --- a/script/data/branches.rb +++ b/script/data/branches.rb @@ -11,7 +11,7 @@ .publish_contract(consumer: "branch-consumer", provider: "branch-provider", consumer_version: "1", content_id: "1111", branch: "main") .publish_contract(consumer: "branch-consumer", provider: "branch-provider", consumer_version: "1", content_id: "1111", branch: "feat/x") .publish_contract(consumer: "branch-consumer", provider: "branch-provider", consumer_version: "2", content_id: "1111", branch: "feat/x") - .get_pacts_for_verification(provider: "branch-provider", enable_pending: false) + .get_pacts_for_verification(provider: "branch-provider", enable_pending: false, consumer_version_selectors: [ { branch: "main" }, { branch: "feat/x" }]) .verify_pact( provider_version_branch: "main", provider_version: "1", diff --git a/spec/integration/pact_metdata_spec.rb b/spec/integration/pact_metdata_spec.rb new file mode 100644 index 000000000..2db0303f8 --- /dev/null +++ b/spec/integration/pact_metdata_spec.rb @@ -0,0 +1,105 @@ +RSpec.describe "the consumer version relations in the pact version resource" do + context "when requested with no metadata" do + before do + td.create_pact_with_hierarchy("Foo", "1", "Bar") + .create_consumer_version("2") + .republish_same_pact + end + + let(:pact_version_url) { PactBroker::Api::PactBrokerUrls.pact_version_url(td.and_return(:pact)) } + + subject { get(pact_version_url) } + + + it "includes the consumer version of the latest version that published the pact" do + body = JSON.parse(subject.body) + consumer_version_relation = body["_links"]["pb:consumer-version"] + expect(consumer_version_relation).to include("name" => "2") + + consumer_versions_relations = body["_links"]["pb:consumer-versions"] + expect(consumer_versions_relations).to be nil + end + end + context "for a pact published webhook" do + before do + td.create_global_webhook(event_names: ["contract_published"], body: { "pact_url" => "${pactbroker.pactUrl}" }) + .create_consumer("Foo") + .create_provider("Bar") + .create_consumer_version("1") + .create_pact(json_content: pact_content) + end + + let(:database_connector) { ->(&block) { block.call } } + let(:pact_content) { td.random_json_content("Foo", "Bar") } + + let!(:webhook_request) do + stub_request(:post, /http/).to_return(:status => 200) + end + + let(:publish_pact) { put("/pacts/provider/Bar/consumer/Foo/version/2", pact_content, { "CONTENT_TYPE" => "application/json", "pactbroker.database_connector" => database_connector }) } + + let(:pact_version_response) do + publish_pact + pact_url = PactBroker::Webhooks::Execution.last.logs.scan(/(http:\/\/example.org\/pacts.*?)"/).last.last + get(pact_url) + end + + it "includes the consumer version of the version that just published the pact" do + body = JSON.parse(pact_version_response.body) + + consumer_version_relation = body["_links"]["pb:consumer-version"] + expect(consumer_version_relation).to include("name" => "2") + + consumer_versions_relations = body["_links"]["pb:consumer-versions"] + expect(consumer_versions_relations.size).to eq 1 + expect(consumer_versions_relations).to contain_hash("name" => "2") + end + end + + context "for pacts for verification" do + before do + json_content = td.random_json_content("Foo", "Bar") + td.create_consumer("Foo") + .create_provider("Bar") + .create_consumer_version("1", branch: "main") + .create_pact(json_content: json_content) + .create_consumer_version("2", branch: "feat/x") + .create_pact(json_content: json_content) + .create_consumer_version("2", branch: "feat/y") + .create_pact(json_content: json_content) + .create_consumer_version("3", branch: "feat/z") + end + + let(:pacts_for_verification_request_body) do + { + consumerVersionSelectors: [ { branch: "main" }, { branch: "feat/x" }, { branch: "feat/y" } ], + }.to_json + end + + let(:rack_headers) do + { + "HTTP_ACCEPT" => "application/hal+json" + } + end + + let(:pacts_for_verification) do + response = post("/pacts/provider/Bar/for-verification", pacts_for_verification_request_body, rack_headers) + JSON.parse(response.body)["_embedded"]["pacts"] + end + + let(:pact_version_url) do + pacts_for_verification[0]["_links"]["self"]["href"] + end + + it "includes links for all the consumer versions for which it was selected" do + body = JSON.parse(get(pact_version_url).body) + consumer_version_relation = body["_links"]["pb:consumer-version"] + expect(consumer_version_relation).to include("name" => "2") + + consumer_versions_relations = body["_links"]["pb:consumer-versions"] + expect(consumer_versions_relations.size).to eq 2 + expect(consumer_versions_relations).to contain_hash("name" => "2") + expect(consumer_versions_relations).to contain_hash("name" => "1") + end + end +end