pact_broker 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +4 -2
  4. data/CHANGELOG.md +54 -0
  5. data/DEVELOPER_DOCUMENTATION.md +11 -7
  6. data/README.md +5 -1
  7. data/UPGRADING.md +18 -0
  8. data/db/migrations/19_make_pact_version_content_sha_not_nullable.rb +9 -1
  9. data/db/migrations/25_make_pv_pacticipants_mandatory.rb +8 -0
  10. data/db/migrations/38_create_triggered_webhooks_table.rb +19 -0
  11. data/db/migrations/39_add_triggered_webhooks_fk_to_execution.rb +24 -0
  12. data/db/migrations/40_create_latest_triggered_webhooks_view.rb +24 -0
  13. data/db/migrations/41_migrate_execution_data.rb +47 -0
  14. data/db/test/backwards_compatibility/.rspec +3 -0
  15. data/db/test/backwards_compatibility/Appraisals +49 -0
  16. data/db/test/backwards_compatibility/Gemfile +11 -0
  17. data/db/test/backwards_compatibility/Rakefile +55 -0
  18. data/db/test/backwards_compatibility/config.ru +18 -0
  19. data/db/test/backwards_compatibility/gemfiles/1.18.0.gemfile +14 -0
  20. data/db/test/backwards_compatibility/gemfiles/1.18.0.gemfile.lock +210 -0
  21. data/db/test/backwards_compatibility/gemfiles/2.0.0.gemfile +14 -0
  22. data/db/test/backwards_compatibility/gemfiles/2.0.0.gemfile.lock +208 -0
  23. data/db/test/backwards_compatibility/gemfiles/2.1.0.gemfile +14 -0
  24. data/db/test/backwards_compatibility/gemfiles/2.1.0.gemfile.lock +209 -0
  25. data/db/test/backwards_compatibility/gemfiles/2.2.0.gemfile +14 -0
  26. data/db/test/backwards_compatibility/gemfiles/2.2.0.gemfile.lock +197 -0
  27. data/db/test/backwards_compatibility/gemfiles/2.3.0.gemfile +13 -0
  28. data/db/test/backwards_compatibility/gemfiles/2.3.0.gemfile.lock +196 -0
  29. data/db/test/backwards_compatibility/gemfiles/2.4.2.gemfile +13 -0
  30. data/db/test/backwards_compatibility/gemfiles/2.4.2.gemfile.lock +196 -0
  31. data/db/test/backwards_compatibility/gemfiles/head.gemfile +13 -0
  32. data/db/test/backwards_compatibility/gemfiles/head.gemfile.lock +200 -0
  33. data/db/test/backwards_compatibility/spec/fixtures/foo-bar.json +22 -0
  34. data/db/test/backwards_compatibility/spec/publish_pact_spec.rb +72 -0
  35. data/db/test/backwards_compatibility/spec/spec_helper.rb +20 -0
  36. data/db/test/backwards_compatibility/spec/support/fixture_helpers.rb +12 -0
  37. data/db/test/backwards_compatibility/spec/support/request_helpers.rb +20 -0
  38. data/example/Gemfile +2 -2
  39. data/example/pact_broker_database.sqlite3 +0 -0
  40. data/lib/pact_broker/api/decorators/pact_collection_decorator.rb +1 -2
  41. data/lib/pact_broker/api/decorators/pact_decorator.rb +12 -10
  42. data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +1 -2
  43. data/lib/pact_broker/api/decorators/pact_webhooks_status_decorator.rb +123 -0
  44. data/lib/pact_broker/api/decorators/versions_decorator.rb +17 -6
  45. data/lib/pact_broker/api/decorators/webhook_decorator.rb +8 -10
  46. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +0 -1
  47. data/lib/pact_broker/api/pact_broker_urls.rb +13 -1
  48. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +47 -3
  49. data/lib/pact_broker/api/resources/badge.rb +3 -3
  50. data/lib/pact_broker/api/resources/base_resource.rb +1 -1
  51. data/lib/pact_broker/api/resources/latest_pact.rb +5 -1
  52. data/lib/pact_broker/api/resources/pact.rb +5 -1
  53. data/lib/pact_broker/api/resources/pact_webhooks_status.rb +61 -0
  54. data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +36 -0
  55. data/lib/pact_broker/api/resources/webhook.rb +31 -3
  56. data/lib/pact_broker/api/resources/webhook_execution.rb +12 -2
  57. data/lib/pact_broker/api.rb +3 -0
  58. data/lib/pact_broker/app.rb +11 -3
  59. data/lib/pact_broker/badges/service.rb +26 -5
  60. data/lib/pact_broker/configuration.rb +12 -5
  61. data/lib/pact_broker/constants.rb +1 -1
  62. data/lib/pact_broker/diagnostic/resources/heartbeat.rb +1 -2
  63. data/lib/pact_broker/doc/views/pact-webhooks.markdown +1 -1
  64. data/lib/pact_broker/doc/views/webhooks-webhooks.markdown +1 -1
  65. data/lib/pact_broker/doc/views/webhooks.markdown +1 -1
  66. data/lib/pact_broker/domain/relationship.rb +13 -4
  67. data/lib/pact_broker/domain/verification.rb +0 -4
  68. data/lib/pact_broker/domain/webhook.rb +2 -6
  69. data/lib/pact_broker/domain/webhook_execution_result.rb +1 -2
  70. data/lib/pact_broker/domain/webhook_request.rb +59 -40
  71. data/lib/pact_broker/pacticipants/service.rb +4 -3
  72. data/lib/pact_broker/pacts/repository.rb +8 -0
  73. data/lib/pact_broker/pacts/service.rb +2 -0
  74. data/lib/pact_broker/services.rb +1 -1
  75. data/lib/pact_broker/ui/view_models/relationship.rb +29 -2
  76. data/lib/pact_broker/ui/views/relationships/show.haml +7 -10
  77. data/lib/pact_broker/verifications/repository.rb +8 -1
  78. data/lib/pact_broker/version.rb +1 -1
  79. data/lib/pact_broker/webhooks/execution.rb +25 -4
  80. data/lib/pact_broker/webhooks/job.rb +55 -13
  81. data/lib/pact_broker/webhooks/latest_triggered_webhook.rb +9 -0
  82. data/lib/pact_broker/webhooks/redact_logs.rb +10 -0
  83. data/lib/pact_broker/webhooks/repository.rb +76 -8
  84. data/lib/pact_broker/webhooks/service.rb +48 -8
  85. data/lib/pact_broker/webhooks/status.rb +29 -0
  86. data/lib/pact_broker/webhooks/triggered_webhook.rb +96 -0
  87. data/lib/pact_broker/webhooks/webhook.rb +19 -8
  88. data/lib/rack/pact_broker/database_transaction.rb +9 -3
  89. data/pact_broker.gemspec +3 -3
  90. data/public/javascripts/pact.js +5 -0
  91. data/public/stylesheets/pact.css +14 -1
  92. data/public/stylesheets/relationships.css +0 -1
  93. data/script/db-spec.sh +7 -0
  94. data/script/seed.rb +13 -8
  95. data/spec/features/create_webhook_spec.rb +1 -1
  96. data/spec/features/delete_pact_spec.rb +5 -1
  97. data/spec/features/delete_webhook_spec.rb +2 -1
  98. data/spec/features/edit_webhook_spec.rb +61 -0
  99. data/spec/features/execute_webhook_spec.rb +73 -0
  100. data/spec/features/get_latest_pact_badge_spec.rb +1 -1
  101. data/spec/features/get_latest_tagged_pact_badge_spec.rb +1 -1
  102. data/spec/features/get_latest_untagged_pact_badge_spec.rb +1 -1
  103. data/spec/features/get_pact_spec.rb +1 -1
  104. data/spec/features/merge_pact_spec.rb +1 -1
  105. data/spec/features/publish_pact_spec.rb +1 -1
  106. data/spec/integration/app_spec.rb +1 -1
  107. data/spec/integration/endpoints/group.rb +1 -1
  108. data/spec/lib/pact_broker/api/decorators/latest_pact_decorator_spec.rb +2 -1
  109. data/spec/lib/pact_broker/api/decorators/pact_decorator_spec.rb +8 -6
  110. data/spec/lib/pact_broker/api/decorators/pact_webhooks_status_decorator_spec.rb +134 -0
  111. data/spec/lib/pact_broker/api/decorators/relationships_csv_decorator_spec.rb +1 -1
  112. data/spec/lib/pact_broker/api/decorators/representable_pact_spec.rb +1 -1
  113. data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +27 -1
  114. data/spec/lib/pact_broker/api/resources/badge_spec.rb +32 -15
  115. data/spec/lib/pact_broker/api/resources/base_resource_spec.rb +17 -0
  116. data/spec/lib/pact_broker/api/resources/latest_pact_spec.rb +5 -3
  117. data/spec/lib/pact_broker/api/resources/pact_spec.rb +9 -2
  118. data/spec/lib/pact_broker/api/resources/triggered_webhook_logs_spec.rb +28 -0
  119. data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +15 -5
  120. data/spec/lib/pact_broker/api/resources/webhook_spec.rb +43 -31
  121. data/spec/lib/pact_broker/app_spec.rb +12 -8
  122. data/spec/lib/pact_broker/badges/service_spec.rb +15 -1
  123. data/spec/lib/pact_broker/configuration_spec.rb +3 -2
  124. data/spec/lib/pact_broker/domain/relationship_spec.rb +24 -0
  125. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +47 -31
  126. data/spec/lib/pact_broker/domain/webhook_spec.rb +4 -6
  127. data/spec/lib/pact_broker/pacticipants/service_spec.rb +16 -1
  128. data/spec/lib/pact_broker/pacts/repository_spec.rb +22 -1
  129. data/spec/lib/pact_broker/pacts/service_spec.rb +32 -1
  130. data/spec/lib/pact_broker/ui/view_models/relationship_spec.rb +44 -0
  131. data/spec/lib/pact_broker/verifications/repository_spec.rb +19 -0
  132. data/spec/lib/pact_broker/verifications/service_spec.rb +1 -1
  133. data/spec/lib/pact_broker/webhooks/job_spec.rb +80 -19
  134. data/spec/lib/pact_broker/webhooks/redact_logs_spec.rb +49 -0
  135. data/spec/lib/pact_broker/webhooks/repository_spec.rb +271 -21
  136. data/spec/lib/pact_broker/webhooks/service_spec.rb +70 -3
  137. data/spec/lib/pact_broker/webhooks/status_spec.rb +48 -0
  138. data/spec/lib/pact_broker/webhooks/triggered_webhook_spec.rb +40 -0
  139. data/spec/lib/rack/pact_broker/database_transaction_spec.rb +14 -4
  140. data/spec/migrations/23_pact_versions_spec.rb +8 -30
  141. data/spec/migrations/24_populate_pact_contents_spec.rb +3 -21
  142. data/spec/migrations/34_latest_tagged_pacts_spec.rb +1 -17
  143. data/spec/migrations/34_pact_revisions_spec.rb +7 -23
  144. data/spec/migrations/41_migrate_execution_data_spec.rb +109 -0
  145. data/spec/service_consumers/pact_helper.rb +5 -1
  146. data/spec/spec_helper.rb +15 -7
  147. data/spec/support/database_cleaner.rb +15 -2
  148. data/spec/support/migration_helpers.rb +16 -0
  149. data/spec/support/test_data_builder.rb +41 -9
  150. data/tasks/database.rb +7 -2
  151. data/tasks/db.rake +10 -0
  152. data/tasks/rspec.rake +1 -1
  153. data/vendor/hal-browser/browser.html +3 -2
  154. data/vendor/hal-browser/js/hal/resource.js +16 -2
  155. metadata +72 -13
  156. data/script/record_verification.sh +0 -4
@@ -14,13 +14,14 @@ module PactBroker
14
14
 
15
15
  include PactBroker::Logging
16
16
 
17
- def self.call pact
18
- new(pact).call
17
+ def self.call pact, options = {}
18
+ new(pact, options).call
19
19
  end
20
20
 
21
- def initialize pact
21
+ def initialize pact, options = {}
22
22
  @json_content = pact.json_content
23
23
  @pact = pact
24
+ @options = options
24
25
  end
25
26
 
26
27
  def call
@@ -40,12 +41,17 @@ module PactBroker
40
41
  <link rel='stylesheet' type='text/css' href='/stylesheets/pact.css'>
41
42
  <link rel='stylesheet' type='text/css' href='/stylesheets/github-json.css'>
42
43
  <script src='/javascripts/highlight.pack.js'></script>
44
+ <script src='/javascripts/jquery-2.1.1.min.js'></script>
45
+ <script src='/js/bootstrap.min.js'></script>
46
+ <script src='/javascripts/pact.js'></script>
43
47
  <script>hljs.initHighlightingOnLoad();</script>"
44
48
  end
45
49
 
46
50
  def pact_metadata
47
51
  "<div class='pact-metadata'>
48
52
  <ul>
53
+ #{badge_list_item}
54
+ #{badge_markdown_item}
49
55
  <li>
50
56
  <span class='name'>#{@pact.consumer.name} version:</span>
51
57
  <span class='value'>#{@pact.consumer_version_number}#{tags}</span>
@@ -64,6 +70,36 @@ module PactBroker
64
70
  </div>"
65
71
  end
66
72
 
73
+ def badge_list_item
74
+ "<li class='badge'>
75
+ <img src='#{badge_url}'/>
76
+ </li>
77
+ "
78
+ end
79
+
80
+ def badge_markdown_item
81
+ "<li class='badge-markdown' style='display:none'>
82
+ <textarea rows='3' cols='100'>#{badge_markdown}</textarea>
83
+ </li>"
84
+ end
85
+
86
+ def badge_markdown
87
+ warning = if badges_protected?
88
+ "If the broker is protected by authentication, set `enable_public_badge_access` to true in the configuration to enable badges to be embedded in a markdown file.\n"
89
+ else
90
+ ""
91
+ end
92
+ "#{warning}[![#{@pact.consumer.name}/#{@pact.provider.name} Pact Status](#{badge_url})](#{badge_target_url})"
93
+ end
94
+
95
+ def badges_protected?
96
+ !PactBroker.configuration.enable_public_badge_access
97
+ end
98
+
99
+ def base_url
100
+ @options[:base_url] || ''
101
+ end
102
+
67
103
  def title
68
104
  "Pact between #{@pact.consumer.name} and #{@pact.provider.name}"
69
105
  end
@@ -80,6 +116,14 @@ module PactBroker
80
116
  PactBroker::Api::PactBrokerUrls.pact_url '', @pact
81
117
  end
82
118
 
119
+ def badge_target_url
120
+ base_url
121
+ end
122
+
123
+ def badge_url
124
+ @options[:badge_url]
125
+ end
126
+
83
127
  def tags
84
128
  if @pact.consumer_version_tag_names.any?
85
129
  " (#{@pact.consumer_version_tag_names.join(", ")})"
@@ -17,11 +17,11 @@ module PactBroker
17
17
  end
18
18
 
19
19
  def resource_exists?
20
- PactBroker.configuration.enable_badge_resources
20
+ true
21
21
  end
22
22
 
23
23
  def is_authorized?(authorization_header)
24
- true
24
+ super || PactBroker.configuration.enable_public_badge_access
25
25
  end
26
26
 
27
27
  def forbidden?
@@ -31,7 +31,7 @@ module PactBroker
31
31
  private
32
32
 
33
33
  def to_svg
34
- badges_service.pact_verification_badge pact, label, initials, verification_status
34
+ badge_service.pact_verification_badge pact, label, initials, verification_status
35
35
  end
36
36
 
37
37
  def pact
@@ -75,7 +75,7 @@ module PactBroker
75
75
  end
76
76
 
77
77
  def resource_url
78
- request.uri.to_s
78
+ request.uri.to_s.gsub(/\?.*/, '')
79
79
  end
80
80
 
81
81
  def decorator_context options = {}
@@ -27,7 +27,11 @@ module PactBroker
27
27
  end
28
28
 
29
29
  def to_html
30
- PactBroker.configuration.html_pact_renderer.call(pact)
30
+ PactBroker.configuration.html_pact_renderer.call(
31
+ pact, {
32
+ base_url: base_url,
33
+ badge_url: "#{resource_url}/badge.svg"
34
+ })
31
35
  end
32
36
 
33
37
  def pact
@@ -79,7 +79,11 @@ module PactBroker
79
79
  end
80
80
 
81
81
  def to_html
82
- PactBroker.configuration.html_pact_renderer.call(pact)
82
+ PactBroker.configuration.html_pact_renderer.call(
83
+ pact, {
84
+ base_url: base_url,
85
+ badge_url: badge_url_for_latest_pact(pact, base_url)
86
+ })
83
87
  end
84
88
 
85
89
  def delete_resource
@@ -0,0 +1,61 @@
1
+ require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/api/decorators/pact_webhooks_status_decorator'
3
+
4
+ module PactBroker
5
+
6
+ module Api
7
+ module Resources
8
+
9
+ class PactWebhooksStatus < BaseResource
10
+
11
+ def allowed_methods
12
+ ["GET"]
13
+ end
14
+
15
+ def content_types_provided
16
+ [["application/hal+json", :to_json]]
17
+ end
18
+
19
+ def resource_exists?
20
+ consumer && provider
21
+ end
22
+
23
+ def to_json
24
+ decorator_for(latest_triggered_webhooks).to_json(user_options: decorator_context(identifier_from_path))
25
+ end
26
+
27
+ private
28
+
29
+ def latest_triggered_webhooks
30
+ @latest_triggered_webhooks ||= webhook_service.find_latest_triggered_webhooks(consumer, provider)
31
+ end
32
+
33
+ def pact
34
+ @pact ||= pact_service.find_latest_pact(pact_params)
35
+ end
36
+
37
+ def webhooks
38
+ webhook_service.find_by_consumer_and_provider consumer, provider
39
+ end
40
+
41
+ def consumer
42
+ @consumer ||= find_pacticipant(identifier_from_path[:consumer_name], "consumer")
43
+ end
44
+
45
+ def provider
46
+ @provider ||= find_pacticipant(identifier_from_path[:provider_name], "provider")
47
+ end
48
+
49
+ def find_pacticipant name, role
50
+ pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
51
+ set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
52
+ end
53
+ end
54
+
55
+ def decorator_for latest_triggered_webhooks
56
+ PactBroker::Api::Decorators::PactWebhooksStatusDecorator.new(latest_triggered_webhooks)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/webhooks/triggered_webhook'
3
+
4
+ module PactBroker
5
+ module Api
6
+ module Resources
7
+
8
+ class TriggeredWebhookLogs < BaseResource
9
+
10
+ def content_types_provided
11
+ [["text/plain", :to_text]]
12
+ end
13
+
14
+ def allowed_methods
15
+ ["GET"]
16
+ end
17
+
18
+ def resource_exists?
19
+ triggered_webhook
20
+ end
21
+
22
+ def to_text
23
+ # Too simple to bother putting into a service
24
+ triggered_webhook.webhook_executions.collect(&:logs).join("\n")
25
+ end
26
+
27
+ def triggered_webhook
28
+ @triggered_webhook ||= begin
29
+ criteria = {webhook_uuid: identifier_from_path[:uuid], trigger_uuid: identifier_from_path[:trigger_uuid]}
30
+ PactBroker::Webhooks::TriggeredWebhook.where(criteria).single_record
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -7,18 +7,38 @@ module PactBroker
7
7
 
8
8
  class Webhook < BaseResource
9
9
 
10
+ def content_types_accepted
11
+ [["application/json", :from_json]]
12
+ end
13
+
10
14
  def content_types_provided
11
15
  [["application/hal+json", :to_json]]
12
16
  end
13
17
 
14
18
  def allowed_methods
15
- ["GET", "DELETE"]
19
+ ["GET", "PUT", "DELETE"]
16
20
  end
17
21
 
18
22
  def resource_exists?
19
23
  webhook
20
24
  end
21
25
 
26
+ def malformed_request?
27
+ if request.put?
28
+ return invalid_json? || validation_errors?(webhook)
29
+ end
30
+ false
31
+ end
32
+
33
+ def from_json
34
+ if webhook
35
+ @webhook = webhook_service.update_by_uuid uuid, new_webhook
36
+ response.body = to_json
37
+ else
38
+ 404
39
+ end
40
+ end
41
+
22
42
  def to_json
23
43
  Decorators::WebhookDecorator.new(webhook).to_json(user_options: { base_url: base_url })
24
44
  end
@@ -30,16 +50,24 @@ module PactBroker
30
50
 
31
51
  private
32
52
 
53
+ def validation_errors? webhook
54
+ errors = webhook_service.errors(new_webhook)
55
+ set_json_validation_error_messages(errors.messages) if !errors.empty?
56
+ !errors.empty?
57
+ end
58
+
33
59
  def webhook
34
60
  @webhook ||= webhook_service.find_by_uuid uuid
35
61
  end
36
62
 
63
+ def new_webhook
64
+ @new_webhook ||= Decorators::WebhookDecorator.new(PactBroker::Domain::Webhook.new).from_json(request_body)
65
+ end
66
+
37
67
  def uuid
38
68
  identifier_from_path[:uuid]
39
69
  end
40
-
41
70
  end
42
71
  end
43
-
44
72
  end
45
73
  end
@@ -1,6 +1,7 @@
1
1
  require 'pact_broker/api/resources/base_resource'
2
2
  require 'pact_broker/services'
3
3
  require 'pact_broker/api/decorators/webhook_execution_result_decorator'
4
+ require 'pact_broker/constants'
4
5
 
5
6
  module PactBroker
6
7
  module Api
@@ -13,10 +14,15 @@ module PactBroker
13
14
  end
14
15
 
15
16
  def process_post
16
- webhook_execution_result = webhook_service.execute_webhook_now webhook
17
+ webhook_execution_result = webhook_service.execute_webhook_now webhook, pact
17
18
  response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
18
19
  response.body = post_response_body webhook_execution_result
19
- webhook_execution_result.success? ? true : 500
20
+ if webhook_execution_result.success?
21
+ true
22
+ else
23
+ response.headers[PactBroker::DO_NOT_ROLLBACK] = 'true'
24
+ 500
25
+ end
20
26
  end
21
27
 
22
28
  def resource_exists?
@@ -33,6 +39,10 @@ module PactBroker
33
39
  @webhook ||= webhook_service.find_by_uuid uuid
34
40
  end
35
41
 
42
+ def pact
43
+ @pact ||= pact_service.find_latest_pact consumer_name: webhook.consumer_name, provider_name: webhook.provider_name
44
+ end
45
+
36
46
  def uuid
37
47
  identifier_from_path[:uuid]
38
48
  end
@@ -49,7 +49,10 @@ module PactBroker
49
49
 
50
50
  # Webhooks
51
51
  add ['webhooks', 'provider', :provider_name, 'consumer', :consumer_name ], Api::Resources::PactWebhooks, {resource_name: "pact_webhooks"}
52
+ add ['webhooks', 'provider', :provider_name, 'consumer', :consumer_name, 'status' ], Api::Resources::PactWebhooksStatus, {resource_name: "pact_webhooks_status"}
53
+
52
54
  add ['webhooks', :uuid ], Api::Resources::Webhook, {resource_name: "webhook"}
55
+ add ['webhooks', :uuid, 'trigger', :trigger_uuid, 'logs' ], Api::Resources::TriggeredWebhookLogs, {resource_name: "triggered_webhook_logs"}
53
56
  add ['webhooks', :uuid, 'execute' ], Api::Resources::WebhookExecution, {resource_name: "execute_webhook"}
54
57
  add ['webhooks'], Api::Resources::Webhooks, {resource_name: "webhooks"}
55
58
 
@@ -23,7 +23,7 @@ module PactBroker
23
23
  @configuration = PactBroker.configuration
24
24
  yield configuration
25
25
  post_configure
26
- migrate_database
26
+ prepare_database
27
27
  prepare_app
28
28
  end
29
29
 
@@ -45,15 +45,18 @@ module PactBroker
45
45
  PactBroker.logger = configuration.logger
46
46
  SuckerPunch.logger = configuration.logger
47
47
  configure_database_connection
48
+ configure_sucker_punch
48
49
  end
49
50
 
50
- def migrate_database
51
+ def prepare_database
51
52
  if configuration.auto_migrate_db
52
53
  logger.info "Migrating database"
53
54
  PactBroker::DB.run_migrations configuration.database_connection
54
55
  else
55
56
  logger.info "Skipping database migrations"
56
57
  end
58
+ require 'pact_broker/webhooks/service'
59
+ PactBroker::Webhooks::Service.fail_retrying_triggered_webhooks
57
60
  end
58
61
 
59
62
  def configure_database_connection
@@ -119,6 +122,12 @@ module PactBroker
119
122
  builder
120
123
  end
121
124
 
125
+ def configure_sucker_punch
126
+ SuckerPunch.exception_handler = -> (ex, klass, args) do
127
+ PactBroker.log_error(ex, "Unhandled Suckerpunch error for #{klass}.perform(#{args.inspect})")
128
+ end
129
+ end
130
+
122
131
  def running_app
123
132
  @running_app ||= begin
124
133
  apps = @cascade_apps
@@ -128,6 +137,5 @@ module PactBroker
128
137
  @app_builder
129
138
  end
130
139
  end
131
-
132
140
  end
133
141
  end
@@ -12,6 +12,7 @@ module PactBroker
12
12
  include PactBroker::Logging
13
13
 
14
14
  SPACE_DASH_UNDERSCORE = /[\s_\-]/
15
+ CACHE = {}
15
16
 
16
17
  def pact_verification_badge pact, label, initials, verification_status
17
18
  return static_svg(pact, verification_status) unless pact
@@ -23,6 +24,10 @@ module PactBroker
23
24
  dynamic_svg(title, status, color) || static_svg(pact, verification_status)
24
25
  end
25
26
 
27
+ def clear_cache
28
+ CACHE.clear
29
+ end
30
+
26
31
  private
27
32
 
28
33
  def badge_title pact, label, initials
@@ -79,7 +84,7 @@ module PactBroker
79
84
  response = do_request(uri)
80
85
  response.code == '200' ? response.body : nil
81
86
  rescue StandardError => e
82
- log_error e, "Error retrieving badge from #{uri}"
87
+ logger.error "Error retrieving badge from #{uri} due to #{e.class} - #{e.message}"
83
88
  nil
84
89
  end
85
90
  end
@@ -95,11 +100,27 @@ module PactBroker
95
100
  end
96
101
 
97
102
  def do_request(uri)
98
- request = Net::HTTP::Get.new(uri)
99
- Net::HTTP.start(uri.hostname, uri.port,
100
- use_ssl: uri.scheme == 'https', read_timeout: 1000) do |http|
101
- http.request request
103
+ with_cache uri do
104
+ request = Net::HTTP::Get.new(uri)
105
+ Net::HTTP.start(uri.hostname, uri.port,
106
+ use_ssl: uri.scheme == 'https',
107
+ read_timeout: 3,
108
+ open_timeout: 1,
109
+ ssl_timeout: 1,
110
+ continue_timeout: 1) do |http|
111
+ http.request request
112
+ end
113
+ end
114
+ end
115
+
116
+ def with_cache uri
117
+ if !(response = CACHE[uri])
118
+ response = yield
119
+ if response.code == '200'
120
+ CACHE[uri] = response
121
+ end
102
122
  end
123
+ response
103
124
  end
104
125
 
105
126
  def static_svg pact, verification_status
@@ -11,13 +11,14 @@ module PactBroker
11
11
 
12
12
  class Configuration
13
13
 
14
- SAVABLE_SETTING_NAMES = [:order_versions_by_date, :use_case_sensitive_resource_names, :enable_badge_resources, :shields_io_base_url]
14
+ SAVABLE_SETTING_NAMES = [:order_versions_by_date, :use_case_sensitive_resource_names, :enable_public_badge_access, :shields_io_base_url]
15
15
 
16
16
  attr_accessor :log_dir, :database_connection, :auto_migrate_db, :use_hal_browser, :html_pact_renderer
17
17
  attr_accessor :validate_database_connection_config, :enable_diagnostic_endpoints, :version_parser
18
18
  attr_accessor :use_case_sensitive_resource_names, :order_versions_by_date
19
19
  attr_accessor :semver_formats
20
- attr_accessor :enable_badge_resources, :shields_io_base_url
20
+ attr_accessor :enable_public_badge_access, :shields_io_base_url
21
+ attr_accessor :webhook_retry_schedule
21
22
  attr_writer :logger
22
23
 
23
24
  def initialize
@@ -39,7 +40,7 @@ module PactBroker
39
40
  config.use_hal_browser = true
40
41
  config.validate_database_connection_config = true
41
42
  config.enable_diagnostic_endpoints = true
42
- config.enable_badge_resources = false # For security
43
+ config.enable_public_badge_access = false # For security
43
44
  config.shields_io_base_url = "https://img.shields.io".freeze
44
45
  config.use_case_sensitive_resource_names = true
45
46
  config.html_pact_renderer = default_html_pact_render
@@ -48,13 +49,14 @@ module PactBroker
48
49
  # consistently extract an orderable object from the consumer application version number.
49
50
  config.order_versions_by_date = false
50
51
  config.semver_formats = ["%M.%m.%p%s%d","%M.%m", "%M"]
52
+ config.webhook_retry_schedule = [10, 60, 120, 300, 600, 1200] #10 sec, 1 min, 2 min, 5 min, 10 min, 20 min => 38 minutes
51
53
  config
52
54
  end
53
55
 
54
56
  def self.default_html_pact_render
55
- lambda { |pact|
57
+ lambda { |pact, options|
56
58
  require 'pact_broker/api/renderers/html_pact_renderer'
57
- PactBroker::Api::Renderers::HtmlPactRenderer.call pact
59
+ PactBroker::Api::Renderers::HtmlPactRenderer.call pact, options
58
60
  }
59
61
  end
60
62
 
@@ -106,6 +108,11 @@ module PactBroker
106
108
  end
107
109
  end
108
110
 
111
+ def enable_badge_resources= enable_badge_resources
112
+ puts "Pact Broker configuration property `enable_badge_resources` is deprecated. Please use `enable_public_badge_access`"
113
+ self.enable_public_badge_access = enable_badge_resources
114
+ end
115
+
109
116
  def save_to_database
110
117
  # Can't require a Sequel::Model class before the connection has been set
111
118
  require 'pact_broker/config/save'
@@ -1,5 +1,5 @@
1
1
  module PactBroker
2
2
 
3
3
  CONSUMER_VERSION_HEADER = 'X-Pact-Consumer-Version'.freeze
4
-
4
+ DO_NOT_ROLLBACK = 'X-Pact-Broker-Do-Not-Rollback'.freeze
5
5
  end
@@ -14,7 +14,7 @@ module PactBroker
14
14
  end
15
15
 
16
16
  def to_json
17
- {
17
+ @@json ||= {
18
18
  "ok" => true,
19
19
  "_links" => {
20
20
  "self" => {
@@ -23,7 +23,6 @@ module PactBroker
23
23
  }
24
24
  }.to_json
25
25
  end
26
-
27
26
  end
28
27
  end
29
28
  end
@@ -47,4 +47,4 @@ Send a DELETE request to the webhook URL.
47
47
 
48
48
  ### Updating
49
49
 
50
- Currently not implemented. You will need to delete and re-create the webhook.
50
+ Send a PUT request to the webhook URL with all fields required for the new webhook.
@@ -12,4 +12,4 @@ Send a DELETE request to the webhook URL.
12
12
 
13
13
  ### Updating
14
14
 
15
- Currently not implemented. You will need to delete and re-create the webhook.
15
+ Send a PUT request to the webhook URL with all fields required for the new webhook.
@@ -47,4 +47,4 @@ Send a DELETE request to the webhook URL.
47
47
 
48
48
  ### Updating
49
49
 
50
- Currently not implemented. You will need to delete and re-create the webhook.
50
+ Send a PUT request to the webhook URL with all fields required for the new webhook.
@@ -1,22 +1,23 @@
1
1
  require 'pact_broker/verifications/verification_status'
2
+ require 'pact_broker/webhooks/status'
2
3
 
3
4
  module PactBroker
4
5
  module Domain
5
-
6
6
  class Relationship
7
7
 
8
8
  attr_reader :consumer, :provider, :latest_pact, :latest_verification, :webhooks
9
9
 
10
- def initialize consumer, provider, latest_pact = nil, latest_verification = nil, webhooks = []
10
+ def initialize consumer, provider, latest_pact = nil, latest_verification = nil, webhooks = [], triggered_webhooks = []
11
11
  @consumer = consumer
12
12
  @provider = provider
13
13
  @latest_pact = latest_pact
14
14
  @latest_verification = latest_verification
15
15
  @webhooks = webhooks
16
+ @triggered_webhooks = triggered_webhooks
16
17
  end
17
18
 
18
- def self.create consumer, provider, latest_pact, latest_verification, webhooks = []
19
- new consumer, provider, latest_pact, latest_verification, webhooks
19
+ def self.create consumer, provider, latest_pact, latest_verification, webhooks = [], triggered_webhooks = []
20
+ new consumer, provider, latest_pact, latest_verification, webhooks, triggered_webhooks
20
21
  end
21
22
 
22
23
  def eq? other
@@ -45,6 +46,14 @@ module PactBroker
45
46
  @webhooks.any?
46
47
  end
47
48
 
49
+ def webhook_status
50
+ @webhook_status ||= PactBroker::Webhooks::Status.new(@latest_pact, @webhooks, @triggered_webhooks).to_sym
51
+ end
52
+
53
+ def last_webhook_execution_date
54
+ @last_webhook_execution_date ||= @triggered_webhooks.any? ? @triggered_webhooks.sort{|a, b| a.created_at <=> b.created_at }.last.created_at : nil
55
+ end
56
+
48
57
  def verification_status
49
58
  @verification_status ||= PactBroker::Verifications::Status.new(@latest_pact, @latest_verification).to_sym
50
59
  end
@@ -52,10 +52,6 @@ module PactBroker
52
52
  .left_outer_join(:tags, {version_id: :consumer_version_id})
53
53
  .where(Sequel.qualify(:tags, :name) => nil)
54
54
  end
55
-
56
- def latest
57
- reverse_order(:consumer_version_order, :number).limit(1)
58
- end
59
55
  end
60
56
 
61
57
  def pact_version_sha
@@ -4,9 +4,7 @@ require 'pact_broker/logging'
4
4
  require 'pact_broker/api/contracts/webhook_contract'
5
5
 
6
6
  module PactBroker
7
-
8
7
  module Domain
9
-
10
8
  class Webhook
11
9
 
12
10
  include Messages
@@ -33,9 +31,9 @@ module PactBroker
33
31
  request && request.description
34
32
  end
35
33
 
36
- def execute
34
+ def execute options
37
35
  logger.info "Executing #{self}"
38
- request.execute
36
+ request.execute options
39
37
  end
40
38
 
41
39
  def to_s
@@ -50,7 +48,5 @@ module PactBroker
50
48
  provider && provider.name
51
49
  end
52
50
  end
53
-
54
51
  end
55
-
56
52
  end
@@ -11,7 +11,7 @@ module PactBroker
11
11
  end
12
12
 
13
13
  def success?
14
- !@response.nil? && @response.code.to_i < 400
14
+ !@response.nil? && @response.code.to_i < 300
15
15
  end
16
16
 
17
17
  def response
@@ -25,7 +25,6 @@ module PactBroker
25
25
  def logs
26
26
  @logs
27
27
  end
28
-
29
28
  end
30
29
  end
31
30
  end