pact_broker 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YzNkZTQzNTE1OWIzZDNlYmQ0Y2IzODEyZmRkYzhjZWI0YjIwMDIxMg==
5
- data.tar.gz: !binary |-
6
- N2QwYjkzYzk0YWQ1NjFkODVkZjIyOGJiYThhMzEwYzRlNGIxM2RkNw==
2
+ SHA1:
3
+ metadata.gz: c2efe6a0f09d53fcd3eda935e760ffdddf06c39c
4
+ data.tar.gz: 35000029a9b755d9c4f14be9ef900864dcb50a51
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NzlkN2YyYzI4YWM1OTNmNWVmN2RkYTcwMjU1ZWJiNGI2OGE2NDFjYmZmNTY0
10
- MmE3Yzc1NGVmZDA0YmQ2YjJlYjZhODIwODAxOGRhYTgxZGI1MTZmODRlOGY5
11
- ZGYyMDRiNzBkN2MzMmIxOWI4ZTI4MDM1MDIwNmFiNTU5N2I0ZTk=
12
- data.tar.gz: !binary |-
13
- MDNkNzJkNjgwYzI2ZjdiOGM2NTNhZmJiMDBmNWZjNWVjZTVmM2VlYTQyOTI4
14
- NzEwN2JjYzUxYWViMzJiZGI1NzQwYTkyNTJjZTM2NGI1Y2FmNjE4OWVhYTUx
15
- NTA1NDgyYjYxZDQyNDE4NGEyYWY3Yzc5M2VlYWJiYjBjNjRiYmY=
6
+ metadata.gz: f5852a4d2cd9bedd28fc5dff28da6d6464d9936a1cb9e693956364d7c6a0c421b364af5be6d3ff229dd24ac36462b8af530ae01209f433dd971574cd87691e8a
7
+ data.tar.gz: a2224dbd381b2313175752bc002c2d118363c90fdd9a08b54f20d6a868dd1599c334c3574fdeac5fc1c6cf3fff99b46c226695ee4bb7a4d2f6a3e24f29430b57
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.1
6
+ - 2.1.2
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@ Do this to generate your change history
2
2
 
3
3
  $ git log --pretty=format:' * %h - %s (%an, %ad)'
4
4
 
5
+ #### 1.3.1 (2014-10-23)
6
+
7
+ * e61b40e - Added Travis configuration. (Beth, Fri Oct 17 16:32:26 2014 +1100)
8
+ * b320fe4 - Fixed pact publish validation for ruby 1.9.3 (Beth, Fri Oct 17 16:31:41 2014 +1100)
9
+ * b9b4d2b - Added validation to ensure that the participant names in the path match the participant names in the pact. (Beth, Thu Oct 16 20:21:10 2014 +1100)
10
+
5
11
  #### 1.3.0 (2014-10-14)
6
12
 
7
13
  * ed08811 - Converted raw SQL create view statements to Sequel so they will run on Postgres (Beth, Sat Oct 11 22:07:37 2014 +1100)
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  The Pact Broker provides a repository for pacts created using the pact gem. It solves the problem of how to share pacts between consumer and provider projects.
4
4
 
5
+ Travis CI Status: [![Build Status](https://travis-ci.org/bethesque/pact_broker.svg?branch=master)](https://travis-ci.org/bethesque/pact_broker)
6
+
5
7
  The Pact Broker:
6
8
 
7
9
  * Enables pacts to be shared between consumer and provider projects.
@@ -18,7 +20,7 @@ See the [wiki](https://github.com/bethesque/pact_broker/wiki) for documentation.
18
20
 
19
21
  ## Usage
20
22
 
21
- * Create a database using a product that is supported by the Sequel gem (listed on this page http://sequel.jeremyevans.net/rdoc/files/README_rdoc.html). At time of writing, Sequel has adapters for: ADO, Amalgalite, CUBRID, DataObjects, DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLAnywhere, SQLite3, Swift, and TinyTDS.
23
+ * Create a database using a product that is supported by the Sequel gem (listed on this page http://sequel.jeremyevans.net/rdoc/files/README_rdoc.html). At time of writing, Sequel has adapters for: ADO, Amalgalite, CUBRID, DataObjects, DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLAnywhere, SQLite3, Swift, and TinyTDS.
22
24
  * __Note:__ It is recommended to use __PostgreSQL__ as it will support JSON search features that are planned in a future release.
23
25
  * Install ruby 1.9.3 or later
24
26
  * Copy the [example](/example) directory to your workstation.
@@ -0,0 +1,32 @@
1
+ module PactBroker
2
+ module Api
3
+ module Contracts
4
+ module ConsumerVersionNumberValidation
5
+
6
+ include PactBroker::Messages
7
+
8
+ def consumer_version_number_present
9
+ unless consumer_version_number
10
+ errors.add(:base, validation_message('consumer_version_number_missing'))
11
+ end
12
+ end
13
+
14
+ def consumer_version_number_valid
15
+ if consumer_version_number && invalid_consumer_version_number?
16
+ errors.add(:base, consumer_version_number_validation_message)
17
+ end
18
+ end
19
+
20
+ def invalid_consumer_version_number?
21
+ begin
22
+ Versionomy.parse(consumer_version_number)
23
+ false
24
+ rescue Versionomy::Errors::ParseError => e
25
+ true
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,27 @@
1
+ require 'reform'
2
+ require 'reform/contract'
3
+
4
+ module PactBroker
5
+ module Api
6
+ module Contracts
7
+
8
+ class PacticipantNameContract < Reform::Contract
9
+ property :name
10
+ property :name_in_pact
11
+ property :pacticipant
12
+ property :message_args
13
+
14
+
15
+ include PactBroker::Messages
16
+
17
+ def blank? string
18
+ string && string.strip.empty?
19
+ end
20
+
21
+ def empty? string
22
+ string.nil? || blank?(string)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ module PactBroker
2
+ module Api
3
+ module Contracts
4
+ module PacticipantNameValidation
5
+
6
+ include PactBroker::Messages
7
+
8
+ def name_in_pact_present
9
+ unless name_in_pact
10
+ errors.add(:'name', validation_message('pact_missing_pacticipant_name', pacticipant: pacticipant))
11
+ end
12
+ end
13
+
14
+ def name_not_blank
15
+ if blank? name
16
+ errors.add(:'name', validation_message('blank'))
17
+ end
18
+ end
19
+
20
+ def blank? string
21
+ string && string.strip.empty?
22
+ end
23
+
24
+ def empty? string
25
+ string.nil? || blank?(string)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,55 @@
1
+ require 'reform'
2
+ require 'reform/contract'
3
+ require 'versionomy'
4
+ require 'pact_broker/messages'
5
+ require 'pact_broker/constants'
6
+ require 'pact_broker/api/contracts/pacticipant_name_contract'
7
+ require 'pact_broker/api/contracts/consumer_version_number_validation'
8
+
9
+ module PactBroker
10
+ module Api
11
+ module Contracts
12
+
13
+ class PutPacticipantNameContract < PacticipantNameContract
14
+
15
+ validates :name, presence: true, blank: false
16
+ validate :name_in_path_matches_name_in_pact
17
+
18
+ def name_in_path_matches_name_in_pact
19
+ if present?(name) && present?(name_in_pact)
20
+ if name != name_in_pact
21
+ errors.add(:name, validation_message('pacticipant_name_mismatch', message_args))
22
+ end
23
+ end
24
+ end
25
+
26
+ def present? string
27
+ string && !blank?(string)
28
+ end
29
+
30
+ end
31
+
32
+ class PutPactParamsContract < Reform::Contract
33
+
34
+ include PactBroker::Messages
35
+
36
+ property :consumer_version_number
37
+ property :consumer, form: PutPacticipantNameContract
38
+ property :provider, form: PutPacticipantNameContract
39
+
40
+ validates :consumer_version_number, presence: true
41
+ validate :consumer_version_number_valid
42
+
43
+
44
+ include ConsumerVersionNumberValidation
45
+
46
+ def consumer_version_number_validation_message
47
+ validation_message('consumer_version_number_invalid', consumer_version_number: consumer_version_number)
48
+ end
49
+
50
+ end
51
+
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ require 'reform'
2
+ require 'reform/contract'
3
+ require 'net/http'
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Contracts
8
+
9
+ module RequestValidations
10
+ def method_is_valid
11
+ if http_method && !valid_method?
12
+ errors.add(:method, "is not a recognised HTTP method")
13
+ end
14
+ end
15
+
16
+ def valid_method?
17
+ Net::HTTP.const_defined?(http_method.capitalize)
18
+ end
19
+
20
+ def url_is_valid
21
+ if url && !url_valid?
22
+ errors.add(:url, "is not a valid URL eg. http://example.org")
23
+ end
24
+ end
25
+
26
+ def url_valid?
27
+ uri && uri.scheme && uri.host
28
+ end
29
+
30
+ def uri
31
+ begin
32
+ URI(url)
33
+ rescue URI::InvalidURIError
34
+ nil
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ require 'reform'
2
+ require 'reform/contract'
3
+ require 'pact_broker/api/contracts/request_validations'
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Contracts
8
+
9
+ class WebhookContract < Reform::Contract
10
+
11
+ property :request
12
+ validates :request, presence: true
13
+
14
+ property :request do
15
+
16
+ include RequestValidations
17
+
18
+ property :url
19
+ property :http_method
20
+
21
+ validates :url, presence: true
22
+ validates :http_method, presence: true
23
+
24
+ validate :method_is_valid
25
+ validate :url_is_valid
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -29,6 +29,13 @@ module PactBroker
29
29
  "#{pactigration_base_url(base_url, representable_pact)}/version/#{representable_pact.consumer.version.number}"
30
30
  end
31
31
 
32
+ def pact_url_from_params base_url, params
33
+ [ base_url, 'pacts',
34
+ 'provider', url_encode(params[:provider_name]),
35
+ 'consumer', url_encode(params[:consumer_name]),
36
+ 'version', url_encode(params[:consumer_version_number]) ].join('/')
37
+ end
38
+
32
39
  def latest_pact_url base_url, pact
33
40
  "#{pactigration_base_url(base_url, pact)}/latest"
34
41
  end
@@ -36,6 +36,8 @@ module PactBroker
36
36
  end
37
37
  end
38
38
 
39
+ alias_method :path_info, :identifier_from_path
40
+
39
41
  def base_url
40
42
  request.uri.to_s.gsub(/#{request.uri.path}$/,'')
41
43
  end
@@ -99,6 +101,13 @@ module PactBroker
99
101
  end
100
102
  end
101
103
 
104
+ def contract_validation_errors? contract
105
+ if (invalid = !contract.validate)
106
+ set_json_validation_error_messages contract.errors.full_messages
107
+ end
108
+ invalid
109
+ end
110
+
102
111
  end
103
112
  end
104
113
  end
@@ -3,6 +3,8 @@ require 'pact_broker/api/resources/base_resource'
3
3
  require 'pact_broker/api/resources/pacticipant_resource_methods'
4
4
  require 'pact_broker/api/decorators/pact_decorator'
5
5
  require 'pact_broker/json'
6
+ require 'pact_broker/pacts/pact_params'
7
+ require 'pact_broker/api/contracts/put_pact_params_contract'
6
8
 
7
9
  module PactBroker
8
10
 
@@ -28,7 +30,8 @@ module PactBroker
28
30
  def malformed_request?
29
31
  if request.put?
30
32
  return invalid_json? ||
31
- potential_duplicate_pacticipants?([identifier_from_path[:consumer_name], identifier_from_path[:provider_name]])
33
+ contract_validation_errors?(Contracts::PutPactParamsContract.new(pact_params)) ||
34
+ potential_duplicate_pacticipants?(pact_params.pacticipant_names)
32
35
  else
33
36
  false
34
37
  end
@@ -40,7 +43,7 @@ module PactBroker
40
43
 
41
44
  def from_json
42
45
  response_code = pact ? 200 : 201
43
- @pact = pact_service.create_or_update_pact(identifier_from_path.merge(:json_content => request_body))
46
+ @pact = pact_service.create_or_update_pact(pact_params)
44
47
  response.body = to_json
45
48
  response_code
46
49
  end
@@ -50,7 +53,11 @@ module PactBroker
50
53
  end
51
54
 
52
55
  def pact
53
- @pact ||= pact_service.find_pact(identifier_from_path)
56
+ @pact ||= pact_service.find_pact(pact_params)
57
+ end
58
+
59
+ def pact_params
60
+ @pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
54
61
  end
55
62
 
56
63
  end
@@ -2,6 +2,7 @@
2
2
  require 'pact_broker/api/resources/base_resource'
3
3
  require 'pact_broker/api/decorators/webhook_decorator'
4
4
  require 'pact_broker/api/decorators/webhooks_decorator'
5
+ require 'pact_broker/api/contracts/webhook_contract'
5
6
 
6
7
  module PactBroker
7
8
 
@@ -33,6 +34,14 @@ module PactBroker
33
34
  false
34
35
  end
35
36
 
37
+ def validation_errors? webhook
38
+ if (errors = webhook_service.errors(webhook)).any?
39
+ response.headers['Content-Type'] = 'application/json'
40
+ response.body = {errors: errors.full_messages }.to_json
41
+ end
42
+ errors.any?
43
+ end
44
+
36
45
  def create_path
37
46
  webhook_url next_uuid, base_url
38
47
  end
@@ -0,0 +1,5 @@
1
+ module PactBroker
2
+
3
+ CONSUMER_VERSION_HEADER = 'X-Pact-Consumer-Version'.freeze
4
+
5
+ end
@@ -2,9 +2,9 @@
2
2
 
3
3
  "Pacticipant" - a party that participates in a pact (ie. a Consumer or a Provider).
4
4
 
5
- ### Creating participants
5
+ ### Creating pacticipants
6
6
  Participants are created automatically when a pact is published to the pact broker. The name is based on the URL compontents used to publish the pact (ie. /pacts/provider/$PROVIDER\_NAME/consumer/$CONSUMER\_NAME/version/$CONSUMER\_VERSION), not on the contents of the pact, as the Pact Broker is designed to be agnostic of the actual pact format as much as possible.
7
7
 
8
8
 
9
9
  ### Deleting pacticipants
10
- Deleting a pacticipant will delete all associated pacts, versions, tags and webhooks. To delete a pacticipant, send a DELETE request to the relevant pacticipant URL via the HAL browser.
10
+ Deleting a pacticipant will delete all associated pacts, versions, tags and webhooks. To delete a pacticipant, send a DELETE request to the relevant pacticipant URL via the HAL browser.
@@ -2,9 +2,15 @@ en:
2
2
  pact_broker:
3
3
  errors:
4
4
  validation:
5
+ blank: "cannot be blank."
5
6
  attribute_missing: "Missing required attribute '%{attribute}'"
6
7
  invalid_http_method: "Invalid HTTP method '%{method}'"
7
8
  invalid_url: "Invalid URL '%{url}'. Expected format: http://example.org"
9
+ pact_missing_pacticipant_name: "was not found at expected path $.%{pacticipant}.name in the submitted pact file."
10
+ consumer_version_number_missing: "Please specify the consumer version number by setting the X-Pact-Consumer-Version header."
11
+ consumer_version_number_header_invalid: "X-Pact-Consumer-Version '%{consumer_version_number}' is not recognised as a standard semantic version. eg. 1.3.0 or 2.0.4.rc1"
12
+ consumer_version_number_invalid: "Consumer version number '%{consumer_version_number}' is not recognised as a standard semantic version. eg. 1.3.0 or 2.0.4.rc1"
13
+ pacticipant_name_mismatch: "in pact ('%{name_in_pact}') does not match %{pacticipant} name in path ('%{name}')."
8
14
  duplicate_pacticipant: |
9
15
  This is the first time a pact has been published for "%{new_name}".
10
16
  The name "%{new_name}" is very similar to the following existing consumers/providers:
@@ -19,6 +19,10 @@ module PactBroker
19
19
  ::I18n.t(key, options.merge(:scope => :pact_broker))
20
20
  end
21
21
 
22
+ def validation_message key, options = {}
23
+ message('errors.validation.' + key, options)
24
+ end
25
+
22
26
  def potential_duplicate_pacticipant_message new_name, potential_duplicate_pacticipants, base_url
23
27
  existing_names = potential_duplicate_pacticipants.
24
28
  collect{ | p | "* #{p.name}" }.join("\n")
@@ -1,6 +1,7 @@
1
1
  require 'pact_broker/models/webhook_request'
2
2
  require 'pact_broker/messages'
3
3
  require 'pact_broker/logging'
4
+ require 'pact_broker/api/contracts/webhook_contract'
4
5
 
5
6
  module PactBroker
6
7
 
@@ -22,13 +23,6 @@ module PactBroker
22
23
  @updated_at = attributes[:updated_at]
23
24
  end
24
25
 
25
- def validate
26
- messages = []
27
- messages << message('errors.validation.attribute_missing', attribute: 'request') unless request
28
- messages.concat request.validate if request
29
- messages
30
- end
31
-
32
26
  def description
33
27
  "A webhook for the pact between #{consumer.name} and #{provider.name}"
34
28
  end
@@ -44,7 +38,15 @@ module PactBroker
44
38
  end
45
39
 
46
40
  def to_s
47
- "webhook for consumer=#{consumer.name} provider=#{provider.name} uuid=#{uuid} request=#{request}"
41
+ "webhook for consumer=#{consumer_name} provider=#{provider_name} uuid=#{uuid} request=#{request}"
42
+ end
43
+
44
+ def consumer_name
45
+ consumer && consumer.name
46
+ end
47
+
48
+ def provider_name
49
+ provider && provider.name
48
50
  end
49
51
  end
50
52