Skip to content

Commit

Permalink
feat: create endpoint to compare arbitrary pact versions, ignoring in…
Browse files Browse the repository at this point in the history
…teraction/message order
  • Loading branch information
bethesque committed May 3, 2018
1 parent a519731 commit 15f0688
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 30 deletions.
2 changes: 2 additions & 0 deletions lib/pact_broker/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ module PactBroker
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'pact-version', :pact_version_sha], Api::Resources::PactVersion, {resource_name: "pact_publication"}
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'version', :consumer_version_number, 'previous-distinct'], Api::Resources::PreviousDistinctPactVersion, {resource_name: "previous_distinct_pact_version"}
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'version', :consumer_version_number, 'diff', 'previous-distinct'], Api::Resources::PactContentDiff, {resource_name: "previous_distinct_pact_version_diff"}
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'version', :consumer_version_number, 'diff', 'version', :comparison_consumer_version], Api::Resources::PactContentDiff, {resource_name: "pact_version_diff_by_consumer_version"}
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'pact-version', :pact_version_sha, 'diff', 'pact-version', :comparison_pact_version_sha], Api::Resources::PactContentDiff, {resource_name: "pact_version_diff_by_pact_version_sha"}

# Verifications
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'pact-version', :pact_version_sha, 'verification-results'], Api::Resources::Verifications, {resource_name: "verification_results"}
Expand Down
16 changes: 14 additions & 2 deletions lib/pact_broker/api/resources/pact_content_diff.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def resource_exists?
end

def to_text
output = PactBroker::Pacts::Diff.new.process pact_params.merge(base_url: base_url)
output = PactBroker::Pacts::Diff.new.process pact_params.merge(base_url: base_url), comparison_pact_params, raw: false
response.body = output
end

Expand All @@ -29,7 +29,19 @@ def pact
end

def pact_params
@pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
@pact_params ||= PactBroker::Pacts::PactParams.from_path_info identifier_from_path
end

def comparison_pact_params
if identifier_from_path[:comparison_consumer_version_number] || identifier_from_path[:comparison_pact_version_sha]
comparison_identifier_from_path = identifier_from_path.merge(
consumer_version_number: identifier_from_path[:comparison_consumer_version_number],
pact_version_sha: identifier_from_path[:comparison_pact_version_sha],
base_url: base_url)
PactBroker::Pacts::PactParams.from_path_info(comparison_identifier_from_path)
else
nil
end
end
end
end
Expand Down
37 changes: 24 additions & 13 deletions lib/pact_broker/pacts/diff.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'pact_broker/api/pact_broker_urls'
require 'pact_broker/date_helper'
require 'pact_broker/pacts/create_formatted_diff'
require 'pact_broker/pacts/sort_verifiable_content'
require 'pact_broker/repositories'
require 'yaml'

Expand All @@ -10,13 +11,13 @@ module Pacts
class Diff
include PactBroker::Repositories

def process(params)
def process(params, comparison_pact_params = nil, options = {})
pact = find_pact(params)
previous_distinct_pact = pact_repository.find_previous_distinct_pact(pact)
comparison_pact = comparison_pact_params ? find_pact(comparison_pact_params) : pact_repository.find_previous_distinct_pact(pact)

if previous_distinct_pact
next_pact = pact_repository.find_next_pact(previous_distinct_pact)
DiffDecorator.new(pact, previous_distinct_pact, next_pact, params[:base_url]).to_text
if comparison_pact
next_pact = pact_repository.find_next_pact(comparison_pact) || pact
DiffDecorator.new(pact, comparison_pact, next_pact, params[:base_url], { raw: options[:raw] }).to_text
else
no_previous_version_message pact
end
Expand All @@ -27,7 +28,8 @@ def process(params)
def find_pact(params)
pact_repository.find_pact(params.consumer_name,
params.consumer_version_number,
params.provider_name)
params.provider_name,
params.pact_version_sha)
end

def no_previous_version_message(pact)
Expand All @@ -46,11 +48,12 @@ def no_previous_version_message(pact)
# the latest distinct version content was first created.

class DiffDecorator
def initialize(pact, previous_distinct_pact, next_pact, base_url)
def initialize(pact, comparison_pact, next_pact, base_url, options)
@pact = pact
@previous_distinct_pact = previous_distinct_pact
@comparison_pact = comparison_pact
@next_pact = next_pact
@base_url = base_url
@options = options
end

def to_text
Expand All @@ -59,7 +62,7 @@ def to_text

private

attr_reader :pact, :previous_distinct_pact, :next_pact, :base_url
attr_reader :pact, :comparison_pact, :next_pact, :base_url, :options

def change_date_in_words
DateHelper.local_date_in_words next_pact.created_at
Expand All @@ -70,15 +73,15 @@ def now
end

def header
title = "# Diff between versions #{previous_distinct_pact.consumer_version_number} and #{pact.consumer_version_number} of the pact between #{pact.consumer.name} and #{pact.provider.name}"
title = "# Diff between versions #{comparison_pact.consumer_version_number} and #{pact.consumer_version_number} of the pact between #{pact.consumer.name} and #{pact.provider.name}"
description = "The following changes were made #{change_date_ago_in_words} ago (#{change_date_in_words})"

title + "\n\n" + description
end

def links
self_url = PactBroker::Api::PactBrokerUrls.pact_url(base_url, pact)
previous_distinct_url = PactBroker::Api::PactBrokerUrls.pact_url(base_url, previous_distinct_pact)
previous_distinct_url = PactBroker::Api::PactBrokerUrls.pact_url(base_url, comparison_pact)

links = {
"current-pact-version" => {
Expand All @@ -88,20 +91,28 @@ def links
},
"previous-distinct-pact-version" => {
"title" => "Pact",
"name" => previous_distinct_pact.name,
"name" => comparison_pact.name,
"href" => previous_distinct_url
}
}
"## Links\n" + YAML.dump(links).gsub(/---/,'')
end

def diff
CreateFormattedDiff.(pact.json_content, previous_distinct_pact.json_content)
CreateFormattedDiff.(prepare_content(pact.json_content), prepare_content(comparison_pact.json_content))
end

def change_date_ago_in_words
DateHelper.distance_of_time_in_words next_pact.created_at, now
end

def prepare_content json_content
if options[:raw]
json_content
else
PactBroker::Pacts::SortVerifiableContent.call(json_content)
end
end
end
end
end
Expand Down
10 changes: 10 additions & 0 deletions lib/pact_broker/pacts/pact_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ def initialize attributes
merge!(attributes)
end

def self.from_path_info path_info
new(
consumer_name: path_info.fetch(:consumer_name),
provider_name: path_info.fetch(:provider_name),
consumer_version_number: path_info[:consumer_version_number],
revision_number: path_info[:revision_number],
pact_version_sha: path_info[:pact_version_sha]
)
end

def self.from_request request, path_info
json_content = request.body.to_s
parsed_content = begin
Expand Down
41 changes: 41 additions & 0 deletions lib/pact_broker/pacts/sort_verifiable_content.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'pact_broker/json'

module PactBroker
module Pacts
class SortVerifiableContent

def self.call json
hash = JSON.parse(json, PACT_PARSING_OPTIONS)
verifiable_content = if hash['interactions']
hash['interactions']
elsif hash['messages']
hash['messages']
end
order_verifiable_content(verifiable_content).to_json
end

def self.order_verifiable_content array
array_with_ordered_hashes = order_hashes(array)
array_with_ordered_hashes.sort{|a, b| a.to_json <=> b.to_json }
end

def self.order_hashes thing
case thing
when Hash then order_hash(thing)
when Array then order_child_array(thing)
else thing
end
end

def self.order_child_array array
array.collect{|thing| order_hashes(thing) }
end

def self.order_hash hash
hash.keys.sort.each_with_object({}) do | key, new_hash |
new_hash[key] = order_hashes(hash[key])
end
end
end
end
end
41 changes: 34 additions & 7 deletions spec/lib/pact_broker/pacts/diff_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ module Pacts
end
let(:pact_content_version_2) { load_fixture('consumer-provider.json') }
let(:pact_content_version_3) { pact_content_version_2 }
let(:pact_content_version_4) do
hash = load_json_fixture('consumer-provider.json')
hash['interactions'].first['request']['method'] = 'delete'
hash.to_json
end

let(:pact_params) do
PactBroker::Pacts::PactParams.new(
consumer_name: 'Consumer',
provider_name: 'Provider',
consumer_version_number: '3'
)
end

before do
TestDataBuilder.new
Expand All @@ -26,21 +39,36 @@ module Pacts
.create_pact(json_content: pact_content_version_2)
.create_consumer_version("3")
.create_pact(json_content: pact_content_version_3)
.create_consumer_version("4")
.create_pact(json_content: pact_content_version_4)

allow(DateHelper).to receive(:local_date_in_words).and_return("a date")
end

subject { Diff.new.process(pact_params.merge(base_url: 'http://example.org')) }

context "when there is a previous distinct version" do
subject { Diff.new.process(pact_params.merge(base_url: 'http://example.org'), nil, raw: true) }

let(:pact_params) do
context "when a comparison version is specified" do
let(:comparison_pact_params) do
PactBroker::Pacts::PactParams.new(
consumer_name: 'Consumer',
provider_name: 'Provider',
consumer_version_number: '3'
)
consumer_version_number: '4'
).merge(base_url: 'http://example.org')
end

subject { Diff.new.process(pact_params.merge(base_url: 'http://example.org'), comparison_pact_params) }

it "compares the two pacts" do
expect(subject).to include "Pact between Consumer (v3) and Provider"
expect(subject).to include "Pact between Consumer (v4) and Provider"
end

it "includes a link to the comparison pact", pending: true do
expect(subject).to include "comparision-pact-version:"
end
end

context "when there is a previous distinct version" do
it "indicates when the previous change was made" do
expect(subject).to include "The following changes were made less than a minute ago (a date)"
end
Expand All @@ -66,7 +94,6 @@ module Pacts
end
end
end

end
end
end
41 changes: 33 additions & 8 deletions spec/lib/pact_broker/pacts/pact_params_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,44 @@ module Pacts
let(:consumer_version_number) { '1.2.3' }
let(:headers) { { 'X-Pact-Consumer-Version' => consumer_version_number, 'Host' => 'example.org' } }
let(:revision_number) { '1' }
let(:path_info) do
{
consumer_name: 'Consumer',
provider_name: 'Provider',
consumer_version_number: '1.2.3',
revision_number: revision_number,
pact_version_sha: '123'
}
end

describe "from_path_info" do
subject { PactParams.from_path_info(path_info) }

it "extracts the consumer name from the path" do
expect(subject.consumer_name).to eq "Consumer"
end

it "extracts the provider name from the path" do
expect(subject.provider_name).to eq "Provider"
end

it "extracts the consumer_version_number from the path" do
expect(subject.consumer_version_number).to eq "1.2.3"
end

it "extracts the revision_number from the path" do
expect(subject.revision_number).to eq "1"
end

it "extracts the pact_version_sha from the path" do
expect(subject.pact_version_sha).to eq "123"
end
end

describe "from_request" do

context "from a PUT request" do
let(:request) { Webmachine::Request.new("PUT", "/", headers, body)}
let(:path_info) do
{
consumer_name: 'Consumer',
provider_name: 'Provider',
consumer_version_number: '1.2.3',
revision_number: revision_number
}
end

subject { PactParams.from_request(request, path_info) }

Expand Down
25 changes: 25 additions & 0 deletions spec/lib/pact_broker/pacts/sort_verifiable_content_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'pact_broker/pacts/sort_verifiable_content'

module PactBroker
module Pacts
describe SortVerifiableContent do
let(:pact_content_1) do
{
a: 1,
interactions: [{ a: 1, b: 2 }, { a: 2, b: 3 }]
}.to_json
end

let(:pact_content_2) do
{
interactions: [{ b: 3, a: 2}, { b: 2, a: 1 }],
a: 1
}.to_json
end

it "sorts the interactions/messages and keys in a deterministic way" do
expect(SortVerifiableContent.call(pact_content_1).to_json).to eq(SortVerifiableContent.call(pact_content_2).to_json)
end
end
end
end

0 comments on commit 15f0688

Please sign in to comment.