pact_broker 2.107.1 → 2.109.0

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/Gemfile +5 -6
  4. data/README.md +1 -0
  5. data/db/migrations/20230615_add_integrations_contract_data_updated_at.rb +13 -0
  6. data/db/migrations/20230616_set_integrations_contract_data_updated_at.rb +11 -0
  7. data/db/migrations/20231002_add_version_id_index_to_released_version.rb +21 -0
  8. data/db/migrations/20231003_add_version_id_index_to_deployed_version.rb +21 -0
  9. data/docs/CONFIGURATION.md +10 -0
  10. data/lib/pact_broker/api/contracts/base_contract.rb +2 -1
  11. data/lib/pact_broker/api/contracts/dry_validation_errors_formatter.rb +2 -0
  12. data/lib/pact_broker/api/contracts/pagination_query_params_schema.rb +19 -0
  13. data/lib/pact_broker/api/contracts/publish_contracts_contract_contract.rb +29 -18
  14. data/lib/pact_broker/api/decorators/base_decorator.rb +40 -1
  15. data/lib/pact_broker/api/decorators/branch_decorator.rb +35 -0
  16. data/lib/pact_broker/api/decorators/branch_version_decorator.rb +16 -0
  17. data/lib/pact_broker/api/decorators/configuration.rb +19 -0
  18. data/lib/pact_broker/api/decorators/custom_error_problem_json_decorator.rb +1 -1
  19. data/lib/pact_broker/api/decorators/decorator_context_creator.rb +47 -2
  20. data/lib/pact_broker/api/decorators/deployed_version_decorator.rb +2 -1
  21. data/lib/pact_broker/api/decorators/deployed_versions_decorator.rb +2 -2
  22. data/lib/pact_broker/api/decorators/dry_validation_errors_decorator.rb +32 -0
  23. data/lib/pact_broker/api/decorators/dry_validation_errors_problem_json_decorator.rb +24 -0
  24. data/lib/pact_broker/api/decorators/embedded_branch_version_decorator.rb +2 -2
  25. data/lib/pact_broker/api/decorators/embedded_deployed_version_decorator.rb +30 -0
  26. data/lib/pact_broker/api/decorators/embedded_error_problem_json_decorator.rb +84 -0
  27. data/lib/pact_broker/api/decorators/embedded_released_version_decorator.rb +27 -0
  28. data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +0 -3
  29. data/lib/pact_broker/api/decorators/error_decorator.rb +30 -0
  30. data/lib/pact_broker/api/decorators/extended_pact_decorator.rb +6 -1
  31. data/lib/pact_broker/api/decorators/integration_decorator.rb +3 -2
  32. data/lib/pact_broker/api/decorators/integrations_decorator.rb +3 -0
  33. data/lib/pact_broker/api/decorators/notices_decorator.rb +11 -0
  34. data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +1 -5
  35. data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +0 -1
  36. data/lib/pact_broker/api/decorators/pact_webhooks_status_decorator.rb +0 -1
  37. data/lib/pact_broker/api/decorators/pacticipant_branches_decorator.rb +32 -0
  38. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +11 -1
  39. data/lib/pact_broker/api/decorators/{pacticipant_collection_decorator.rb → pacticipants_decorator.rb} +8 -4
  40. data/lib/pact_broker/api/decorators/pagination_links.rb +2 -2
  41. data/lib/pact_broker/api/decorators/publish_contract_decorator.rb +6 -1
  42. data/lib/pact_broker/api/decorators/released_versions_decorator.rb +2 -2
  43. data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +2 -2
  44. data/lib/pact_broker/api/decorators/validation_errors_decorator.rb +30 -0
  45. data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +4 -6
  46. data/lib/pact_broker/api/decorators/version_decorator.rb +5 -3
  47. data/lib/pact_broker/api/decorators/versions_decorator.rb +24 -14
  48. data/lib/pact_broker/api/decorators/webhook_decorator.rb +1 -1
  49. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +2 -0
  50. data/lib/pact_broker/api/middleware/configuration.rb +2 -0
  51. data/lib/pact_broker/api/pact_broker_urls.rb +18 -2
  52. data/lib/pact_broker/api/resources/after_reply.rb +15 -0
  53. data/lib/pact_broker/api/resources/all_webhooks.rb +3 -3
  54. data/lib/pact_broker/api/resources/badge_methods.rb +2 -1
  55. data/lib/pact_broker/api/resources/base_resource.rb +6 -4
  56. data/lib/pact_broker/api/resources/branch.rb +40 -0
  57. data/lib/pact_broker/api/resources/branch_versions.rb +59 -0
  58. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -1
  59. data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +1 -1
  60. data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +1 -1
  61. data/lib/pact_broker/api/resources/dashboard.rb +10 -0
  62. data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -1
  63. data/lib/pact_broker/api/resources/environment.rb +4 -0
  64. data/lib/pact_broker/api/resources/environments.rb +1 -1
  65. data/lib/pact_broker/api/resources/error_handler.rb +18 -52
  66. data/lib/pact_broker/api/resources/error_handling_methods.rb +40 -14
  67. data/lib/pact_broker/api/resources/error_response_generator.rb +23 -6
  68. data/lib/pact_broker/api/resources/event_methods.rb +15 -0
  69. data/lib/pact_broker/api/resources/filter_methods.rb +15 -0
  70. data/lib/pact_broker/api/resources/group.rb +11 -2
  71. data/lib/pact_broker/api/resources/index.rb +6 -0
  72. data/lib/pact_broker/api/resources/integrations.rb +18 -4
  73. data/lib/pact_broker/api/resources/latest_version.rb +2 -0
  74. data/lib/pact_broker/api/resources/pact.rb +16 -7
  75. data/lib/pact_broker/api/resources/pacticipant_branches.rb +67 -0
  76. data/lib/pact_broker/api/resources/pacticipants.rb +26 -7
  77. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +2 -2
  78. data/lib/pact_broker/api/resources/pagination_methods.rb +11 -1
  79. data/lib/pact_broker/api/resources/publish_contracts.rb +1 -1
  80. data/lib/pact_broker/api/resources/verifications.rb +9 -4
  81. data/lib/pact_broker/api/resources/versions.rb +12 -0
  82. data/lib/pact_broker/api.rb +5 -0
  83. data/lib/pact_broker/app.rb +10 -9
  84. data/lib/pact_broker/application_context.rb +29 -25
  85. data/lib/pact_broker/async/after_reply.rb +30 -0
  86. data/lib/pact_broker/config/runtime_configuration.rb +9 -21
  87. data/lib/pact_broker/config/runtime_configuration_coercion_methods.rb +32 -2
  88. data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
  89. data/lib/pact_broker/config/runtime_configuration_logging_methods.rb +15 -5
  90. data/lib/pact_broker/configuration.rb +29 -12
  91. data/lib/pact_broker/contracts/contract_to_publish.rb +4 -5
  92. data/lib/pact_broker/contracts/contracts_to_publish.rb +8 -0
  93. data/lib/pact_broker/contracts/service.rb +9 -3
  94. data/lib/pact_broker/dataset/page.rb +22 -0
  95. data/lib/pact_broker/dataset.rb +122 -0
  96. data/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb +47 -0
  97. data/lib/pact_broker/db/migrate_data.rb +1 -0
  98. data/lib/pact_broker/db/models.rb +1 -1
  99. data/lib/pact_broker/deployments/currently_deployed_version_id.rb +2 -5
  100. data/lib/pact_broker/deployments/deployed_version.rb +3 -3
  101. data/lib/pact_broker/deployments/environment.rb +0 -2
  102. data/lib/pact_broker/deployments/released_version.rb +4 -5
  103. data/lib/pact_broker/doc/views/index/pacticipant-branch.markdown +25 -0
  104. data/lib/pact_broker/domain/label.rb +6 -3
  105. data/lib/pact_broker/domain/pact.rb +10 -5
  106. data/lib/pact_broker/domain/pacticipant.rb +4 -34
  107. data/lib/pact_broker/domain/tag.rb +4 -5
  108. data/lib/pact_broker/domain/verification.rb +2 -4
  109. data/lib/pact_broker/domain/version.rb +12 -19
  110. data/lib/pact_broker/errors/error_reporter.rb +30 -0
  111. data/lib/pact_broker/errors.rb +2 -15
  112. data/lib/pact_broker/events/subscriber.rb +12 -4
  113. data/lib/pact_broker/feature_toggle.rb +1 -1
  114. data/lib/pact_broker/groups/service.rb +38 -5
  115. data/lib/pact_broker/index/service.rb +1 -2
  116. data/lib/pact_broker/integrations/event_listener.rb +23 -0
  117. data/lib/pact_broker/integrations/integration.rb +24 -2
  118. data/lib/pact_broker/integrations/repository.rb +34 -1
  119. data/lib/pact_broker/integrations/service.rb +17 -18
  120. data/lib/pact_broker/labels/repository.rb +4 -8
  121. data/lib/pact_broker/locale/en.yml +5 -0
  122. data/lib/pact_broker/logging.rb +10 -0
  123. data/lib/pact_broker/matrix/every_row.rb +58 -40
  124. data/lib/pact_broker/matrix/integration_row.rb +95 -0
  125. data/lib/pact_broker/matrix/integrations_repository.rb +133 -0
  126. data/lib/pact_broker/matrix/matrix_row.rb +88 -0
  127. data/lib/pact_broker/matrix/matrix_row_dataset_module.rb +185 -0
  128. data/lib/pact_broker/matrix/matrix_row_instance_methods.rb +150 -0
  129. data/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +83 -0
  130. data/lib/pact_broker/matrix/parse_query.rb +1 -0
  131. data/lib/pact_broker/matrix/repository.rb +62 -285
  132. data/lib/pact_broker/matrix/resolved_selector.rb +13 -4
  133. data/lib/pact_broker/matrix/resolved_selector_builder.rb +84 -0
  134. data/lib/pact_broker/matrix/resolved_selectors_builder.rb +39 -0
  135. data/lib/pact_broker/matrix/row_ignorer.rb +36 -0
  136. data/lib/pact_broker/matrix/selector_ignorer.rb +59 -0
  137. data/lib/pact_broker/matrix/selector_resolver.rb +130 -0
  138. data/lib/pact_broker/metrics/service.rb +4 -9
  139. data/lib/pact_broker/pacticipants/latest_version_for_pacticipant_eager_loader.rb +33 -0
  140. data/lib/pact_broker/pacticipants/repository.rb +7 -9
  141. data/lib/pact_broker/pacticipants/service.rb +2 -2
  142. data/lib/pact_broker/pacts/generate_sha.rb +9 -4
  143. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +2 -4
  144. data/lib/pact_broker/pacts/metadata.rb +3 -1
  145. data/lib/pact_broker/pacts/pact_params.rb +1 -1
  146. data/lib/pact_broker/pacts/pact_publication.rb +23 -5
  147. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +5 -1
  148. data/lib/pact_broker/pacts/pact_version.rb +2 -3
  149. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +2 -5
  150. data/lib/pact_broker/pacts/placeholder_pact.rb +3 -1
  151. data/lib/pact_broker/pacts/repository.rb +12 -12
  152. data/lib/pact_broker/pacts/service.rb +12 -13
  153. data/lib/pact_broker/repositories.rb +9 -1
  154. data/lib/pact_broker/string_refinements.rb +4 -0
  155. data/lib/pact_broker/tags/head_pact_tags.rb +2 -5
  156. data/lib/pact_broker/tags/repository.rb +3 -7
  157. data/lib/pact_broker/test/test_data_builder.rb +57 -2
  158. data/lib/pact_broker/ui/controllers/base_controller.rb +4 -2
  159. data/lib/pact_broker/ui/controllers/groups.rb +2 -1
  160. data/lib/pact_broker/ui/view_models/matrix_line.rb +4 -4
  161. data/lib/pact_broker/ui/views/groups/show.html.erb +2 -1
  162. data/lib/pact_broker/ui.rb +20 -9
  163. data/lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb +0 -3
  164. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +2 -4
  165. data/lib/pact_broker/verifications/repository.rb +2 -5
  166. data/lib/pact_broker/verifications/sequence.rb +1 -4
  167. data/lib/pact_broker/verifications/service.rb +4 -0
  168. data/lib/pact_broker/version.rb +1 -1
  169. data/lib/pact_broker/versions/branch.rb +2 -5
  170. data/lib/pact_broker/versions/branch_head.rb +0 -3
  171. data/lib/pact_broker/versions/branch_repository.rb +76 -0
  172. data/lib/pact_broker/versions/branch_service.rb +13 -25
  173. data/lib/pact_broker/versions/branch_version.rb +0 -3
  174. data/lib/pact_broker/versions/branch_version_repository.rb +17 -0
  175. data/lib/pact_broker/versions/repository.rb +19 -2
  176. data/lib/pact_broker/versions/sequence.rb +1 -3
  177. data/lib/pact_broker/versions/service.rb +4 -0
  178. data/lib/pact_broker/webhooks/execution.rb +3 -7
  179. data/lib/pact_broker/webhooks/job.rb +16 -7
  180. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +2 -2
  181. data/lib/pact_broker/webhooks/repository.rb +0 -1
  182. data/lib/pact_broker/webhooks/trigger_service.rb +11 -12
  183. data/lib/pact_broker/webhooks/triggered_webhook.rb +3 -4
  184. data/lib/pact_broker/webhooks/webhook.rb +2 -2
  185. data/lib/pact_broker/webhooks/webhook_event.rb +2 -5
  186. data/lib/rack/pact_broker/add_cache_header.rb +14 -0
  187. data/lib/rack/pact_broker/application_context.rb +16 -0
  188. data/lib/rack/pact_broker/configurable_make_it_later.rb +1 -1
  189. data/lib/rack/pact_broker/invalid_uri_protection.rb +19 -3
  190. data/lib/webmachine/describe_routes.rb +55 -39
  191. data/lib/webmachine/render_error_monkey_patch.rb +13 -4
  192. data/pact_broker.gemspec +5 -5
  193. metadata +54 -31
  194. data/lib/pact_broker/api/decorators/decorator_context.rb +0 -22
  195. data/lib/pact_broker/matrix/query_builder.rb +0 -90
  196. data/lib/pact_broker/matrix/query_ids.rb +0 -40
  197. data/lib/pact_broker/matrix/quick_row.rb +0 -458
  198. data/lib/pact_broker/relationships/groupify.rb +0 -45
  199. data/lib/pact_broker/repositories/helpers.rb +0 -96
  200. data/lib/pact_broker/repositories/page.rb +0 -24
  201. data/lib/pact_broker/tags/tag_with_latest_flag.rb +0 -28
@@ -1,5 +1,7 @@
1
1
  require "pact_broker/logging"
2
2
 
3
+ # Allows the load-time configuration to be overridden on a per-request basis (for Pactflow)
4
+
3
5
  module PactBroker
4
6
  module Api
5
7
  module Middleware
@@ -227,8 +227,24 @@ module PactBroker
227
227
  "#{tags_url(base_url, tag.version)}/#{url_encode(tag.name)}"
228
228
  end
229
229
 
230
+ def branch_url(branch, base_url = "")
231
+ "#{pacticipant_branches_url(branch.pacticipant, base_url)}/#{url_encode(branch.name)}"
232
+ end
233
+
234
+ def pacticipant_branches_url(pacticipant, base_url = "")
235
+ "#{pacticipant_url(base_url, pacticipant)}/branches"
236
+ end
237
+
238
+ def branch_versions_url(branch, base_url = "")
239
+ "#{branch_url(branch, base_url)}/versions"
240
+ end
241
+
230
242
  def branch_version_url(branch_version, base_url = "")
231
- "#{pacticipant_url(base_url, branch_version.pacticipant)}/branches/#{url_encode(branch_version.branch_name)}/versions/#{url_encode(branch_version.version_number)}"
243
+ "#{branch_versions_url(branch_version.branch, base_url)}/#{url_encode(branch_version.version_number)}"
244
+ end
245
+
246
+ def latest_version_for_branch_url(branch, base_url = "")
247
+ "#{branch_url(branch, base_url)}/latest-version"
232
248
  end
233
249
 
234
250
  def templated_tag_url_for_pacticipant pacticipant_name, base_url = ""
@@ -271,7 +287,7 @@ module PactBroker
271
287
  "#{base_url}/webhooks"
272
288
  end
273
289
 
274
- def webhook_url uuid, base_url
290
+ def webhook_url uuid, base_url = ""
275
291
  "#{base_url}/webhooks/#{uuid}"
276
292
  end
277
293
 
@@ -0,0 +1,15 @@
1
+ require "pact_broker/async/after_reply"
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ module AfterReply
7
+
8
+ # @param [Callable] block the block to execute after the response has been sent to the user.
9
+ def after_reply(&block)
10
+ PactBroker::Async::AfterReply.new(request.env).execute(&block)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -38,7 +38,7 @@ module PactBroker
38
38
  end
39
39
 
40
40
  def from_json
41
- saved_webhook = webhook_service.create(next_uuid, webhook, consumer, provider)
41
+ saved_webhook = webhook_service.create(next_uuid, webhook, webhook_consumer, webhook_provider)
42
42
  response.body = decorator_class(:webhook_decorator).new(saved_webhook).to_json(**decorator_options)
43
43
  end
44
44
 
@@ -60,11 +60,11 @@ module PactBroker
60
60
  api_contract_class(:webhook_contract)
61
61
  end
62
62
 
63
- def consumer
63
+ def webhook_consumer
64
64
  webhook.consumer&.name ? pacticipant_service.find_pacticipant_by_name(webhook.consumer.name) : nil
65
65
  end
66
66
 
67
- def provider
67
+ def webhook_provider
68
68
  webhook.provider&.name ? pacticipant_service.find_pacticipant_by_name(webhook.provider.name) : nil
69
69
  end
70
70
 
@@ -37,7 +37,8 @@ module PactBroker
37
37
  badge_url
38
38
  rescue StandardError => e
39
39
  # Want to render a badge, even if there's an error
40
- badge_service.error_badge_url("error", ErrorResponseGenerator.display_message(e, "reference: #{PactBroker::Errors.generate_error_reference}"))
40
+ error_reference = log_and_report_error(e)
41
+ badge_service.error_badge_url("error", "reference: #{error_reference}")
41
42
  end
42
43
  end
43
44
 
@@ -10,6 +10,7 @@ require "pact_broker/pacts/pact_params"
10
10
  require "pact_broker/api/resources/authentication"
11
11
  require "pact_broker/api/resources/authorization"
12
12
  require "pact_broker/errors"
13
+ require "pact_broker/messages"
13
14
  require "pact_broker/api/resources/error_handling_methods"
14
15
  require "pact_broker/api/contracts/utf_8_validation"
15
16
 
@@ -26,7 +27,7 @@ module PactBroker
26
27
  include PactBroker::Api::Resources::Authorization
27
28
  include PactBroker::Api::Resources::ErrorHandlingMethods
28
29
  include PactBroker::Api::Contracts::UTF8Validation
29
-
30
+ include PactBroker::Messages
30
31
  include PactBroker::Logging
31
32
 
32
33
  attr_accessor :user
@@ -206,7 +207,7 @@ module PactBroker
206
207
  def find_pacticipant name, role
207
208
  pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
208
209
  if pacticipant.nil?
209
- set_json_error_message("No #{role} with name '#{name}' found", title: "Not found", type: "not_found", status: 404)
210
+ set_json_error_message("No #{role} with name '#{name}' found", title: "Not found", type: "not-found", status: 404)
210
211
  end
211
212
  end
212
213
  end
@@ -257,8 +258,9 @@ module PactBroker
257
258
  end
258
259
 
259
260
  def validation_errors_for_schema?(schema_to_use = schema, params_to_validate = params)
260
- if (errors = schema_to_use.call(params_to_validate)).any?
261
- set_json_validation_error_messages(errors)
261
+ result = schema_to_use.call(params_to_validate)
262
+ if result.errors.any?
263
+ set_json_validation_error_messages(result.errors)
262
264
  true
263
265
  else
264
266
  false
@@ -0,0 +1,40 @@
1
+ require "pact_broker/api/resources/base_resource"
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ class Branch < BaseResource
7
+ def content_types_provided
8
+ [["application/hal+json", :to_json]]
9
+ end
10
+
11
+ def allowed_methods
12
+ ["GET", "DELETE", "OPTIONS"]
13
+ end
14
+
15
+ def resource_exists?
16
+ !!branch
17
+ end
18
+
19
+ def to_json
20
+ decorator_class(:branch_decorator).new(branch).to_json(**decorator_options)
21
+ end
22
+
23
+ def delete_resource
24
+ branch_service.delete_branch(branch)
25
+ true
26
+ end
27
+
28
+ def policy_name
29
+ :'versions::branch'
30
+ end
31
+
32
+ private
33
+
34
+ def branch
35
+ @branch_version ||= branch_service.find_branch(**identifier_from_path.slice(:pacticipant_name, :branch_name))
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ require "pact_broker/api/resources/base_resource"
2
+ require "pact_broker/configuration"
3
+ require "pact_broker/api/decorators/versions_decorator"
4
+ require "pact_broker/api/resources/pagination_methods"
5
+
6
+
7
+ module PactBroker
8
+ module Api
9
+ module Resources
10
+ class BranchVersions < BaseResource
11
+ include PaginationMethods
12
+
13
+ def content_types_provided
14
+ [["application/hal+json", :to_json]]
15
+ end
16
+
17
+ def allowed_methods
18
+ ["GET", "OPTIONS"]
19
+ end
20
+
21
+ def malformed_request?
22
+ super || request.get? && validation_errors_for_schema?(schema, request.query)
23
+ end
24
+
25
+ def resource_exists?
26
+ !!branch
27
+ end
28
+
29
+ def to_json
30
+ decorator_class(:versions_decorator).new(versions).to_json(**decorator_options(identifier_from_path.merge(resource_title: resource_title)))
31
+ end
32
+
33
+ def versions
34
+ @versions ||= version_service.find_pacticipant_versions_in_reverse_order(pacticipant_name, { branch_name: identifier_from_path[:branch_name] }, pagination_options)
35
+ end
36
+
37
+ def policy_name
38
+ :'versions::versions'
39
+ end
40
+
41
+ def branch
42
+ @branch ||= branch_service.find_branch(**identifier_from_path.slice(:pacticipant_name, :branch_name))
43
+ end
44
+
45
+ def resource_title
46
+ "Versions for branch #{branch.name} of #{branch.pacticipant.name}"
47
+ end
48
+
49
+ private
50
+
51
+ def schema
52
+ if request.get?
53
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -14,7 +14,7 @@ module PactBroker
14
14
  elsif pacticipant.nil?
15
15
  badge_service.error_badge_url("pacticipant", "not found")
16
16
  elsif version.nil?
17
- if branch_service.find_branch(identifier_from_path.slice(:pacticipant_name, :branch_name)).nil?
17
+ if branch_service.find_branch(**identifier_from_path.slice(:pacticipant_name, :branch_name)).nil?
18
18
  badge_service.error_badge_url("branch", "not found")
19
19
  else
20
20
  badge_service.error_badge_url("version", "not found")
@@ -21,7 +21,7 @@ module PactBroker
21
21
  end
22
22
 
23
23
  def to_json
24
- decorator_class(decorator_name).new(deployed_versions).to_json(**decorator_options(title: title))
24
+ decorator_class(decorator_name).new(deployed_versions).to_json(**decorator_options(title: title, expand: [:pacticipant, :version]))
25
25
  end
26
26
 
27
27
  def policy_name
@@ -21,7 +21,7 @@ module PactBroker
21
21
  end
22
22
 
23
23
  def to_json
24
- decorator_class(decorator_name).new(released_versions).to_json(**decorator_options(title: title))
24
+ decorator_class(decorator_name).new(released_versions).to_json(**decorator_options(title: title, expand: [:pacticipant, :version]))
25
25
  end
26
26
 
27
27
  def policy_name
@@ -20,6 +20,10 @@ module PactBroker
20
20
  ["GET", "OPTIONS"]
21
21
  end
22
22
 
23
+ def malformed_request?
24
+ super || (request.get? && validation_errors_for_schema?(schema, request.query))
25
+ end
26
+
23
27
  def to_json
24
28
  decorator_class(:dashboard_decorator).new(index_items).to_json(**decorator_options)
25
29
  end
@@ -34,6 +38,12 @@ module PactBroker
34
38
 
35
39
  private
36
40
 
41
+ def schema
42
+ if request.get?
43
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema
44
+ end
45
+ end
46
+
37
47
  def index_items
38
48
  index_service.find_index_items_for_api(**identifier_from_path.merge(pagination_options))
39
49
  end
@@ -78,7 +78,7 @@ module PactBroker
78
78
  end
79
79
 
80
80
  def title
81
- "Deployed versions for #{pacticipant_name} version #{pacticipant_version_number}"
81
+ "Deployed versions for #{pacticipant_name} version #{pacticipant_version_number} in environment #{environment.display_name}"
82
82
  end
83
83
  end
84
84
  end
@@ -42,6 +42,10 @@ module PactBroker
42
42
  :'deployments::environment'
43
43
  end
44
44
 
45
+ def policy_record
46
+ environment
47
+ end
48
+
45
49
  def to_json
46
50
  decorator_class(:environment_decorator).new(environment).to_json(**decorator_options)
47
51
  end
@@ -60,7 +60,7 @@ module PactBroker
60
60
 
61
61
  def environments
62
62
  @environments ||= if request.query["name"]
63
- [environment_service.find_by_name(request.query["name"])]
63
+ [environment_service.find_by_name(request.query["name"])].compact
64
64
  else
65
65
  environment_service.find_all
66
66
  end
@@ -1,70 +1,36 @@
1
1
  require "pact_broker/configuration"
2
2
  require "securerandom"
3
3
 
4
+ # Logs the error
5
+ # Reports the error
6
+ # Generates and returns response headers and response body
7
+
4
8
  module PactBroker
5
9
  module Api
6
10
  module Resources
7
11
  class ErrorHandler
8
12
  include PactBroker::Logging
9
13
 
10
- def self.call e, request, response
11
- error_reference = generate_error_reference
12
- if log_as_warning?(e)
13
- logger.warn("Error reference #{error_reference}", e)
14
- elsif reportable?(e)
15
- log_error(e, "Error reference #{error_reference}")
16
- report(e, error_reference, request)
17
- else
18
- logger.info("Error reference #{error_reference}", e)
19
- end
20
- response.body = response_body_hash(e, error_reference).to_json
14
+ def initialize(error_logger, error_response_generator, error_reporter)
15
+ @error_logger = error_logger
16
+ @error_response_generator = error_response_generator
17
+ @error_reporter = error_reporter
21
18
  end
22
19
 
23
- def self.generate_error_reference
24
- SecureRandom.urlsafe_base64.gsub(/[^a-z]/i, "")[0,10]
25
- end
20
+ def call(error, env, message = nil)
21
+ error_reference = PactBroker::Errors.generate_error_reference
26
22
 
27
- def self.reportable?(e)
28
- !e.is_a?(PactBroker::Error) && !e.is_a?(JSON::JSONError)
29
- end
23
+ # log error
24
+ error_logger.call(error, error_reference, env)
30
25
 
31
- def self.log_as_warning?(e)
32
- PactBroker.configuration.warning_error_classes.any? { |clazz| e.is_a?(clazz) || e.cause&.is_a?(clazz) }
33
- end
26
+ # report error
27
+ error_reporter.call(error, error_reference, env)
34
28
 
35
- def self.display_message(e, obfuscated_message)
36
- if PactBroker.configuration.show_backtrace_in_error_response?
37
- e.message || obfuscated_message
38
- else
39
- reportable?(e) ? obfuscated_message : e.message
40
- end
41
- end
42
-
43
- def self.obfuscated_error_message error_reference
44
- "An error has occurred. The details have been logged with the reference #{error_reference}"
45
- end
46
-
47
- def self.response_body_hash e, error_reference
48
- response_body = {
49
- error: {
50
- message: display_message(e, obfuscated_error_message(error_reference)),
51
- reference: error_reference
52
- }
53
- }
54
- if PactBroker.configuration.show_backtrace_in_error_response?
55
- response_body[:error][:backtrace] = e.backtrace
56
- end
57
- response_body
58
- end
59
29
 
60
- def self.report e, error_reference, request
61
- PactBroker.configuration.api_error_reporters.each do | error_notifier |
62
- begin
63
- error_notifier.call(e, env: request.env, error_reference: error_reference)
64
- rescue StandardError => e
65
- log_error(e, "Error executing api_error_reporter")
66
- end
67
- end
30
+ # generate response
31
+ headers, body = error_response_generator.call(error, error_reference, env, message: message)
32
+ headers.each { | key, value | response.headers[key] = value }
33
+ return headers, body
68
34
  end
69
35
  end
70
36
  end
@@ -7,29 +7,39 @@ module PactBroker
7
7
  module ErrorHandlingMethods
8
8
 
9
9
  # @override
10
+ # @param [StandardError] error
10
11
  def handle_exception(error)
11
- error_reference = PactBroker::Errors.generate_error_reference
12
- application_context.error_logger.call(error, error_reference, request.env)
13
- if PactBroker::Errors.reportable_error?(error)
14
- PactBroker::Errors.report(error, error_reference, request.env)
15
- end
12
+ error_reference = log_and_report_error(error)
16
13
  headers, body = application_context.error_response_generator.call(error, error_reference, request.env)
17
14
  headers.each { | key, value | response.headers[key] = value }
18
15
  response.body = body
19
16
  end
20
17
 
21
- def set_json_error_message detail, title: "Server error", type: "server_error", status: 500
18
+ # @param [StandardError] error
19
+ def log_and_report_error(error)
20
+ # generate reference
21
+ error_reference = PactBroker::Errors.generate_error_reference
22
+ # log error
23
+ application_context.error_logger.call(error, error_reference, request.env)
24
+ # report error
25
+ application_context.error_reporter.call(error, error_reference, request.env)
26
+ # generate response
27
+ error_reference
28
+ end
29
+
30
+ # @param [String] detail
31
+ # @param [String] title
32
+ # @param [String] type
33
+ # @param [Integer] status
34
+ def set_json_error_message(detail, title: "Server error", type: "server-error", status: 500)
22
35
  response.headers["Content-Type"] = error_response_content_type
23
36
  response.body = error_response_body(detail, title, type, status)
24
37
  end
25
38
 
26
- def set_json_validation_error_messages errors
39
+ # @param [Hash,Dry::Validation::MessageSet] errors
40
+ def set_json_validation_error_messages(errors)
27
41
  response.headers["Content-Type"] = error_response_content_type
28
- if problem_json_error_content_type?
29
- response.body = PactBroker::Api::Decorators::ValidationErrorsProblemJSONDecorator.new(errors).to_json(**decorator_options)
30
- else
31
- response.body = { errors: errors }.to_json
32
- end
42
+ response.body = validation_errors_response_body(errors)
33
43
  end
34
44
 
35
45
  def error_response_content_type
@@ -42,15 +52,31 @@ module PactBroker
42
52
 
43
53
  def error_response_body(detail, title, type, status)
44
54
  if problem_json_error_content_type?
45
- PactBroker::Api::Decorators::CustomErrorProblemJSONDecorator.new(detail: detail, title: title, type: type, status: status).to_json(**decorator_options)
55
+ decorator_class(:custom_error_problem_json_decorator).new(detail: detail, title: title, type: type, status: status).to_json(**decorator_options_for_error)
46
56
  else
47
- { error: detail }.to_json
57
+ decorator_class(:error_decorator).new(detail).to_json
48
58
  end
49
59
  end
50
60
 
61
+ # @param [Hash,Dry::Validation::MessageSet] errors
62
+ def validation_errors_response_body(errors)
63
+ validation_errors_decorator_class(errors).new(errors).to_json(**decorator_options_for_error)
64
+ end
65
+
66
+ # @param [Hash,Dry::Validation::MessageSet] errors
67
+ def validation_errors_decorator_class(errors)
68
+ application_context.decorator_configuration.validation_error_decorator_class_for(errors.class, request.headers["Accept"])
69
+ end
70
+
51
71
  def problem_json_error_content_type?
52
72
  request.headers["Accept"]&.include?("application/problem+json")
53
73
  end
74
+
75
+ # If we use the normal decorator options that have policy objects we can get into recursive loops in Pactflow, so just make a simple variant of the
76
+ # decorator options here
77
+ def decorator_options_for_error
78
+ { user_options: { base_url: base_url } }
79
+ end
54
80
  end
55
81
  end
56
82
  end
@@ -1,23 +1,36 @@
1
1
  require "pact_broker/configuration"
2
2
  require "pact_broker/api/decorators/runtime_error_problem_json_decorator"
3
+ require "pact_broker/errors"
4
+ require "pact_broker/messages"
5
+
6
+ # Generates the response headers and body for use when there is a runtime
7
+ # error in the business logic (services and repositories) when executing a Webmachine resource request.
8
+ # Obfuscates any exception messages that might expose vulnerablities in production.
9
+ # Uses the Accept header to determine whether to return application/problem+json
10
+ # or application/hal+json, for backwards compatibility.
11
+ # In the next major version of the Pact Broker, all error responses
12
+ # should use problem+json, regardless of Accept headers.
3
13
 
4
14
  module PactBroker
5
15
  module Api
6
16
  module Resources
7
17
  class ErrorResponseGenerator
8
18
  include PactBroker::Logging
19
+ extend PactBroker::Messages
9
20
 
10
21
  # @param error [StandardError]
11
22
  # @param error_reference [String] an error reference to display to the user
12
23
  # @param env [Hash] the rack env
13
24
  # @return [Hash, String] the response headers to set, the response body to set
14
- def self.call error, error_reference, env = {}
15
- body = response_body_hash(error, error_reference, env, display_message(error, obfuscated_error_message(error_reference)))
25
+ def self.call(error, error_reference, env, message: nil)
26
+ body = response_body_hash(error, error_reference, env, display_message(error, message, obfuscated_error_message(error_reference)))
16
27
  return headers(env), body.to_json
17
28
  end
18
29
 
19
- def self.display_message(error, obfuscated_message)
20
- if PactBroker.configuration.show_backtrace_in_error_response?
30
+ def self.display_message(error, message, obfuscated_message)
31
+ if message
32
+ message
33
+ elsif PactBroker.configuration.show_backtrace_in_error_response?
21
34
  error.message || obfuscated_message
22
35
  else
23
36
  PactBroker::Errors.reportable_error?(error) ? obfuscated_message : error.message
@@ -46,11 +59,11 @@ module PactBroker
46
59
  end
47
60
 
48
61
  private_class_method def self.problem_json_response_body(message, env)
49
- PactBroker::Api::Decorators::RuntimeErrorProblemJSONDecorator.new(message).to_hash(user_options: { base_url: env["pactbroker.base_url" ] })
62
+ error_decorator_class(env).new(message).to_hash(user_options: { base_url: env["pactbroker.base_url" ] })
50
63
  end
51
64
 
52
65
  private_class_method def self.obfuscated_error_message(error_reference)
53
- "An error has occurred. The details have been logged with the reference #{error_reference}"
66
+ message("errors.runtime.with_error_reference", error_reference: error_reference)
54
67
  end
55
68
 
56
69
  private_class_method def self.headers(env)
@@ -64,6 +77,10 @@ module PactBroker
64
77
  private_class_method def self.problem_json?(env)
65
78
  env["HTTP_ACCEPT"]&.include?("application/problem+json")
66
79
  end
80
+
81
+ private_class_method def self.error_decorator_class(env)
82
+ env["pactbroker.application_context"].decorator_configuration.class_for(:runtime_error_problem_json_decorator)
83
+ end
67
84
  end
68
85
  end
69
86
  end
@@ -0,0 +1,15 @@
1
+ require "pact_broker/events/subscriber"
2
+
3
+ module PactBroker
4
+ module Api
5
+ module Resources
6
+ module EventMethods
7
+ def subscribe(listener)
8
+ PactBroker::Events.subscribe(listener) do
9
+ yield
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module PactBroker
2
+ module Api
3
+ module Resources
4
+ module FilterMethods
5
+ def filter_options
6
+ if request.query.has_key?("q")
7
+ { query_string: request.query["q"] }
8
+ else
9
+ {}
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,4 @@
1
+ require "pact_broker/string_refinements"
1
2
  require "pact_broker/api/resources/base_resource"
2
3
  require "pact_broker/api/decorators/relationships_csv_decorator"
3
4
 
@@ -5,6 +6,8 @@ module PactBroker
5
6
  module Api
6
7
  module Resources
7
8
  class Group < BaseResource
9
+ using PactBroker::StringRefinements
10
+
8
11
  def content_types_provided
9
12
  [["text/csv", :to_csv]]
10
13
  end
@@ -32,9 +35,15 @@ module PactBroker
32
35
  private
33
36
 
34
37
  def group
35
- @group ||= group_service.find_group_containing(pacticipant)
38
+ @group ||= group_service.find_group_containing(pacticipant, max_pacticipants: max_pacticipants)
39
+ end
40
+
41
+ def max_pacticipants
42
+ if request.query["maxPacticipants"]&.integer?
43
+ request.query["maxPacticipants"].to_i
44
+ end
36
45
  end
37
46
  end
38
47
  end
39
48
  end
40
- end
49
+ end
@@ -116,6 +116,12 @@ module PactBroker
116
116
  title: "Get, create or delete a tag for a pacticipant version",
117
117
  templated: true
118
118
  },
119
+ "pb:pacticipant-branch" =>
120
+ {
121
+ href: base_url + "/pacticipants/{pacticipant}/branches/{branch}",
122
+ title: "Get or delete a pacticipant branch",
123
+ templated: true
124
+ },
119
125
  "pb:pacticipant-branch-version" =>
120
126
  {
121
127
  href: base_url + "/pacticipants/{pacticipant}/branches/{branch}/versions/{version}",