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
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