pact_broker 1.9.3 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1822a1c9b39785852ced69a22fd88a124a894e06
4
- data.tar.gz: 17276a5d6ea111fd01b5766d3246154a59ab1a60
3
+ metadata.gz: df97d13a663699e7a3eb4d7f58cc7abe8a1012d2
4
+ data.tar.gz: 319a6de81120c2bc7d627fbc2255caf51da2e742
5
5
  SHA512:
6
- metadata.gz: a74a5d53bc9b238c522190b2b6039b66c2af41ab2f8a7d900c2e12ea9e008788223d852285bf7e71c381f333679c047f48dcfbc59f1295cccd5326903fda85a3
7
- data.tar.gz: a45265e6cafa287a32fc6f31b257d4efc63bcfcf907cc9cb7808f1c35af11d342aa5caf3ff1ad766c7155af390b92e921f974b80dd53151ebcd96491213e3c4f
6
+ metadata.gz: 0130cac31b2abaebf71905c841d136a0e6d7fcf4e00186c3e33afb2fda2a8ce4fddceb5fcfe4cd5de9b0650bf6eb8d5cec1f33c422bf17af36292f01665e253b
7
+ data.tar.gz: 3a7f9c3663de5c1b1143608727321f97a40af7737d3868034e9bff1b2cfbb86ec131731667963f173484057bd118c6b84f32ca41fa22101f8f2542aaa34e1e56
data/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@ Do this to generate your change history
2
2
 
3
3
  $ git log --pretty=format:' * %h - %s (%an, %ad)' vX.Y.Z..HEAD
4
4
 
5
+ #### 1.10.0 (2016-08-01)
6
+ * efdde13 - Add ability to merge pacts via PATCH requests (Steve Pletcher, Thu Jul 28 16:29:22 2016 -0400)
7
+
5
8
  #### 1.9.3 (2016-06-27)
6
9
  * f57db36 - Clarify that pact_broker will only work with ruby >= 2.0 (Sergei Matheson, Mon Jun 27 11:06:40 2016 +1000)
7
10
  * a1742b8 - Correct release instructions (Sergei Matheson, Mon Jun 27 11:03:03 2016 +1000)
data/example/Gemfile CHANGED
@@ -1,5 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem "pact_broker", "~>1.4"
3
+ gem "pact_broker", "~>1.9.3"
4
4
  gem "sqlite3" # Replace with your choice of database driver eg. gem "pg"
5
- gem "thin" # Keep, or replace with your choice of web server
5
+ gem "thin" # Keep, or replace with your choice of web server
@@ -6,6 +6,14 @@ require 'pact_broker/json'
6
6
  require 'pact_broker/pacts/pact_params'
7
7
  require 'pact_broker/api/contracts/put_pact_params_contract'
8
8
 
9
+ module Webmachine
10
+ class Request
11
+ def patch?
12
+ method == "PATCH"
13
+ end
14
+ end
15
+ end
16
+
9
17
  module PactBroker
10
18
 
11
19
  module Api
@@ -24,7 +32,11 @@ module PactBroker
24
32
  end
25
33
 
26
34
  def allowed_methods
27
- ["GET", "PUT", "DELETE"]
35
+ ["GET", "PUT", "DELETE", "PATCH"]
36
+ end
37
+
38
+ def known_methods
39
+ super + ['PATCH']
28
40
  end
29
41
 
30
42
  def is_conflict?
@@ -32,8 +44,8 @@ module PactBroker
32
44
  end
33
45
 
34
46
  def malformed_request?
35
- if request.put?
36
- return invalid_json? ||
47
+ if request.patch? || request.put?
48
+ invalid_json? ||
37
49
  contract_validation_errors?(Contracts::PutPactParamsContract.new(pact_params))
38
50
  else
39
51
  false
@@ -41,12 +53,18 @@ module PactBroker
41
53
  end
42
54
 
43
55
  def resource_exists?
44
- pact
56
+ !!pact
45
57
  end
46
58
 
47
59
  def from_json
48
60
  response_code = pact ? 200 : 201
49
- @pact = pact_service.create_or_update_pact(pact_params)
61
+
62
+ if request.patch? && resource_exists?
63
+ @pact = pact_service.merge_pact(pact_params)
64
+ else
65
+ @pact = pact_service.create_or_update_pact(pact_params)
66
+ end
67
+
50
68
  response.body = to_json
51
69
  response_code
52
70
  end
@@ -73,4 +91,4 @@ module PactBroker
73
91
  end
74
92
  end
75
93
  end
76
- end
94
+ end
@@ -67,4 +67,4 @@ module PactBroker
67
67
  end
68
68
 
69
69
  end
70
- end
70
+ end
@@ -0,0 +1,53 @@
1
+ require 'json'
2
+
3
+ module PactBroker
4
+ module Pacts
5
+ module Merger
6
+
7
+ extend self
8
+
9
+ # Accepts two hashes representing pacts, outputs a merged hash
10
+ # Does not make any guarantees about order of interactions
11
+ def merge_pacts original_json, additional_json
12
+ original, additional = [original_json, additional_json].map{|str| JSON.parse(str, PACT_PARSING_OPTIONS) }
13
+
14
+ new_pact = original
15
+
16
+ additional["interactions"].each do |new_interaction|
17
+ # check to see if this interaction matches an existing interaction
18
+ overwrite_index = original["interactions"].find_index do |original_interaction|
19
+ matching_request?(original_interaction, new_interaction)
20
+ end
21
+
22
+ # overwrite existing interaction if a match is found, otherwise appends the new interaction
23
+ if overwrite_index
24
+ new_pact["interactions"][overwrite_index] = new_interaction
25
+ else
26
+ new_pact["interactions"] << new_interaction
27
+ end
28
+ end
29
+
30
+ new_pact.to_json
31
+ end
32
+
33
+ private
34
+
35
+ def matching_request? original_interaction, new_interaction
36
+ same_description_and_state?(original_interaction, new_interaction) &&
37
+ same_request_properties?(original_interaction["request"], new_interaction["request"])
38
+ end
39
+
40
+ def same_description_and_state? original, additional
41
+ original["description"] == additional["description"] &&
42
+ original["provider_state"] == additional["provider_state"]
43
+ end
44
+
45
+ def same_request_properties? original, additional
46
+ method_matches = original["method"] == additional["method"]
47
+ path_matches = original["path"] == additional["path"]
48
+
49
+ method_matches && path_matches && original["headers"] == additional["headers"]
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,6 +1,7 @@
1
1
  require 'pact_broker/repositories'
2
2
  require 'pact_broker/services'
3
3
  require 'pact_broker/logging'
4
+ require 'pact_broker/pacts/merger'
4
5
 
5
6
  module PactBroker
6
7
  module Pacts
@@ -42,6 +43,17 @@ module PactBroker
42
43
  end
43
44
  end
44
45
 
46
+ def merge_pact params
47
+ provider = pacticipant_repository.find_by_name_or_create params[:provider_name]
48
+ consumer = pacticipant_repository.find_by_name_or_create params[:consumer_name]
49
+ consumer_version = version_repository.find_by_pacticipant_id_and_number_or_create consumer.id, params[:consumer_version_number]
50
+ existing_pact = pact_repository.find_by_version_and_provider(consumer_version.id, provider.id)
51
+
52
+ params.merge!(json_content: Merger.merge_pacts(params[:json_content], existing_pact.json_content))
53
+
54
+ update_pact params, existing_pact
55
+ end
56
+
45
57
  def find_all_pact_versions_between consumer, options
46
58
  pact_repository.find_all_pact_versions_between consumer, options
47
59
  end
@@ -102,7 +114,6 @@ module PactBroker
102
114
  webhook_service.execute_webhooks pact
103
115
  end
104
116
  end
105
-
106
117
  end
107
118
  end
108
- end
119
+ end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '1.9.3'
2
+ VERSION = '1.10.0'
3
3
  end
@@ -0,0 +1,55 @@
1
+ require 'spec/support/provider_state_builder'
2
+
3
+ describe "Merging a pact" do
4
+
5
+ let(:pact_content) { load_fixture('a_consumer-a_provider.json') }
6
+ let(:path) { "/pacts/provider/A%20Provider/consumer/A%20Consumer/version/1.2.3" }
7
+ let(:response_body_json) { JSON.parse(subject.body) }
8
+
9
+ subject do
10
+ patch path, pact_content, {'CONTENT_TYPE' => 'application/json' }; last_response
11
+ end
12
+
13
+ context "when a pact for this consumer version does not exist" do
14
+ it "returns a 201 Created" do
15
+ expect(subject.status).to be 201
16
+ end
17
+
18
+ it "returns a json body" do
19
+ expect(subject.headers['Content-Type']).to eq "application/json;charset=utf-8"
20
+ end
21
+
22
+ it "returns the pact in the body" do
23
+ expect(response_body_json).to include JSON.parse(pact_content)
24
+ end
25
+ end
26
+
27
+ context "when a pact for this consumer version does exist" do
28
+ let(:existing_pact_content) { load_fixture('a_consumer-a_provider-2.json') }
29
+ let(:merged_pact_content) { load_fixture('a_consumer-a_provider-merged.json') }
30
+
31
+ before do
32
+ ProviderStateBuilder.new.create_pact_with_hierarchy "A Consumer", "1.2.3", "A Provider", existing_pact_content
33
+ end
34
+
35
+ it "returns a 200 Success" do
36
+ expect(subject.status).to be 200
37
+ end
38
+
39
+ it "returns an application/json Content-Type" do
40
+ expect(subject.headers['Content-Type']).to eq "application/json;charset=utf-8"
41
+ end
42
+
43
+ it "returns the merged pact in the response body" do
44
+ expect(response_body_json).to contain_hash JSON.parse(merged_pact_content)
45
+ end
46
+ end
47
+
48
+ context "when the pacticipant names in the path do not match those in the pact" do
49
+ let(:path) { "/pacts/provider/Another%20Provider/consumer/A%20Consumer/version/1.2.3" }
50
+
51
+ it "returns a json error response" do
52
+ expect(subject).to be_a_json_error_response "does not match"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,34 @@
1
+ {
2
+ "consumer": {
3
+ "name": "A Consumer"
4
+ },
5
+ "provider": {
6
+ "name": "A Provider"
7
+ },
8
+ "interactions": [
9
+ {
10
+ "description" : "a request for something",
11
+ "provider_state": null,
12
+ "request": {
13
+ "method": "post",
14
+ "path" : "/something"
15
+ },
16
+ "response": {
17
+ "status": 200,
18
+ "body" : "something"
19
+ }
20
+ },
21
+ {
22
+ "description" : "a request for something",
23
+ "provider_state": null,
24
+ "request": {
25
+ "method": "get",
26
+ "path" : "/something"
27
+ },
28
+ "response": {
29
+ "status": 200,
30
+ "body" : "something"
31
+ }
32
+ }
33
+ ]
34
+ }
@@ -14,9 +14,8 @@ module PactBroker::Api
14
14
  let(:app) { PactBroker::API }
15
15
  let(:json) { {some: 'json'}.to_json }
16
16
 
17
- describe "PUT" do
18
-
19
- subject { put "/pacts/provider/Provider/consumer/Consumer/version/1.2", json, {'CONTENT_TYPE' => "application/json"} ; last_response }
17
+ shared_examples "an update endpoint" do |http_method|
18
+ subject { self.send http_method, "/pacts/provider/Provider/consumer/Consumer/version/1.2", json, {'CONTENT_TYPE' => "application/json"} ; last_response }
20
19
 
21
20
  let(:response) { subject; last_response }
22
21
 
@@ -76,7 +75,14 @@ module PactBroker::Api
76
75
  expect(response.body).to eq "message1\nmessage2"
77
76
  end
78
77
  end
78
+ end
79
+
80
+ describe "PUT" do
81
+ it_behaves_like "an update endpoint", :put
82
+ end
79
83
 
84
+ describe "PATCH" do
85
+ it_behaves_like "an update endpoint", :patch
80
86
  end
81
87
 
82
88
  describe "DELETE" do
@@ -0,0 +1,114 @@
1
+ require 'spec_helper'
2
+ require 'spec/support/provider_state_builder'
3
+ require 'pact_broker/pacts/merger'
4
+ require 'json'
5
+
6
+ module PactBroker
7
+ module Pacts
8
+ describe Merger do
9
+ let(:example_pact) { load_json_fixture('consumer-provider.json') }
10
+ let(:example_interaction) do
11
+ {
12
+ "description" => "some description",
13
+ "provider_state" => nil,
14
+ "request" => {
15
+ "method" => "get",
16
+ "path" => "/cash_money",
17
+ "headers" => {
18
+ "Content-Type" => "application/json"
19
+ }
20
+ },
21
+ "response" => {
22
+ "status" => 200,
23
+ "body" => "$$$$$$$$",
24
+ "headers" => {
25
+ "Content-Type" => "application/json"
26
+ }
27
+ }
28
+ }
29
+ end
30
+
31
+ before :each do
32
+ @pact_to_merge = load_json_fixture('consumer-provider.json')
33
+ end
34
+
35
+ it "merges two pacts" do
36
+ @pact_to_merge["interactions"] << example_interaction
37
+ result = merge_pacts(example_pact, @pact_to_merge)
38
+ expect(result["interactions"]).to match_array(example_pact["interactions"].push(example_interaction))
39
+ end
40
+
41
+ it "is idempotent" do
42
+ @pact_to_merge["interactions"] << example_interaction
43
+ first_result = merge_pacts(example_pact, @pact_to_merge)
44
+ second_result = merge_pacts(first_result, @pact_to_merge)
45
+ expect(first_result).to contain_hash second_result
46
+ end
47
+
48
+ it "overwrites identical interactions" do
49
+ @pact_to_merge["interactions"][0]["response"]["body"] = "changed!"
50
+ result = merge_pacts(example_pact, @pact_to_merge)
51
+
52
+ expect(result["interactions"].length).to eq example_pact["interactions"].length
53
+ expect(result["interactions"].first["response"]["body"]).to eq "changed!"
54
+ end
55
+
56
+ it "appends interactions with a different provider state" do
57
+ @pact_to_merge["interactions"][0]["provider_state"] = "upside down"
58
+
59
+ result = merge_pacts(example_pact, @pact_to_merge)
60
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
61
+ end
62
+
63
+ it "appends interactions with a different description" do
64
+ @pact_to_merge["interactions"][0]["description"] = "getting $$$"
65
+
66
+ result = merge_pacts(example_pact, @pact_to_merge)
67
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
68
+ end
69
+
70
+ it "appends interactions with a different request method" do
71
+ @pact_to_merge["interactions"][0]["request"]["method"] = "delete"
72
+
73
+ result = merge_pacts(example_pact, @pact_to_merge)
74
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
75
+ end
76
+
77
+ it "appends interactions with a different request path" do
78
+ @pact_to_merge["interactions"][0]["request"]["path"] = "/decrypt_all_passwords"
79
+
80
+ result = merge_pacts(example_pact, @pact_to_merge)
81
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
82
+ end
83
+
84
+ it "appends interactions which have additional request headers in the new pact" do
85
+ @pact_to_merge["interactions"][0]["request"]["headers"] = { "Accept" => "script/javascript" }
86
+
87
+ result = merge_pacts(example_pact, @pact_to_merge)
88
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
89
+ end
90
+
91
+ it "appends interactions with different request headers" do
92
+ example_pact["interactions"][0]["request"]["headers"] = { "Content-Type" => "script/javascript" }
93
+ @pact_to_merge["interactions"][0]["request"]["headers"] = { "Content-Type" => "ayy/lmao" }
94
+
95
+ result = merge_pacts(example_pact, @pact_to_merge)
96
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
97
+ end
98
+
99
+ it "appends interactions with fewer request headers" do
100
+ example_pact["interactions"][0]["request"]["headers"] = { "Content-Type" => "script/javascript" }
101
+
102
+ result = merge_pacts(example_pact, @pact_to_merge)
103
+ expect(result["interactions"].length).to eq example_pact["interactions"].length + 1
104
+ end
105
+
106
+ # helper that lets these specs deal with hashes instead of JSON strings
107
+ def merge_pacts(a, b, return_hash = true)
108
+ result = PactBroker::Pacts::Merger.merge_pacts(a.to_json, b.to_json)
109
+
110
+ return_hash ? JSON.parse(result) : result
111
+ end
112
+ end
113
+ end
114
+ end
@@ -66,4 +66,4 @@ module PactBroker
66
66
 
67
67
  end
68
68
  end
69
- end
69
+ end
@@ -0,0 +1,25 @@
1
+ # checks if actual contains all the key-value pairs that expected does,
2
+ # ignoring order for any child arrays
3
+ RSpec::Matchers.define :contain_hash do |expected|
4
+ match do |actual|
5
+ contains_hash?(expected, actual)
6
+ end
7
+ end
8
+
9
+
10
+ def contains_hash?(expected, actual)
11
+ expected.all? do |key, value|
12
+ unordered_match(actual[key], value)
13
+ end
14
+ end
15
+
16
+ def unordered_match(expected, actual)
17
+ case
18
+ when [expected, actual].all?{|val| val.is_a? Array }
19
+ expected.all?{|el| actual.include? el }
20
+ when [expected, actual].all?{|val| val.is_a? Hash }
21
+ contains_hash?(expected, actual)
22
+ else
23
+ expected == actual
24
+ end
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.3
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bethany Skurrie
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2016-06-27 00:00:00.000000000 Z
13
+ date: 2016-08-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -498,6 +498,7 @@ files:
498
498
  - lib/pact_broker/pacts/diff.rb
499
499
  - lib/pact_broker/pacts/latest_pacts.rb
500
500
  - lib/pact_broker/pacts/latest_tagged_pacts.rb
501
+ - lib/pact_broker/pacts/merger.rb
501
502
  - lib/pact_broker/pacts/pact_params.rb
502
503
  - lib/pact_broker/pacts/pact_version_content.rb
503
504
  - lib/pact_broker/pacts/repository.rb
@@ -575,10 +576,12 @@ files:
575
576
  - spec/features/get_previous_distinct_version.rb
576
577
  - spec/features/get_provider_pacts.rb
577
578
  - spec/features/get_version.rb
579
+ - spec/features/merge_pact_spec.rb
578
580
  - spec/features/publish_not_a_pact_spec.rb
579
581
  - spec/features/publish_pact_spec.rb
580
582
  - spec/features/update_pacticipant_spec.rb
581
583
  - spec/fixtures/a_consumer-a_provider-2.json
584
+ - spec/fixtures/a_consumer-a_provider-merged.json
582
585
  - spec/fixtures/a_consumer-a_provider.json
583
586
  - spec/fixtures/consumer-provider.json
584
587
  - spec/fixtures/renderer_pact.json
@@ -630,6 +633,7 @@ files:
630
633
  - spec/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names_spec.rb
631
634
  - spec/lib/pact_broker/pacts/create_formatted_diff_spec.rb
632
635
  - spec/lib/pact_broker/pacts/diff_spec.rb
636
+ - spec/lib/pact_broker/pacts/merger_spec.rb
633
637
  - spec/lib/pact_broker/pacts/pact_params_spec.rb
634
638
  - spec/lib/pact_broker/pacts/repository_spec.rb
635
639
  - spec/lib/pact_broker/pacts/service_spec.rb
@@ -653,6 +657,7 @@ files:
653
657
  - spec/support/database_cleaner.rb
654
658
  - spec/support/fixture_helpers.rb
655
659
  - spec/support/provider_state_builder.rb
660
+ - spec/support/rspec_match_hash.rb
656
661
  - spec/support/shared_examples_for_responses.rb
657
662
  - tasks/database.rb
658
663
  - tasks/db.rake
@@ -728,10 +733,12 @@ test_files:
728
733
  - spec/features/get_previous_distinct_version.rb
729
734
  - spec/features/get_provider_pacts.rb
730
735
  - spec/features/get_version.rb
736
+ - spec/features/merge_pact_spec.rb
731
737
  - spec/features/publish_not_a_pact_spec.rb
732
738
  - spec/features/publish_pact_spec.rb
733
739
  - spec/features/update_pacticipant_spec.rb
734
740
  - spec/fixtures/a_consumer-a_provider-2.json
741
+ - spec/fixtures/a_consumer-a_provider-merged.json
735
742
  - spec/fixtures/a_consumer-a_provider.json
736
743
  - spec/fixtures/consumer-provider.json
737
744
  - spec/fixtures/renderer_pact.json
@@ -783,6 +790,7 @@ test_files:
783
790
  - spec/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names_spec.rb
784
791
  - spec/lib/pact_broker/pacts/create_formatted_diff_spec.rb
785
792
  - spec/lib/pact_broker/pacts/diff_spec.rb
793
+ - spec/lib/pact_broker/pacts/merger_spec.rb
786
794
  - spec/lib/pact_broker/pacts/pact_params_spec.rb
787
795
  - spec/lib/pact_broker/pacts/repository_spec.rb
788
796
  - spec/lib/pact_broker/pacts/service_spec.rb
@@ -806,4 +814,5 @@ test_files:
806
814
  - spec/support/database_cleaner.rb
807
815
  - spec/support/fixture_helpers.rb
808
816
  - spec/support/provider_state_builder.rb
817
+ - spec/support/rspec_match_hash.rb
809
818
  - spec/support/shared_examples_for_responses.rb