From 7679f6174f3840ffd3839911a096656659ee177f Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 25 Jan 2018 11:03:27 +1100 Subject: [PATCH] feat(describe-version): allow retrieving latest pacticipant version --- .../Pact Broker Client - Pact Broker.md | 106 ++++++++++++++++ lib/pact_broker/client/base_client.rb | 17 ++- lib/pact_broker/client/cli/broker.rb | 2 +- lib/pact_broker/client/error.rb | 2 + lib/pact_broker/client/versions.rb | 14 ++- lib/pact_broker/client/versions/describe.rb | 6 +- .../pact_broker/client/base_client_spec.rb | 28 +++++ .../pacts/pact_broker_client-pact_broker.json | 118 ++++++++++++++++++ .../pact_broker_client_versions_spec.rb | 114 +++++++++++++++++ 9 files changed, 398 insertions(+), 9 deletions(-) create mode 100644 spec/service_providers/pact_broker_client_versions_spec.rb diff --git a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md index 64718fc4..0a12db60 100644 --- a/doc/pacts/markdown/Pact Broker Client - Pact Broker.md +++ b/doc/pacts/markdown/Pact Broker Client - Pact Broker.md @@ -20,6 +20,10 @@ * [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_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 + * [A request for the list of the latest pacts from all consumers for the Pricing Service'](#a_request_for_the_list_of_the_latest_pacts_from_all_consumers_for_the_Pricing_Service'_given_a_latest_pact_between_Condor_and_the_Pricing_Service_exists) given a latest pact between Condor and the Pricing Service exists * [A request for the list of the latest prod pacts from all consumers for the Pricing Service'](#a_request_for_the_list_of_the_latest_prod_pacts_from_all_consumers_for_the_Pricing_Service'_given_tagged_as_prod_pact_between_Condor_and_the_Pricing_Service_exists) given tagged as prod pact between Condor and the Pricing Service exists @@ -50,10 +54,14 @@ * [A request to register the repository URL of a pacticipant](#a_request_to_register_the_repository_URL_of_a_pacticipant_given_the_'Pricing_Service'_does_not_exist_in_the_pact-broker) given the 'Pricing Service' does not exist in the pact-broker +* [A request to retrieve the latest 'production' version of Condor](#a_request_to_retrieve_the_latest_'production'_version_of_Condor_given_'Condor'_exists_in_the_pact-broker_with_the_latest_tagged_'production'_version_1.2.3) given 'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3 + * [A request to retrieve the latest pact between Condor and the Pricing Service](#a_request_to_retrieve_the_latest_pact_between_Condor_and_the_Pricing_Service_given_a_pact_between_Condor_and_the_Pricing_Service_exists) given a pact between Condor and the Pricing Service exists * [A request to retrieve the latest pact between Condor and the Pricing Service](#a_request_to_retrieve_the_latest_pact_between_Condor_and_the_Pricing_Service_given_no_pact_between_Condor_and_the_Pricing_Service_exists) given no pact between Condor and the Pricing Service exists +* [A request to retrieve the latest version of Condor](#a_request_to_retrieve_the_latest_version_of_Condor_given_'Condor'_exists_in_the_pact-broker_with_the_latest_version_1.2.3) given 'Condor' exists in the pact-broker with the latest version 1.2.3 + * [A request to retrieve the pact between the production verison of Condor and the Pricing Service](#a_request_to_retrieve_the_pact_between_the_production_verison_of_Condor_and_the_Pricing_Service_given_a_pact_between_Condor_and_the_Pricing_Service_exists_for_the_production_version_of_Condor) given a pact between Condor and the Pricing Service exists for the production version of Condor * [A request to tag the production version of Condor](#a_request_to_tag_the_production_version_of_Condor_given_'Condor'_exists_in_the_pact-broker_with_version_1.3.0,_tagged_with_'prod') given 'Condor' exists in the pact-broker with version 1.3.0, tagged with 'prod' @@ -418,6 +426,54 @@ Pact Broker will respond with: } } ``` + +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 +{ + "method": "get", + "path": "/", + "headers": { + "Accept": "application/json, application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "body": { + "_links": { + "pb:latest-tagged-version": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-TAGGED-VERSION-{pacticipant}-{tag}" + } + } + } +} +``` + +Given **the pb:latest-version relation exists in the index resource**, upon receiving **a request for the index resource** from Pact Broker Client, with +```json +{ + "method": "get", + "path": "/", + "headers": { + "Accept": "application/json, application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "body": { + "_links": { + "pb:latest-version": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-VERSION-{pacticipant}" + } + } + } +} +``` Given **a latest pact between Condor and the Pricing Service exists**, upon receiving **a request for the list of the latest pacts from all consumers for the Pricing Service'** from Pact Broker Client, with ```json @@ -923,6 +979,31 @@ Pact Broker will respond with: "status": 201 } ``` + +Given **'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3**, upon receiving **a request to retrieve the latest 'production' version of Condor** from Pact Broker Client, with +```json +{ + "method": "get", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-TAGGED-VERSION-Condor-production", + "headers": { + "Accept": "application/json, application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "body": { + "number": "1.2.3", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + } +} +``` Given **a pact between Condor and the Pricing Service exists**, upon receiving **a request to retrieve the latest pact between Condor and the Pricing Service** from Pact Broker Client, with ```json @@ -966,6 +1047,31 @@ Pact Broker will respond with: "status": 404 } ``` + +Given **'Condor' exists in the pact-broker with the latest version 1.2.3**, upon receiving **a request to retrieve the latest version of Condor** from Pact Broker Client, with +```json +{ + "method": "get", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-VERSION-Condor", + "headers": { + "Accept": "application/json, application/hal+json" + } +} +``` +Pact Broker will respond with: +```json +{ + "status": 200, + "body": { + "number": "1.2.3", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + } +} +``` Given **a pact between Condor and the Pricing Service exists for the production version of Condor**, upon receiving **a request to retrieve the pact between the production verison of Condor and the Pricing Service** from Pact Broker Client, with ```json diff --git a/lib/pact_broker/client/base_client.rb b/lib/pact_broker/client/base_client.rb index daa8c757..68bb551e 100644 --- a/lib/pact_broker/client/base_client.rb +++ b/lib/pact_broker/client/base_client.rb @@ -63,7 +63,7 @@ def default_put_headers def handle_response response if response.success? - yield + yield response elsif response.code == 404 nil elsif response.code == 401 @@ -98,6 +98,21 @@ def get url, *args self.class.get(url, *args) end + def url_for_relation relation_name, params + handle_response(get("/", headers: default_get_headers)) do | response | + relation = (JSON.parse(response.body)['_links'] || {})[relation_name] + if relation + url = relation['href'] + params.each do | (key, value) | + url = url.gsub("{#{key}}", value) + end + url + else + raise PactBroker::Client::RelationNotFound.new("Could not find relation #{relation_name} in index resource. Try upgrading your Pact Broker as the feature you require may not exist in your version.") + end + end + end + def verbose? @verbose end diff --git a/lib/pact_broker/client/cli/broker.rb b/lib/pact_broker/client/cli/broker.rb index ef62fc7c..c5b731f0 100644 --- a/lib/pact_broker/client/cli/broker.rb +++ b/lib/pact_broker/client/cli/broker.rb @@ -89,7 +89,7 @@ def describe_version pacticipant: options.pacticipant, version: options.version, latest: latest, - tag: options.latest.is_a?(String) ? options.latest : nil + tag: options.latest != "latest" ? options.latest : nil } opts = { output: options.output diff --git a/lib/pact_broker/client/error.rb b/lib/pact_broker/client/error.rb index 6926a484..28723c60 100644 --- a/lib/pact_broker/client/error.rb +++ b/lib/pact_broker/client/error.rb @@ -1,5 +1,7 @@ module PactBroker module Client class Error < StandardError; end + + class RelationNotFound < Error; end end end diff --git a/lib/pact_broker/client/versions.rb b/lib/pact_broker/client/versions.rb index 49b04d3e..3b78a1ae 100644 --- a/lib/pact_broker/client/versions.rb +++ b/lib/pact_broker/client/versions.rb @@ -13,13 +13,15 @@ def find options end end - # TODO this is not a valid URL! def latest options - query = options[:tag] ? {tag: options[:tag]} : {} - response = self.class.get("#{versions_base_url(options)}/latest", query: query, headers: default_get_headers) - - handle_response(response) do - string_keys_to_symbols(response.to_hash) + puts options + url = if options[:tag] + url_for_relation('pb:latest-tagged-version', pacticipant: options.fetch(:pacticipant), tag: options.fetch(:tag)) + else + url_for_relation('pb:latest-version', pacticipant: options.fetch(:pacticipant)) + end + handle_response(get(url, headers: default_get_headers)) do | response | + JSON.parse(response.body, symbolize_names: true) end end diff --git a/lib/pact_broker/client/versions/describe.rb b/lib/pact_broker/client/versions/describe.rb index 62331062..34b0e3e3 100644 --- a/lib/pact_broker/client/versions/describe.rb +++ b/lib/pact_broker/client/versions/describe.rb @@ -32,7 +32,11 @@ def call else pact_broker_client.pacticipants.versions.latest(params) end - Result.new(true, format_version(version_hash)) + if version_hash + Result.new(true, format_version(version_hash)) + else + Result.new(false, "Pacticipant version not found") + end end private diff --git a/spec/lib/pact_broker/client/base_client_spec.rb b/spec/lib/pact_broker/client/base_client_spec.rb index 2d565680..c5210add 100644 --- a/spec/lib/pact_broker/client/base_client_spec.rb +++ b/spec/lib/pact_broker/client/base_client_spec.rb @@ -3,6 +3,8 @@ module PactBroker module Client describe BaseClient do describe '#initialize' do + subject { BaseClient.new(base_url: base_url) } + let(:base_url) { 'http://pact_broker_base_url'} let(:username) { 'pact_repo_username'} let(:password) { 'pact_repo_password'} @@ -45,6 +47,32 @@ module Client subject end end + + describe "url_for_relation" do + let(:index_body) do + { + _links: { + :'pb:foo-bar' => { + href: "http://foo-bar/{name}" + } + } + }.to_json + end + + let!(:request) do + stub_request(:get, "http://pact_broker_base_url/").to_return(status: 200, body: index_body, headers: { "Content-Type" => "application/json" } ) + end + + it "retrieves a relation URL from the index" do + expect(subject.url_for_relation('pb:foo-bar', name: 'wiffle')).to eq 'http://foo-bar/wiffle' + end + + context "when the relation is not found" do + it "raises an error" do + expect { subject.url_for_relation('pb:not-found', name: 'wiffle') }.to raise_error PactBroker::Client::RelationNotFound, /Could not find relation pb:not-found in index resource/ + 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 6aeb0b46..af282dc8 100644 --- a/spec/pacts/pact_broker_client-pact_broker.json +++ b/spec/pacts/pact_broker_client-pact_broker.json @@ -1090,6 +1090,124 @@ } } } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:latest-version relation exists in the index resource", + "request": { + "method": "get", + "path": "/", + "headers": { + "Accept": "application/json, application/hal+json" + } + }, + "response": { + "status": 200, + "headers": { + }, + "body": { + "_links": { + "pb:latest-version": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-VERSION-{pacticipant}" + } + } + }, + "matchingRules": { + "$.body._links.pb:latest-version.href": { + "match": "regex", + "regex": "http:\\/\\/.*{pacticipant}" + } + } + } + }, + { + "description": "a request to retrieve the latest version of Condor", + "providerState": "'Condor' exists in the pact-broker with the latest version 1.2.3", + "request": { + "method": "get", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-VERSION-Condor", + "headers": { + "Accept": "application/json, application/hal+json" + } + }, + "response": { + "status": 200, + "headers": { + }, + "body": { + "number": "1.2.3", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "http:\\/\\/.*" + } + } + } + }, + { + "description": "a request for the index resource", + "providerState": "the pb:latest-tagged-version relation exists in the index resource", + "request": { + "method": "get", + "path": "/", + "headers": { + "Accept": "application/json, application/hal+json" + } + }, + "response": { + "status": 200, + "headers": { + }, + "body": { + "_links": { + "pb:latest-tagged-version": { + "href": "http://localhost:1234/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-TAGGED-VERSION-{pacticipant}-{tag}" + } + } + }, + "matchingRules": { + "$.body._links.pb:latest-tagged-version.href": { + "match": "regex", + "regex": "http:\\/\\/.*{pacticipant}.*{tag}" + } + } + } + }, + { + "description": "a request to retrieve the latest 'production' version of Condor", + "providerState": "'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3", + "request": { + "method": "get", + "path": "/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-TAGGED-VERSION-Condor-production", + "headers": { + "Accept": "application/json, application/hal+json" + } + }, + "response": { + "status": 200, + "headers": { + }, + "body": { + "number": "1.2.3", + "_links": { + "self": { + "href": "http://localhost:1234/some-url" + } + } + }, + "matchingRules": { + "$.body._links.self.href": { + "match": "regex", + "regex": "http:\\/\\/.*" + } + } + } } ], "metadata": { diff --git a/spec/service_providers/pact_broker_client_versions_spec.rb b/spec/service_providers/pact_broker_client_versions_spec.rb new file mode 100644 index 00000000..cfbbf49d --- /dev/null +++ b/spec/service_providers/pact_broker_client_versions_spec.rb @@ -0,0 +1,114 @@ +require 'spec_helper' +require 'pact_broker/client' +require_relative 'pact_helper' + +describe PactBroker::Client::Versions, pact: true do + + include_context "pact broker" + + let(:get_headers) { { "Accept" => "application/json, application/hal+json" } } + + describe "retrieving the latest pacticipant version" do + let(:latest_version_path) { "/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-VERSION-{pacticipant}" } + let(:latest_version_url) { pact_broker.mock_service_base_url + latest_version_path } + + before do + pact_broker + .given("the pb:latest-version relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with( + method: :get, + path: '/', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:latest-version' => { + href: Pact.term(latest_version_url, /http:\/\/.*{pacticipant}/) + } + } + } + ) + + pact_broker + .given("'Condor' exists in the pact-broker with the latest version 1.2.3") + .upon_receiving("a request to retrieve the latest version of Condor") + .with( + method: :get, + path: '/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-VERSION-Condor', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + number: '1.2.3', + _links: { + self: { + href: Pact.term('http://localhost:1234/some-url', %r{http://.*}) + } + } + } + ) + end + + it "returns the version hash" do + version_hash = pact_broker_client.pacticipants.versions.latest(pacticipant: 'Condor') + expect(version_hash[:number]).to eq '1.2.3' + expect(version_hash[:_links][:self][:href]).to eq 'http://localhost:1234/some-url' + end + end + + describe "retrieving the latest pacticipant version for a tag" do + let(:latest_tagged_version_path) { "/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-TAGGED-VERSION-{pacticipant}-{tag}" } + let(:latest_tagged_version_url) { pact_broker.mock_service_base_url + latest_tagged_version_path } + + before do + pact_broker + .given("the pb:latest-tagged-version relation exists in the index resource") + .upon_receiving("a request for the index resource") + .with( + method: :get, + path: '/', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + _links: { + :'pb:latest-tagged-version' => { + href: Pact.term(latest_tagged_version_url, /http:\/\/.*{pacticipant}.*{tag}/) + } + } + } + ) + + pact_broker + .given("'Condor' exists in the pact-broker with the latest tagged 'production' version 1.2.3") + .upon_receiving("a request to retrieve the latest 'production' version of Condor") + .with( + method: :get, + path: '/HAL-REL-PLACEHOLDER-INDEX-PB-LATEST-TAGGED-VERSION-Condor-production', + headers: get_headers). + will_respond_with( + status: 200, + headers: pact_broker_response_headers, + body: { + number: '1.2.3', + _links: { + self: { + href: Pact.term('http://localhost:1234/some-url', %r{http://.*}) + } + } + } + ) + end + + it "returns the version hash" do + version_hash = pact_broker_client.pacticipants.versions.latest(pacticipant: 'Condor', tag: 'production') + expect(version_hash[:number]).to eq '1.2.3' + expect(version_hash[:_links][:self][:href]).to eq 'http://localhost:1234/some-url' + end + end +end \ No newline at end of file