Skip to content

Commit 15f0688

Browse files
committed
feat: create endpoint to compare arbitrary pact versions, ignoring interaction/message order
1 parent a519731 commit 15f0688

File tree

8 files changed

+183
-30
lines changed

8 files changed

+183
-30
lines changed

lib/pact_broker/api.rb

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ module PactBroker
1616
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'pact-version', :pact_version_sha], Api::Resources::PactVersion, {resource_name: "pact_publication"}
1717
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'version', :consumer_version_number, 'previous-distinct'], Api::Resources::PreviousDistinctPactVersion, {resource_name: "previous_distinct_pact_version"}
1818
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"}
19+
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"}
20+
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"}
1921

2022
# Verifications
2123
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'pact-version', :pact_version_sha, 'verification-results'], Api::Resources::Verifications, {resource_name: "verification_results"}

lib/pact_broker/api/resources/pact_content_diff.rb

+14-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def resource_exists?
2020
end
2121

2222
def to_text
23-
output = PactBroker::Pacts::Diff.new.process pact_params.merge(base_url: base_url)
23+
output = PactBroker::Pacts::Diff.new.process pact_params.merge(base_url: base_url), comparison_pact_params, raw: false
2424
response.body = output
2525
end
2626

@@ -29,7 +29,19 @@ def pact
2929
end
3030

3131
def pact_params
32-
@pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
32+
@pact_params ||= PactBroker::Pacts::PactParams.from_path_info identifier_from_path
33+
end
34+
35+
def comparison_pact_params
36+
if identifier_from_path[:comparison_consumer_version_number] || identifier_from_path[:comparison_pact_version_sha]
37+
comparison_identifier_from_path = identifier_from_path.merge(
38+
consumer_version_number: identifier_from_path[:comparison_consumer_version_number],
39+
pact_version_sha: identifier_from_path[:comparison_pact_version_sha],
40+
base_url: base_url)
41+
PactBroker::Pacts::PactParams.from_path_info(comparison_identifier_from_path)
42+
else
43+
nil
44+
end
3345
end
3446
end
3547
end

lib/pact_broker/pacts/diff.rb

+24-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require 'pact_broker/api/pact_broker_urls'
22
require 'pact_broker/date_helper'
33
require 'pact_broker/pacts/create_formatted_diff'
4+
require 'pact_broker/pacts/sort_verifiable_content'
45
require 'pact_broker/repositories'
56
require 'yaml'
67

@@ -10,13 +11,13 @@ module Pacts
1011
class Diff
1112
include PactBroker::Repositories
1213

13-
def process(params)
14+
def process(params, comparison_pact_params = nil, options = {})
1415
pact = find_pact(params)
15-
previous_distinct_pact = pact_repository.find_previous_distinct_pact(pact)
16+
comparison_pact = comparison_pact_params ? find_pact(comparison_pact_params) : pact_repository.find_previous_distinct_pact(pact)
1617

17-
if previous_distinct_pact
18-
next_pact = pact_repository.find_next_pact(previous_distinct_pact)
19-
DiffDecorator.new(pact, previous_distinct_pact, next_pact, params[:base_url]).to_text
18+
if comparison_pact
19+
next_pact = pact_repository.find_next_pact(comparison_pact) || pact
20+
DiffDecorator.new(pact, comparison_pact, next_pact, params[:base_url], { raw: options[:raw] }).to_text
2021
else
2122
no_previous_version_message pact
2223
end
@@ -27,7 +28,8 @@ def process(params)
2728
def find_pact(params)
2829
pact_repository.find_pact(params.consumer_name,
2930
params.consumer_version_number,
30-
params.provider_name)
31+
params.provider_name,
32+
params.pact_version_sha)
3133
end
3234

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

4850
class DiffDecorator
49-
def initialize(pact, previous_distinct_pact, next_pact, base_url)
51+
def initialize(pact, comparison_pact, next_pact, base_url, options)
5052
@pact = pact
51-
@previous_distinct_pact = previous_distinct_pact
53+
@comparison_pact = comparison_pact
5254
@next_pact = next_pact
5355
@base_url = base_url
56+
@options = options
5457
end
5558

5659
def to_text
@@ -59,7 +62,7 @@ def to_text
5962

6063
private
6164

62-
attr_reader :pact, :previous_distinct_pact, :next_pact, :base_url
65+
attr_reader :pact, :comparison_pact, :next_pact, :base_url, :options
6366

6467
def change_date_in_words
6568
DateHelper.local_date_in_words next_pact.created_at
@@ -70,15 +73,15 @@ def now
7073
end
7174

7275
def header
73-
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}"
76+
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}"
7477
description = "The following changes were made #{change_date_ago_in_words} ago (#{change_date_in_words})"
7578

7679
title + "\n\n" + description
7780
end
7881

7982
def links
8083
self_url = PactBroker::Api::PactBrokerUrls.pact_url(base_url, pact)
81-
previous_distinct_url = PactBroker::Api::PactBrokerUrls.pact_url(base_url, previous_distinct_pact)
84+
previous_distinct_url = PactBroker::Api::PactBrokerUrls.pact_url(base_url, comparison_pact)
8285

8386
links = {
8487
"current-pact-version" => {
@@ -88,20 +91,28 @@ def links
8891
},
8992
"previous-distinct-pact-version" => {
9093
"title" => "Pact",
91-
"name" => previous_distinct_pact.name,
94+
"name" => comparison_pact.name,
9295
"href" => previous_distinct_url
9396
}
9497
}
9598
"## Links\n" + YAML.dump(links).gsub(/---/,'')
9699
end
97100

98101
def diff
99-
CreateFormattedDiff.(pact.json_content, previous_distinct_pact.json_content)
102+
CreateFormattedDiff.(prepare_content(pact.json_content), prepare_content(comparison_pact.json_content))
100103
end
101104

102105
def change_date_ago_in_words
103106
DateHelper.distance_of_time_in_words next_pact.created_at, now
104107
end
108+
109+
def prepare_content json_content
110+
if options[:raw]
111+
json_content
112+
else
113+
PactBroker::Pacts::SortVerifiableContent.call(json_content)
114+
end
115+
end
105116
end
106117
end
107118
end

lib/pact_broker/pacts/pact_params.rb

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ def initialize attributes
1010
merge!(attributes)
1111
end
1212

13+
def self.from_path_info path_info
14+
new(
15+
consumer_name: path_info.fetch(:consumer_name),
16+
provider_name: path_info.fetch(:provider_name),
17+
consumer_version_number: path_info[:consumer_version_number],
18+
revision_number: path_info[:revision_number],
19+
pact_version_sha: path_info[:pact_version_sha]
20+
)
21+
end
22+
1323
def self.from_request request, path_info
1424
json_content = request.body.to_s
1525
parsed_content = begin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
require 'pact_broker/json'
2+
3+
module PactBroker
4+
module Pacts
5+
class SortVerifiableContent
6+
7+
def self.call json
8+
hash = JSON.parse(json, PACT_PARSING_OPTIONS)
9+
verifiable_content = if hash['interactions']
10+
hash['interactions']
11+
elsif hash['messages']
12+
hash['messages']
13+
end
14+
order_verifiable_content(verifiable_content).to_json
15+
end
16+
17+
def self.order_verifiable_content array
18+
array_with_ordered_hashes = order_hashes(array)
19+
array_with_ordered_hashes.sort{|a, b| a.to_json <=> b.to_json }
20+
end
21+
22+
def self.order_hashes thing
23+
case thing
24+
when Hash then order_hash(thing)
25+
when Array then order_child_array(thing)
26+
else thing
27+
end
28+
end
29+
30+
def self.order_child_array array
31+
array.collect{|thing| order_hashes(thing) }
32+
end
33+
34+
def self.order_hash hash
35+
hash.keys.sort.each_with_object({}) do | key, new_hash |
36+
new_hash[key] = order_hashes(hash[key])
37+
end
38+
end
39+
end
40+
end
41+
end

spec/lib/pact_broker/pacts/diff_spec.rb

+34-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ module Pacts
1515
end
1616
let(:pact_content_version_2) { load_fixture('consumer-provider.json') }
1717
let(:pact_content_version_3) { pact_content_version_2 }
18+
let(:pact_content_version_4) do
19+
hash = load_json_fixture('consumer-provider.json')
20+
hash['interactions'].first['request']['method'] = 'delete'
21+
hash.to_json
22+
end
23+
24+
let(:pact_params) do
25+
PactBroker::Pacts::PactParams.new(
26+
consumer_name: 'Consumer',
27+
provider_name: 'Provider',
28+
consumer_version_number: '3'
29+
)
30+
end
1831

1932
before do
2033
TestDataBuilder.new
@@ -26,21 +39,36 @@ module Pacts
2639
.create_pact(json_content: pact_content_version_2)
2740
.create_consumer_version("3")
2841
.create_pact(json_content: pact_content_version_3)
42+
.create_consumer_version("4")
43+
.create_pact(json_content: pact_content_version_4)
44+
2945
allow(DateHelper).to receive(:local_date_in_words).and_return("a date")
3046
end
3147

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

36-
let(:pact_params) do
50+
context "when a comparison version is specified" do
51+
let(:comparison_pact_params) do
3752
PactBroker::Pacts::PactParams.new(
3853
consumer_name: 'Consumer',
3954
provider_name: 'Provider',
40-
consumer_version_number: '3'
41-
)
55+
consumer_version_number: '4'
56+
).merge(base_url: 'http://example.org')
4257
end
4358

59+
subject { Diff.new.process(pact_params.merge(base_url: 'http://example.org'), comparison_pact_params) }
60+
61+
it "compares the two pacts" do
62+
expect(subject).to include "Pact between Consumer (v3) and Provider"
63+
expect(subject).to include "Pact between Consumer (v4) and Provider"
64+
end
65+
66+
it "includes a link to the comparison pact", pending: true do
67+
expect(subject).to include "comparision-pact-version:"
68+
end
69+
end
70+
71+
context "when there is a previous distinct version" do
4472
it "indicates when the previous change was made" do
4573
expect(subject).to include "The following changes were made less than a minute ago (a date)"
4674
end
@@ -66,7 +94,6 @@ module Pacts
6694
end
6795
end
6896
end
69-
7097
end
7198
end
7299
end

spec/lib/pact_broker/pacts/pact_params_spec.rb

+33-8
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,44 @@ module Pacts
99
let(:consumer_version_number) { '1.2.3' }
1010
let(:headers) { { 'X-Pact-Consumer-Version' => consumer_version_number, 'Host' => 'example.org' } }
1111
let(:revision_number) { '1' }
12+
let(:path_info) do
13+
{
14+
consumer_name: 'Consumer',
15+
provider_name: 'Provider',
16+
consumer_version_number: '1.2.3',
17+
revision_number: revision_number,
18+
pact_version_sha: '123'
19+
}
20+
end
21+
22+
describe "from_path_info" do
23+
subject { PactParams.from_path_info(path_info) }
24+
25+
it "extracts the consumer name from the path" do
26+
expect(subject.consumer_name).to eq "Consumer"
27+
end
28+
29+
it "extracts the provider name from the path" do
30+
expect(subject.provider_name).to eq "Provider"
31+
end
32+
33+
it "extracts the consumer_version_number from the path" do
34+
expect(subject.consumer_version_number).to eq "1.2.3"
35+
end
36+
37+
it "extracts the revision_number from the path" do
38+
expect(subject.revision_number).to eq "1"
39+
end
40+
41+
it "extracts the pact_version_sha from the path" do
42+
expect(subject.pact_version_sha).to eq "123"
43+
end
44+
end
1245

1346
describe "from_request" do
1447

1548
context "from a PUT request" do
1649
let(:request) { Webmachine::Request.new("PUT", "/", headers, body)}
17-
let(:path_info) do
18-
{
19-
consumer_name: 'Consumer',
20-
provider_name: 'Provider',
21-
consumer_version_number: '1.2.3',
22-
revision_number: revision_number
23-
}
24-
end
2550

2651
subject { PactParams.from_request(request, path_info) }
2752

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'pact_broker/pacts/sort_verifiable_content'
2+
3+
module PactBroker
4+
module Pacts
5+
describe SortVerifiableContent do
6+
let(:pact_content_1) do
7+
{
8+
a: 1,
9+
interactions: [{ a: 1, b: 2 }, { a: 2, b: 3 }]
10+
}.to_json
11+
end
12+
13+
let(:pact_content_2) do
14+
{
15+
interactions: [{ b: 3, a: 2}, { b: 2, a: 1 }],
16+
a: 1
17+
}.to_json
18+
end
19+
20+
it "sorts the interactions/messages and keys in a deterministic way" do
21+
expect(SortVerifiableContent.call(pact_content_1).to_json).to eq(SortVerifiableContent.call(pact_content_2).to_json)
22+
end
23+
end
24+
end
25+
end

0 commit comments

Comments
 (0)