pact_broker 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +5 -13
  2. data/.travis.yml +6 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +3 -1
  5. data/lib/pact_broker/api/contracts/consumer_version_number_validation.rb +32 -0
  6. data/lib/pact_broker/api/contracts/pacticipant_name_contract.rb +27 -0
  7. data/lib/pact_broker/api/contracts/pacticipant_name_validation.rb +30 -0
  8. data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +55 -0
  9. data/lib/pact_broker/api/contracts/request_validations.rb +40 -0
  10. data/lib/pact_broker/api/contracts/webhook_contract.rb +32 -0
  11. data/lib/pact_broker/api/pact_broker_urls.rb +7 -0
  12. data/lib/pact_broker/api/resources/base_resource.rb +9 -0
  13. data/lib/pact_broker/api/resources/pact.rb +10 -3
  14. data/lib/pact_broker/api/resources/pact_webhooks.rb +9 -0
  15. data/lib/pact_broker/constants.rb +5 -0
  16. data/lib/pact_broker/doc/views/pacticipants.markdown +2 -2
  17. data/lib/pact_broker/locale/en.yml +6 -0
  18. data/lib/pact_broker/messages.rb +4 -0
  19. data/lib/pact_broker/models/webhook.rb +10 -8
  20. data/lib/pact_broker/models/webhook_request.rb +5 -17
  21. data/lib/pact_broker/pacts/pact_params.rb +79 -0
  22. data/lib/pact_broker/services/webhook_service.rb +6 -0
  23. data/lib/pact_broker/version.rb +1 -1
  24. data/pact_broker.gemspec +4 -3
  25. data/spec/fixtures/webhook_valid.json +14 -0
  26. data/spec/integration/endpoints/pact_put_spec.rb +16 -0
  27. data/spec/integration/endpoints/pact_webhooks_spec.rb +96 -0
  28. data/spec/lib/pact_broker/api/contracts/put_pact_params_contract_spec.rb +122 -0
  29. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +106 -0
  30. data/spec/lib/pact_broker/api/resources/pact_spec.rb +15 -1
  31. data/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb +11 -8
  32. data/spec/lib/pact_broker/models/webhook_request_spec.rb +0 -31
  33. data/spec/lib/pact_broker/models/webhook_spec.rb +0 -21
  34. data/spec/lib/pact_broker/pacts/pact_params_spec.rb +69 -0
  35. data/spec/support/shared_examples_for_responses.rb +14 -0
  36. metadata +88 -55
@@ -2,6 +2,7 @@ require 'pact_broker/models/webhook_request_header'
2
2
  require 'pact_broker/models/webhook_execution_result'
3
3
  require 'pact_broker/logging'
4
4
  require 'pact_broker/messages'
5
+ require 'net/http'
5
6
 
6
7
  module PactBroker
7
8
 
@@ -23,6 +24,10 @@ module PactBroker
23
24
 
24
25
  attr_accessor :method, :url, :headers, :body, :username, :password
25
26
 
27
+ # Reform gets confused by the :method method, as :method is a standard
28
+ # Ruby method.
29
+ alias_method :http_method, :method
30
+
26
31
  def initialize attributes = {}
27
32
  @method = attributes[:method]
28
33
  @url = attributes[:url]
@@ -70,15 +75,6 @@ module PactBroker
70
75
 
71
76
  end
72
77
 
73
- def validate
74
- messages = []
75
- messages << message('errors.validation.attribute_missing', attribute: 'method') unless method
76
- messages << message('errors.validation.attribute_missing', attribute: 'url') unless url
77
- messages << message('errors.validation.invalid_http_method', method: method) unless method && method_valid?
78
- messages << message('errors.validation.invalid_url', url: url) unless url && url_valid?
79
- messages
80
- end
81
-
82
78
  private
83
79
 
84
80
  def to_s
@@ -89,14 +85,6 @@ module PactBroker
89
85
  Net::HTTP.const_get(method.capitalize).new(url)
90
86
  end
91
87
 
92
- def method_valid?
93
- Net::HTTP.const_defined?(method.capitalize)
94
- end
95
-
96
- def url_valid?
97
- uri.scheme && uri.host
98
- end
99
-
100
88
  def uri
101
89
  URI(url)
102
90
  end
@@ -0,0 +1,79 @@
1
+ require 'pact_broker/json'
2
+ require 'pact_broker/constants'
3
+ require 'ostruct'
4
+
5
+ module PactBroker
6
+ module Pacts
7
+ class PactParams < Hash
8
+
9
+ def initialize attributes
10
+ merge!(attributes)
11
+ end
12
+
13
+ def self.from_request request, path_info
14
+ json_content = request.body.to_s
15
+ pact_hash = begin
16
+ JSON.parse(json_content, PACT_PARSING_OPTIONS)
17
+ rescue
18
+ {}
19
+ end
20
+
21
+ new(
22
+ consumer_name: path_info.fetch(:consumer_name),
23
+ provider_name: path_info.fetch(:provider_name),
24
+ consumer_version_number: path_info.fetch(:consumer_version_number),
25
+ consumer_name_in_pact: pact_hash.fetch('consumer',{})['name'],
26
+ provider_name_in_pact: pact_hash.fetch('provider',{})['name'],
27
+ json_content: json_content
28
+ )
29
+ end
30
+
31
+ def pacticipant_names
32
+ [consumer_name, provider_name]
33
+ end
34
+
35
+ def consumer_name
36
+ self[:consumer_name]
37
+ end
38
+
39
+ def provider_name
40
+ self[:provider_name]
41
+ end
42
+
43
+ def consumer_version_number
44
+ self[:consumer_version_number]
45
+ end
46
+
47
+ def json_content
48
+ self[:json_content]
49
+ end
50
+
51
+ def consumer_name_in_pact
52
+ self[:consumer_name_in_pact]
53
+ end
54
+
55
+ def provider_name_in_pact
56
+ self[:provider_name_in_pact]
57
+ end
58
+
59
+ def consumer
60
+ PacticipantName.new(consumer_name, consumer_name_in_pact, 'consumer')
61
+ end
62
+
63
+ def provider
64
+ PacticipantName.new(provider_name, provider_name_in_pact, 'provider')
65
+ end
66
+
67
+ class PacticipantName < Struct.new(:name, :name_in_pact, :pacticipant)
68
+ def message_args
69
+ {
70
+ name: name,
71
+ name_in_pact: name_in_pact,
72
+ pacticipant: pacticipant
73
+ }
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -14,6 +14,12 @@ module PactBroker
14
14
  SecureRandom.urlsafe_base64
15
15
  end
16
16
 
17
+ def self.errors webhook
18
+ contract = PactBroker::Api::Contracts::WebhookContract.new(webhook)
19
+ contract.validate
20
+ contract.errors
21
+ end
22
+
17
23
  def self.create uuid, webhook, consumer, provider
18
24
  webhook_repository.create uuid, webhook, consumer, provider
19
25
  end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '1.3.0'
2
+ VERSION = '1.3.1'
3
3
  end
data/pact_broker.gemspec CHANGED
@@ -22,10 +22,11 @@ Gem::Specification.new do |gem|
22
22
  #gem.add_runtime_dependency 'pact'
23
23
  gem.add_runtime_dependency 'httparty'
24
24
  gem.add_runtime_dependency 'json'
25
- gem.add_runtime_dependency 'roar'
25
+ gem.add_runtime_dependency 'roar', '~> 0.12.9'
26
+ gem.add_runtime_dependency 'reform', '~> 1.0'
26
27
  gem.add_runtime_dependency 'sequel', '~> 4.12'
27
- gem.add_runtime_dependency 'webmachine'
28
- gem.add_runtime_dependency 'versionomy'
28
+ gem.add_runtime_dependency 'webmachine', '~> 1.2'
29
+ gem.add_runtime_dependency 'versionomy', '~> 0.4'
29
30
  gem.add_runtime_dependency 'rack'
30
31
  gem.add_runtime_dependency 'redcarpet', '~>3.1'
31
32
  gem.add_runtime_dependency 'pact', '~>1.3', '>=1.3.2'
@@ -0,0 +1,14 @@
1
+ {
2
+ "request": {
3
+ "method": "POST",
4
+ "url": "http://some.url",
5
+ "username": "username",
6
+ "password": "password",
7
+ "headers": {
8
+ "Accept": "application/json"
9
+ },
10
+ "body": {
11
+ "a" : "body"
12
+ }
13
+ }
14
+ }
@@ -39,5 +39,21 @@ describe "pacts/provider/:provider/consumer/:consumer/version/:version" do
39
39
  expect(response_body_json).to include JSON.parse(pact_content)
40
40
  end
41
41
  end
42
+
43
+ context "when the pacticipant names in the path do not match those in the pact" do
44
+ let(:path) { "/pacts/provider/Another%20Provider/consumer/A%20Consumer/version/1.2.3" }
45
+
46
+ it "returns a json error response" do
47
+ expect(subject).to be_a_json_error_response "does not match"
48
+ end
49
+ end
50
+
51
+ context "when the pact is another type of CDC that doesn't have the Consumer or Provider names in the expected places" do
52
+ let(:pact_content) { {}.to_json }
53
+
54
+ it "accepts the un-pact Pact" do
55
+ expect(subject.status).to be 201
56
+ end
57
+ end
42
58
  end
43
59
  end
@@ -0,0 +1,96 @@
1
+
2
+ require 'support/provider_state_builder'
3
+
4
+ module PactBroker::Api
5
+
6
+ module Resources
7
+
8
+ describe PactWebhooks do
9
+
10
+ before do
11
+ ProviderStateBuilder.new.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
12
+ end
13
+
14
+
15
+ let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" }
16
+ let(:headers) { {'CONTENT_TYPE' => 'application/json'} }
17
+
18
+ describe "POST" do
19
+
20
+ subject { post path, webhook_json, headers }
21
+
22
+ context "with invalid attributes" do
23
+
24
+ let(:errors) { ["Request can't be blank"] }
25
+
26
+ let(:webhook_json) do
27
+ {
28
+
29
+ }.to_json
30
+ end
31
+
32
+ it "returns a 400" do
33
+ subject
34
+ expect(last_response.status).to be 400
35
+ end
36
+
37
+ it "returns a JSON content type" do
38
+ subject
39
+ expect(last_response.headers['Content-Type']).to eq 'application/json'
40
+ end
41
+
42
+ it "returns the validation errors" do
43
+ subject
44
+ expect(JSON.parse(last_response.body, symbolize_names: true)).to eq errors: errors
45
+ end
46
+
47
+ end
48
+
49
+ xcontext "with valid attributes" do
50
+
51
+ let(:webhook_response_json) { {some: 'webhook'}.to_json }
52
+ let(:decorator) { instance_double(Decorators::WebhookDecorator) }
53
+
54
+ before do
55
+ allow_any_instance_of(Decorators::WebhookDecorator).to receive(:to_json).and_return(webhook_response_json)
56
+ end
57
+
58
+ it "saves the webhook" do
59
+ expect(PactBroker::Services::WebhookService).to receive(:create).with(next_uuid, webhook, consumer, provider)
60
+ subject
61
+ end
62
+
63
+ it "returns a 201 response" do
64
+ subject
65
+ expect(last_response.status).to be 201
66
+ end
67
+
68
+ it "returns the Location header" do
69
+ subject
70
+ expect(last_response.headers['Location']).to include(next_uuid)
71
+ end
72
+
73
+ it "returns a JSON content type" do
74
+ subject
75
+ expect(last_response.headers['Content-Type']).to eq 'application/hal+json'
76
+ end
77
+
78
+ it "generates the JSON response body" do
79
+ allow(Decorators::WebhookDecorator).to receive(:new).and_call_original #Deserialise
80
+ expect(Decorators::WebhookDecorator).to receive(:new).with(saved_webhook).and_return(decorator) #Serialize
81
+ expect(decorator).to receive(:to_json).with(base_url: 'http://example.org')
82
+ subject
83
+ end
84
+
85
+ it "returns the JSON representation of the webhook" do
86
+ subject
87
+ expect(last_response.body).to eq webhook_response_json
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/api/contracts/put_pact_params_contract'
3
+
4
+ module PactBroker
5
+ module Api
6
+ module Contracts
7
+
8
+ describe PutPactParamsContract do
9
+
10
+ let(:json_content) { {'some' => 'json' }.to_json }
11
+ let(:pact_params) { Pacts::PactParams.new(attributes) }
12
+
13
+ let(:valid_attributes) do
14
+ {
15
+ consumer_name: "consumer",
16
+ provider_name: "provider",
17
+ consumer_version_number: '1.2.3',
18
+ json_content: json_content,
19
+ consumer_name_in_pact: "consumer",
20
+ provider_name_in_pact: "provider"
21
+ }
22
+ end
23
+
24
+ subject { PutPactParamsContract.new(pact_params) }
25
+
26
+ describe "errors" do
27
+
28
+ let(:attributes) { valid_attributes }
29
+
30
+ before do
31
+ subject.validate
32
+ end
33
+
34
+ context "with valid params" do
35
+
36
+ it "is empty" do
37
+ expect(subject.errors.any?).to be false
38
+ end
39
+ end
40
+
41
+ context "with a nil consumer version number" do
42
+ let(:attributes) do
43
+ valid_attributes.merge(consumer_version_number: nil)
44
+ end
45
+
46
+ it "returns an error" do
47
+ expect(subject.errors.full_messages).to include "Consumer version number can't be blank"
48
+ end
49
+ end
50
+
51
+ context "with an empty consumer version number" do
52
+ let(:attributes) do
53
+ valid_attributes.merge(consumer_version_number: '')
54
+ end
55
+
56
+ it "returns an error" do
57
+ expect(subject.errors.full_messages).to include "Consumer version number can't be blank"
58
+ end
59
+ end
60
+
61
+ context "with an invalid version number" do
62
+ let(:attributes) { {consumer_version_number: 'blah'} }
63
+
64
+ it "returns an error" do
65
+ expect(subject.errors[:base]).to include "Consumer version number 'blah' is not recognised as a standard semantic version. eg. 1.3.0 or 2.0.4.rc1"
66
+ end
67
+ end
68
+
69
+ context "with a consumer name in the pact that does not match the consumer name in the path" do
70
+
71
+ let(:attributes) do
72
+ valid_attributes.merge(consumer_name: "another consumer")
73
+ end
74
+
75
+ it "returns an error" do
76
+ expect(subject.errors.full_messages).to include "Consumer name in pact ('consumer') does not match consumer name in path ('another consumer')."
77
+ end
78
+ end
79
+
80
+ context "with a provider name in the pact that does not match the provider name in the path" do
81
+
82
+ let(:attributes) do
83
+ valid_attributes.merge(provider_name: "another provider")
84
+ end
85
+
86
+ it "returns an error" do
87
+ expect(subject.errors.full_messages).to include "Provider name in pact ('provider') does not match provider name in path ('another provider')."
88
+ end
89
+ end
90
+
91
+ context "when the consumer name in the pact is not present" do
92
+
93
+ let(:attributes) do
94
+ valid_attributes.tap do | atts |
95
+ atts.delete(:consumer_name_in_pact)
96
+ end
97
+ end
98
+
99
+ it "returns no error because I don't want to stop a different CDC from being published" do
100
+ expect(subject.errors.any?).to be false
101
+ end
102
+ end
103
+
104
+ context "when the provider name in the pact is not present" do
105
+
106
+ let(:attributes) do
107
+ valid_attributes.tap do | atts |
108
+ atts.delete(:provider_name_in_pact)
109
+ end
110
+ end
111
+
112
+ it "returns no error because I don't want to stop a different CDC from being published" do
113
+ expect(subject.errors.any?).to be false
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/api/contracts/webhook_contract'
3
+ require 'pact_broker/api/decorators/webhook_decorator'
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Contracts
8
+ describe WebhookContract do
9
+
10
+ let(:json) { load_fixture 'webhook_valid.json' }
11
+ let(:webhook) { PactBroker::Api::Decorators::WebhookDecorator.new(Models::Webhook.new).from_json(json) }
12
+ let(:subject) { WebhookContract.new(webhook) }
13
+
14
+ def valid_webhook_with
15
+ hash = load_json_fixture 'webhook_valid.json'
16
+ yield hash
17
+ hash.to_json
18
+ end
19
+
20
+ describe "errors" do
21
+
22
+ before do
23
+ subject.validate
24
+ end
25
+
26
+ context "with valid fields" do
27
+ it "is empty" do
28
+ expect(subject.errors.any?).to be false
29
+ end
30
+ end
31
+
32
+ context "with no request defined" do
33
+
34
+ let(:json) { {}.to_json }
35
+
36
+ it "contains an error for missing request" do
37
+ subject.validate
38
+ expect(subject.errors[:request]).to eq ["can't be blank"]
39
+ end
40
+ end
41
+
42
+ context "with no method" do
43
+ let(:json) do
44
+ valid_webhook_with do |hash|
45
+ hash['request'].delete('method')
46
+ end
47
+ end
48
+
49
+ it "contains an error for missing method" do
50
+ expect(subject.errors[:"request.http_method"]).to eq ["can't be blank"]
51
+ end
52
+ end
53
+
54
+ context "with an invalid method" do
55
+ let(:json) do
56
+ valid_webhook_with do |hash|
57
+ hash['request']['method'] = 'blah'
58
+ end
59
+ end
60
+
61
+ it "contains an error for invalid method" do
62
+ expect(subject.errors[:"request.method"]).to eq ["is not a recognised HTTP method"]
63
+ end
64
+ end
65
+
66
+ context "with no URL" do
67
+ let(:json) do
68
+ valid_webhook_with do |hash|
69
+ hash['request'].delete('url')
70
+ end
71
+ end
72
+
73
+ it "contains an error for missing URL" do
74
+ expect(subject.errors[:"request.url"]).to eq ["can't be blank"]
75
+ end
76
+ end
77
+
78
+ context "with an invalid URL" do
79
+ let(:json) do
80
+ valid_webhook_with do |hash|
81
+ hash['request']['url'] = 'bl ah'
82
+ end
83
+ end
84
+
85
+ it "contains an error for invalid URL" do
86
+ expect(subject.errors[:"request.url"]).to eq ["is not a valid URL eg. http://example.org"]
87
+ end
88
+ end
89
+
90
+ context "with an URL missing a scheme" do
91
+ let(:json) do
92
+ valid_webhook_with do |hash|
93
+ hash['request']['url'] = 'blah'
94
+ end
95
+ end
96
+
97
+ it "contains an error for invalid URL" do
98
+ expect(subject.errors[:"request.url"]).to eq ["is not a valid URL eg. http://example.org"]
99
+ end
100
+ end
101
+ end
102
+
103
+ end
104
+ end
105
+ end
106
+ end