pact_broker 2.12.0 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -5
  3. data/README.md +13 -8
  4. data/bethtest.rb +21 -94
  5. data/db/migrations/20171112_add_test_results.rb +7 -0
  6. data/db/migrations/20171117_create_webhook_events.rb +14 -0
  7. data/db/migrations/20171118_create_webhook_events.rb +18 -0
  8. data/db/migrations/20180108_create_certificates_table.rb +14 -0
  9. data/db/migrations/20180109_migrate_trigger_type.rb +9 -0
  10. data/db/pact_broker_database.sqlite3 +0 -0
  11. data/db/test/backwards_compatibility/gemfiles/1.18.0.gemfile.lock +5 -5
  12. data/db/test/backwards_compatibility/gemfiles/2.0.0.gemfile.lock +5 -5
  13. data/db/test/backwards_compatibility/gemfiles/2.1.0.gemfile.lock +5 -5
  14. data/db/test/backwards_compatibility/gemfiles/2.2.0.gemfile.lock +12 -14
  15. data/db/test/backwards_compatibility/gemfiles/2.3.0.gemfile.lock +12 -14
  16. data/db/test/backwards_compatibility/gemfiles/2.4.2.gemfile.lock +12 -14
  17. data/db/test/backwards_compatibility/gemfiles/2.5.1.gemfile.lock +12 -14
  18. data/db/test/backwards_compatibility/gemfiles/2.6.0.gemfile.lock +12 -14
  19. data/db/test/backwards_compatibility/gemfiles/head.gemfile.lock +16 -15
  20. data/db/test/change_migration_strategy/before/Gemfile +1 -0
  21. data/example/README.md +65 -0
  22. data/example/config.ru +1 -0
  23. data/example/example_data.sql +17 -0
  24. data/lib/pact_broker/api.rb +1 -1
  25. data/lib/pact_broker/api/contracts/webhook_contract.rb +9 -1
  26. data/lib/pact_broker/api/decorators/verification_decorator.rb +1 -0
  27. data/lib/pact_broker/api/decorators/webhook_decorator.rb +35 -5
  28. data/lib/pact_broker/api/resources/matrix_badge.rb +21 -0
  29. data/lib/pact_broker/certificates/certificate.rb +8 -0
  30. data/lib/pact_broker/certificates/service.rb +41 -0
  31. data/lib/pact_broker/doc/controllers/app.rb +12 -2
  32. data/lib/pact_broker/doc/views/webhooks.markdown +37 -2
  33. data/lib/pact_broker/domain/verification.rb +2 -0
  34. data/lib/pact_broker/domain/webhook.rb +2 -1
  35. data/lib/pact_broker/domain/webhook_request.rb +22 -4
  36. data/lib/pact_broker/error.rb +5 -0
  37. data/lib/pact_broker/matrix/parse_query.rb +7 -10
  38. data/lib/pact_broker/matrix/repository.rb +5 -32
  39. data/lib/pact_broker/matrix/service.rb +21 -3
  40. data/lib/pact_broker/pacts/repository.rb +9 -2
  41. data/lib/pact_broker/pacts/service.rb +2 -2
  42. data/lib/pact_broker/services.rb +5 -0
  43. data/lib/pact_broker/ui/app.rb +15 -0
  44. data/lib/pact_broker/ui/controllers/matrix.rb +58 -3
  45. data/lib/pact_broker/ui/views/matrix/show.haml +65 -10
  46. data/lib/pact_broker/verifications/service.rb +6 -1
  47. data/lib/pact_broker/version.rb +1 -1
  48. data/lib/pact_broker/webhooks/job.rb +1 -1
  49. data/lib/pact_broker/webhooks/repository.rb +17 -0
  50. data/lib/pact_broker/webhooks/service.rb +8 -7
  51. data/lib/pact_broker/webhooks/triggered_webhook.rb +1 -1
  52. data/lib/pact_broker/webhooks/webhook.rb +3 -0
  53. data/lib/pact_broker/webhooks/webhook_event.rb +24 -0
  54. data/pact_broker.gemspec +2 -1
  55. data/public/javascripts/matrix.js +60 -0
  56. data/public/stylesheets/matrix.css +12 -0
  57. data/script/db-spec.sh +1 -0
  58. data/script/foo-bar-verification.json +57 -0
  59. data/script/insert-self-signed-certificate-from-url.rb +32 -0
  60. data/script/publish-verification.sh +5 -0
  61. data/script/seed-matrix.rb +6 -5
  62. data/script/seed.rb +67 -59
  63. data/spec/features/create_webhook_spec.rb +4 -1
  64. data/spec/features/get_matrix_badge_spec.rb +40 -0
  65. data/spec/features/publish_verification_spec.rb +28 -4
  66. data/spec/fixtures/certificate-invalid.pem +29 -0
  67. data/spec/fixtures/certificate.pem +53 -0
  68. data/spec/fixtures/certificates/cacert.pem +21 -0
  69. data/spec/fixtures/certificates/cert.pem +20 -0
  70. data/spec/fixtures/certificates/key.pem +27 -0
  71. data/spec/fixtures/certificates/self-signed.badssl.com.pem +21 -0
  72. data/spec/fixtures/verification.json +4 -1
  73. data/spec/fixtures/webhook_valid.json +3 -0
  74. data/spec/integration/webhooks/certificate_spec.rb +80 -0
  75. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +20 -0
  76. data/spec/lib/pact_broker/api/decorators/pact_webhooks_status_decorator_spec.rb +2 -2
  77. data/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +5 -0
  78. data/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb +1 -0
  79. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +29 -19
  80. data/spec/lib/pact_broker/api/resources/badge_spec.rb +61 -0
  81. data/spec/lib/pact_broker/api/resources/matrix_badge_spec.rb +11 -0
  82. data/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb +8 -9
  83. data/spec/lib/pact_broker/certificates/service_spec.rb +60 -0
  84. data/spec/lib/pact_broker/domain/verification_spec.rb +13 -0
  85. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +0 -4
  86. data/spec/lib/pact_broker/matrix/service_spec.rb +40 -8
  87. data/spec/lib/pact_broker/pacts/repository_spec.rb +18 -1
  88. data/spec/lib/pact_broker/webhooks/job_spec.rb +1 -1
  89. data/spec/lib/pact_broker/webhooks/repository_spec.rb +53 -13
  90. data/spec/lib/pact_broker/webhooks/service_spec.rb +9 -6
  91. data/spec/migrations/change_migration_strategy_spec.rb +13 -14
  92. data/spec/spec_helper.rb +4 -0
  93. data/spec/support/ssl_webhook_server.rb +37 -0
  94. data/spec/support/test_data_builder.rb +12 -4
  95. data/tasks/database.rb +9 -7
  96. data/tasks/database/table_dependency_calculator.rb +44 -0
  97. metadata +57 -6
  98. data/lib/pact_broker/doc/views/pact-webhooks.markdown +0 -50
  99. data/lib/pact_broker/doc/views/webhooks-create.markdown +0 -38
  100. data/lib/pact_broker/doc/views/webhooks-webhooks.markdown +0 -15
@@ -2,5 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem "rake", "~>12.0"
4
4
  gem "pact_broker", git: "https://github.com/pact-foundation/pact_broker.git", ref: "11d3464a120ed5f76e23241c79992fa04b702701"
5
+ gem "webrick", "~>1.3.0" # Webrick requires Ruby version >= 2.5.0dev. TODO remove dependency on Pact gem.
5
6
  gem "sqlite3", "~>1.3"
6
7
  gem "pg"
@@ -0,0 +1,65 @@
1
+ # Run Pact Broker example
2
+
3
+ Clone project
4
+
5
+ ```bash
6
+ git clone https://github.com/pact-foundation/pact_broker
7
+ ```
8
+
9
+ Change directory to `example`
10
+
11
+ ```bash
12
+ cd pact_broker/example
13
+ ```
14
+
15
+ ## Run with sqlite database
16
+
17
+ Install dependencies
18
+
19
+ ```bash
20
+ bundle install
21
+ ```
22
+
23
+ Run Pact Broker
24
+
25
+ ```bash
26
+ bundle exec rackup
27
+ ```
28
+
29
+ ## Run with postgres database
30
+
31
+ Uncomment `gem 'pg'` in the [Gemfile](Gemfile)
32
+
33
+ Comment out `gem 'sqlite3'` in the [Gemfile](Gemfile)
34
+
35
+ Comment out the line with `DATABASE_CREDENTIALS = {adapter: "sqlite"...` in the [config.ru](config.ru#L9).
36
+
37
+ Uncomment the line with `DATABASE_CREDENTIALS = {adapter: "postgres"...`. in the [config.ru](config.ru#L17).
38
+
39
+ Set up postgres database
40
+
41
+ ```bash
42
+ psql postgres -c "CREATE DATABASE pact_broker;"
43
+ psql postgres -c "CREATE ROLE pact_broker WITH LOGIN PASSWORD 'pact_broker';"
44
+ psql postgres -c "GRANT ALL PRIVILEGES ON DATABASE pact_broker TO pact_broker;"
45
+ ```
46
+
47
+ Install dependencies
48
+
49
+ ```bash
50
+ bundle install
51
+ ```
52
+
53
+ Run Pact Broker
54
+
55
+ ```bash
56
+ bundle exec rackup
57
+ ```
58
+
59
+ If you need an example data run following command
60
+
61
+ ```bash
62
+ psql pact_broker < example_data.sql
63
+ ```
64
+
65
+ Now Pact Broker can be access locally at [http://localhost:9292](http://localhost:9292).
@@ -11,6 +11,7 @@ DATABASE_CREDENTIALS = {adapter: "sqlite", database: "pact_broker_database.sqlit
11
11
  # For postgres:
12
12
  #
13
13
  # $ psql postgres -c "CREATE DATABASE pact_broker;"
14
+ # $ psql postgres -c "CREATE ROLE pact_broker WITH LOGIN PASSWORD 'pact_broker';"
14
15
  # $ psql postgres -c "GRANT ALL PRIVILEGES ON DATABASE pact_broker TO pact_broker;"
15
16
  #
16
17
  # DATABASE_CREDENTIALS = {adapter: "postgres", database: "pact_broker", username: 'pact_broker', password: 'pact_broker', :encoding => 'utf8'}
@@ -0,0 +1,17 @@
1
+ BEGIN TRANSACTION;
2
+ INSERT INTO pacticipants VALUES(1,'Animal Service',NULL,'2014-11-18 06:28:36.261868','2014-11-18 06:28:36.261868');
3
+ INSERT INTO pacticipants VALUES(2,'Zoo App',NULL,'2014-11-18 06:28:36.290466','2014-11-18 06:28:36.290466');
4
+ INSERT INTO pacticipants VALUES(3,'Bar',NULL,'2017-05-23 06:10:23.235822','2017-05-23 06:10:23.235822');
5
+ INSERT INTO pacticipants VALUES(4,'Foo',NULL,'2017-05-23 06:10:23.239004','2017-05-23 06:10:23.239004');
6
+ INSERT INTO versions VALUES(1,'1.0.0',NULL,2,0,'2014-11-18 06:28:36.295750','2014-11-18 06:28:36.333021');
7
+ INSERT INTO versions VALUES(2,'1.0.1',NULL,2,1,'2015-01-19 22:13:57.743993','2015-01-19 22:13:57.746417');
8
+ INSERT INTO versions VALUES(3,'1.0.2',NULL,2,2,'2016-11-08 22:45:37.244731','2016-11-08 22:45:37.274760');
9
+ INSERT INTO versions VALUES(4,'1.0.0',NULL,4,0,'2017-05-23 06:10:23.243015','2017-05-23 06:10:23.245281');
10
+ INSERT INTO pact_versions VALUES(1,2,1,'15a22e805caa9d0153b815fc6566a0c03d4a82ae',replace('{\n "provider": {\n "name": "Animal Service"\n },\n "consumer": {\n "name": "Zoo App"\n },\n "interactions": [\n {\n "description": "a request for an alligator",\n "provider_state": "there is an alligator named Mary",\n "request": {\n "method": "get",\n "path": "/alligators/Mary",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 200,\n "headers": {\n "Content-Type": "application/json;charset=utf-8"\n },\n "body": {\n "name": "Mary"\n }\n }\n },\n {\n "description": "a request for an alligator",\n "provider_state": "there is not an alligator named Mary",\n "request": {\n "method": "get",\n "path": "/alligators/Mary",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 404\n }\n },\n {\n "description": "a request for an alligator",\n "provider_state": "an error occurs retrieving an alligator",\n "request": {\n "method": "get",\n "path": "/alligators/Mary",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 500,\n "headers": {\n "Content-Type": "application/json;charset=utf-8"\n },\n "body": {\n "error": "Argh!!!"\n }\n }\n }\n ],\n "metadata": {\n "pactSpecificationVersion": "1.0.0"\n }\n}','\n',''),'2014-11-18 06:28:36.340041');
11
+ INSERT INTO pact_versions VALUES(3,2,1,'23b64b5c5f5baf0e56dd92c4deddf9eb1e249d11',replace('{\n "provider": {\n "name": "Animal Service"\n },\n "consumer": {\n "name": "Zoo App"\n },\n "interactions": [\n {\n "description": "a request for an alligator",\n "provider_state": "there is an alligator named Mary",\n "request": {\n "method": "get",\n "path": "/alligators/Mary",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 200,\n "headers": {\n "Content-Type": "application/json;charset=utf-8"\n },\n "body": {\n "name": "Mary"\n }\n }\n },\n {\n "description": "a request for an alligator",\n "provider_state": "there is not an alligator named Mary",\n "request": {\n "method": "get",\n "path": "/alligators/Mary",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 404\n }\n },\n {\n "description": "a request for an alligator",\n "provider_state": "an error occurs retrieving an alligator",\n "request": {\n "method": "get",\n "path": "/alligators/Mary",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 500,\n "headers": {\n "Content-Type": "application/json;charset=utf-8"\n },\n "body": {\n "error": "Argh!!!"\n }\n }\n },\n {\n "description": "a request for an alligator''s sister",\n "provider_state": "there is an alligator named Mary",\n "request": {\n "method": "get",\n "path": "/alligators/Mary/sister",\n "headers": {\n "Accept": "application/json"\n }\n },\n "response": {\n "status": 200,\n "headers": {\n "Content-Type": "application/json;charset=utf-8"\n },\n "body": {\n "name": "Sue"\n }\n }\n }\n ],\n "metadata": {\n "pactSpecificationVersion": "1.0.0"\n }\n}','\n',''),'2015-01-19 22:15:10.964768');
12
+ INSERT INTO pact_versions VALUES(4,4,3,'73cd294d54d900e5ebb313896b1df8d1da8914f9',replace('{\n "consumer": {\n "name": "Foo"\n },\n "provider": {\n "name": "Bar"\n },\n "interactions": [\n {\n "description": "a retrieve thing request",\n "request": {\n "method": "get",\n "path": "/thing"\n },\n "response": {\n "status": 200,\n "headers": {\n "Content-Type": "application/json"\n },\n "body": [\n {\n "status": "4"\n }\n ],\n "matchingRules": {\n "$.body": {\n "min": 1\n },\n "$.body[*].*": {\n "match": "type"\n },\n "$.body[*].status": {\n "match": "regex",\n "regex": "\\d+"\n }\n }\n }\n }\n ],\n "metadata": {\n "pactSpecification": {\n "version": "2.0.0"\n }\n }\n}','\n',''),'2017-05-23 06:10:23.249813');
13
+ INSERT INTO pact_publications VALUES(1,1,1,1,1,'2014-11-18 06:28:36.340041');
14
+ INSERT INTO pact_publications VALUES(2,2,1,1,3,'2015-01-19 22:15:10.969518');
15
+ INSERT INTO pact_publications VALUES(3,3,1,1,1,'2016-11-08 22:45:37.291839');
16
+ INSERT INTO pact_publications VALUES(4,4,3,1,4,'2017-05-23 06:10:23.252829');
17
+ COMMIT;
@@ -61,6 +61,7 @@ module PactBroker
61
61
 
62
62
  # matrix
63
63
  add ['matrix', 'provider', :provider_name, 'consumer', :consumer_name], Api::Resources::MatrixForConsumerAndProvider, {resource_name: "matrix_consumer_provider"}
64
+ add ['matrix', 'provider', :provider_name, 'latest', :provider_tag, 'consumer', :consumer_name, 'latest', :tag, 'badge'], Api::Resources::MatrixBadge, {resource_name: "matrix_tag_badge"}
64
65
  add ['matrix'], Api::Resources::Matrix, {resource_name: "matrix"}
65
66
 
66
67
  add [], Api::Resources::Index, {resource_name: "index"}
@@ -73,5 +74,4 @@ module PactBroker
73
74
 
74
75
  pact_api.adapter
75
76
  end
76
-
77
77
  end
@@ -5,7 +5,6 @@ module PactBroker
5
5
  module Api
6
6
  module Contracts
7
7
  class WebhookContract < Reform::Form
8
- property :request
9
8
 
10
9
  validation do
11
10
  configure do
@@ -13,6 +12,7 @@ module PactBroker
13
12
  end
14
13
 
15
14
  required(:request).filled
15
+ optional(:events).maybe(min_size?: 1)
16
16
  end
17
17
 
18
18
  property :request do
@@ -39,6 +39,14 @@ module PactBroker
39
39
  required(:url).filled(:valid_url?)
40
40
  end
41
41
  end
42
+
43
+ collection :events do
44
+ property :name
45
+
46
+ validation do
47
+ required(:name).filled
48
+ end
49
+ end
42
50
  end
43
51
  end
44
52
  end
@@ -10,6 +10,7 @@ module PactBroker
10
10
  property :success
11
11
  property :execution_date, as: :verificationDate
12
12
  property :build_url, as: :buildUrl
13
+ property :test_results, as: :testResults
13
14
 
14
15
  link :self do | options |
15
16
  {
@@ -2,19 +2,24 @@ require_relative 'base_decorator'
2
2
  require 'pact_broker/api/decorators/webhook_request_decorator'
3
3
  require 'pact_broker/api/decorators/timestamps'
4
4
  require 'pact_broker/domain/webhook_request'
5
+ require 'pact_broker/webhooks/webhook_event'
5
6
  require 'pact_broker/api/decorators/basic_pacticipant_decorator'
7
+ require_relative 'pact_pacticipant_decorator'
8
+ require_relative 'pacticipant_decorator'
6
9
 
7
10
  module PactBroker
8
11
  module Api
9
12
  module Decorators
10
13
  class WebhookDecorator < BaseDecorator
11
14
 
12
- property :request, :class => PactBroker::Domain::WebhookRequest, :extend => WebhookRequestDecorator
15
+ class WebhookEventDecorator < BaseDecorator
16
+ property :name
17
+ end
13
18
 
14
- include Timestamps
19
+ property :request, :class => PactBroker::Domain::WebhookRequest, extend: WebhookRequestDecorator
20
+ collection :events, :class => PactBroker::Webhooks::WebhookEvent, extend: WebhookEventDecorator
15
21
 
16
- property :consumer, :extend => PactBroker::Api::Decorators::BasicPacticipantDecorator, :embedded => true, writeable: false
17
- property :provider, :extend => PactBroker::Api::Decorators::BasicPacticipantDecorator, :embedded => true, writeable: false
22
+ include Timestamps
18
23
 
19
24
  link :self do | options |
20
25
  {
@@ -31,9 +36,26 @@ module PactBroker
31
36
  }
32
37
  end
33
38
 
39
+
40
+ link :'pb:consumer' do | options |
41
+ {
42
+ title: "Consumer",
43
+ name: represented.consumer.name,
44
+ href: pacticipant_url(options.fetch(:base_url), represented.consumer)
45
+ }
46
+ end
47
+
48
+ link :'pb:provider' do | options |
49
+ {
50
+ title: "Provider",
51
+ name: represented.provider.name,
52
+ href: pacticipant_url(options.fetch(:base_url), represented.provider)
53
+ }
54
+ end
55
+
34
56
  link :'pb:pact-webhooks' do | options |
35
57
  {
36
- title: "All webhooks for the pact between #{represented.consumer.name} and #{represented.provider.name}",
58
+ title: "All webhooks for consumer #{represented.consumer.name} and provider #{represented.provider.name}",
37
59
  href: webhooks_for_pact_url(represented.consumer, represented.provider, options[:base_url])
38
60
  }
39
61
  end
@@ -44,6 +66,14 @@ module PactBroker
44
66
  href: webhooks_url(options[:base_url])
45
67
  }
46
68
  end
69
+
70
+ def from_json represented
71
+ super.tap do | webhook |
72
+ if webhook.events == nil
73
+ webhook.events = [PactBroker::Webhooks::WebhookEvent.new(name: PactBroker::Webhooks::WebhookEvent::DEFAULT_EVENT_NAME)]
74
+ end
75
+ end
76
+ end
47
77
  end
48
78
  end
49
79
  end
@@ -0,0 +1,21 @@
1
+ require 'pact_broker/api/resources/badge'
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ class MatrixBadge < Badge
7
+
8
+ private
9
+
10
+ def latest_verification
11
+ @latest_verification ||= begin
12
+ matrix_row = matrix_service.find_for_consumer_and_provider_with_tags(identifier_from_path)
13
+ if matrix_row && matrix_row[:verification_id]
14
+ verification_service.find_by_id(matrix_row[:verification_id])
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module PactBroker
2
+ module Certificates
3
+ class Certificate < Sequel::Model
4
+ end
5
+
6
+ Certificate.plugin :timestamps, update_on_create: true
7
+ end
8
+ end
@@ -0,0 +1,41 @@
1
+ require 'pact_broker/certificates/certificate'
2
+ require 'pact_broker/logging'
3
+ require 'openssl'
4
+
5
+ module PactBroker
6
+ module Certificates
7
+ module Service
8
+
9
+ extend self
10
+ extend PactBroker::Logging
11
+
12
+ def cert_store
13
+ cert_store = OpenSSL::X509::Store.new
14
+ cert_store.set_default_paths
15
+ find_all_certificates.each do | certificate |
16
+ begin
17
+ logger.debug("Loading certificate #{certificate.subject} in to cert store")
18
+ cert_store.add_cert(certificate)
19
+ rescue StandardError => e
20
+ log_error e, "Error adding certificate object #{certificate.to_s} to store"
21
+ end
22
+ end
23
+ cert_store
24
+ end
25
+
26
+ def find_all_certificates
27
+ Certificate.collect do | certificate |
28
+ cert_arr = certificate.content.split(/(-----END [^\-]+-----)/).each_slice(2).map(&:join)
29
+ cert_arr.collect do |c|
30
+ begin
31
+ OpenSSL::X509::Certificate.new(c)
32
+ rescue StandardError => e
33
+ log_error e, "Error creating certificate object from certificate #{certificate.uuid} '#{certificate.description}'"
34
+ nil
35
+ end
36
+ end
37
+ end.flatten.compact
38
+ end
39
+ end
40
+ end
41
+ end
@@ -11,16 +11,26 @@ module PactBroker
11
11
  set :root, File.join(File.dirname(__FILE__), '..')
12
12
  set :show_exceptions, true
13
13
 
14
+ MAPPINGS = {
15
+ 'webhooks-create' => 'webhooks',
16
+ 'webhooks-webhooks' => 'webhooks',
17
+ 'pact-webhooks' => 'webhooks',
18
+ }.freeze
19
+
14
20
  helpers do
21
+ def view_name_for rel_name
22
+ MAPPINGS[rel_name] || rel_name
23
+ end
24
+
15
25
  def resource_exists? rel_name
16
- File.exist? File.join(self.class.root, 'views', "#{rel_name}.markdown")
26
+ File.exist? File.join(self.class.root, 'views', "#{view_name_for(rel_name)}.markdown")
17
27
  end
18
28
  end
19
29
 
20
30
  get ":rel_name" do
21
31
  rel_name = params[:rel_name]
22
32
  if resource_exists? rel_name
23
- markdown rel_name.to_sym, {:layout_engine => :haml, layout: :'layouts/main'}, {}
33
+ markdown view_name_for(rel_name).to_sym, {:layout_engine => :haml, layout: :'layouts/main'}, {}
24
34
  else
25
35
  response.status = 404
26
36
  end
@@ -1,6 +1,8 @@
1
1
  # Webhooks
2
2
 
3
- Allowed methods: GET, POST, DELETE
3
+ Allowed methods (collection resource): `GET`, `POST`
4
+
5
+ Allowed methods (individual resource): `GET`, `PUT`, `DELETE`
4
6
 
5
7
  ### Creating
6
8
 
@@ -9,9 +11,12 @@ Allowed methods: GET, POST, DELETE
9
11
  2. Click the "NON-GET" button for the "pact-webhooks" relation.
10
12
  3. Paste in the webhook JSON (example shown below) in the body section and click "Make Request".
11
13
 
12
- An example webhook to trigger a Bamboo job.
14
+ An example webhook to trigger a Bamboo job when a contract has changed.
13
15
 
14
16
  {
17
+ "events": [{
18
+ "name": "contract_content_changed"
19
+ }],
15
20
  "request": {
16
21
  "method": "POST",
17
22
  "url": "http://master.ci.my.domain:8085/rest/api/latest/queue/SOME-PROJECT?os_authType=basic",
@@ -26,6 +31,9 @@ An example webhook to trigger a Bamboo job.
26
31
  A request body can be specified as well.
27
32
 
28
33
  {
34
+ "events": [{
35
+ "name": "contract_content_changed"
36
+ }],
29
37
  "request": {
30
38
  "method": "POST",
31
39
  "url": "http://example.org/something",
@@ -37,6 +45,33 @@ A request body can be specified as well.
37
45
 
38
46
  **BEWARE** The password can be reverse engineered from the database, so make a separate account for the Pact Broker to use, don't use your personal account!
39
47
 
48
+ #### Event types
49
+
50
+ `contract_content_changed:` triggered when the content of the contract has changed since the previous publication. Uses plain string equality, so changes to the ordering of hash keys, or whitespace changes will trigger this webhook.
51
+
52
+ `provider_verification_published:` triggered whenever a provider publishes a verification.
53
+
54
+ ### Dynamic variable substitution
55
+
56
+ The following variables may be used in the request parameters or body, and will be replaced with their appropriate values at runtime.
57
+
58
+ `${pactbroker.pactUrl}`: the "permalink" URL to the newly published pact (the URL specifying the consumer version URL, rather than the "/latest" format.)
59
+
60
+ Example usage:
61
+
62
+ {
63
+ "events": [{
64
+ "name": "contract_content_changed"
65
+ }],
66
+ "request": {
67
+ "method": "POST",
68
+ "url": "http://example.org/something",
69
+ "body": {
70
+ "thisPactWasPublished" : "${pactbroker.pactUrl}"
71
+ }
72
+ }
73
+ }
74
+
40
75
  ### Testing
41
76
 
42
77
  To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "execute" relation. The response or error will be shown in the window.
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/db'
2
2
  require 'pact_broker/repositories/helpers'
3
+ require 'json'
3
4
 
4
5
  module PactBroker
5
6
 
@@ -9,6 +10,7 @@ module PactBroker
9
10
  set_primary_key :id
10
11
  associate(:many_to_one, :pact_version, class: "PactBroker::Pacts::PactVersion", key: :pact_version_id, primary_key: :id)
11
12
  associate(:many_to_one, :provider_version, class: "PactBroker::Domain::Version", key: :provider_version_id, primary_key: :id)
13
+ plugin :serialization, :json, :test_results
12
14
 
13
15
  def before_create
14
16
  super
@@ -10,7 +10,7 @@ module PactBroker
10
10
  include Messages
11
11
  include Logging
12
12
 
13
- attr_accessor :uuid, :consumer, :provider, :request, :created_at, :updated_at
13
+ attr_accessor :uuid, :consumer, :provider, :request, :created_at, :updated_at, :events
14
14
  attr_reader :attributes
15
15
 
16
16
  def initialize attributes = {}
@@ -19,6 +19,7 @@ module PactBroker
19
19
  @request = attributes[:request]
20
20
  @consumer = attributes[:consumer]
21
21
  @provider = attributes[:provider]
22
+ @events = attributes[:events]
22
23
  @created_at = attributes[:created_at]
23
24
  @updated_at = attributes[:updated_at]
24
25
  end
@@ -5,6 +5,7 @@ require 'pact_broker/messages'
5
5
  require 'net/http'
6
6
  require 'pact_broker/webhooks/redact_logs'
7
7
  require 'pact_broker/api/pact_broker_urls'
8
+ require 'pact_broker/services'
8
9
 
9
10
  module PactBroker
10
11
 
@@ -23,6 +24,7 @@ module PactBroker
23
24
 
24
25
  include PactBroker::Logging
25
26
  include PactBroker::Messages
27
+ include PactBroker::Services
26
28
 
27
29
  attr_accessor :method, :url, :headers, :body, :username, :password, :uuid
28
30
 
@@ -102,8 +104,13 @@ module PactBroker
102
104
 
103
105
  def do_request uri, req
104
106
  logger.info "Making webhook #{uuid} request #{to_s}"
105
- Net::HTTP.start(uri.hostname, uri.port,
106
- :use_ssl => uri.scheme == 'https') do |http|
107
+ options = {}
108
+ if uri.scheme == 'https'
109
+ options[:use_ssl] = true
110
+ options[:verify_mode] = OpenSSL::SSL::VERIFY_PEER
111
+ options[:cert_store] = cert_store
112
+ end
113
+ Net::HTTP.start(uri.hostname, uri.port, options) do |http|
107
114
  http.request req
108
115
  end
109
116
  end
@@ -120,8 +127,15 @@ module PactBroker
120
127
  end
121
128
 
122
129
  def log_completion_message options, execution_logger, success
123
- execution_logger.info(options[:success_log_message]) if options[:success_log_message] && success
124
- execution_logger.info(options[:failure_log_message]) if options[:failure_log_message] && !success
130
+ if options[:success_log_message] && success
131
+ execution_logger.info(options[:success_log_message])
132
+ logger.info(options[:success_log_message])
133
+ end
134
+
135
+ if options[:failure_log_message] && !success
136
+ execution_logger.info(options[:failure_log_message])
137
+ logger.info(options[:failure_log_message])
138
+ end
125
139
  end
126
140
 
127
141
  def to_s
@@ -153,6 +167,10 @@ module PactBroker
153
167
  escaped_pact_url = CGI::escape(pact_url)
154
168
  url.gsub('${pactbroker.pactUrl}', escaped_pact_url)
155
169
  end
170
+
171
+ def cert_store
172
+ certificate_service.cert_store
173
+ end
156
174
  end
157
175
  end
158
176
  end