pact_broker 2.107.1 → 2.108.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/Gemfile +5 -4
  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/released_versions_decorator.rb +2 -2
  42. data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +2 -2
  43. data/lib/pact_broker/api/decorators/validation_errors_decorator.rb +30 -0
  44. data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +4 -6
  45. data/lib/pact_broker/api/decorators/version_decorator.rb +5 -3
  46. data/lib/pact_broker/api/decorators/versions_decorator.rb +24 -14
  47. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +2 -0
  48. data/lib/pact_broker/api/middleware/configuration.rb +2 -0
  49. data/lib/pact_broker/api/pact_broker_urls.rb +18 -2
  50. data/lib/pact_broker/api/resources/after_reply.rb +15 -0
  51. data/lib/pact_broker/api/resources/all_webhooks.rb +3 -3
  52. data/lib/pact_broker/api/resources/badge_methods.rb +2 -1
  53. data/lib/pact_broker/api/resources/base_resource.rb +6 -4
  54. data/lib/pact_broker/api/resources/branch.rb +40 -0
  55. data/lib/pact_broker/api/resources/branch_versions.rb +59 -0
  56. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -1
  57. data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +1 -1
  58. data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +1 -1
  59. data/lib/pact_broker/api/resources/dashboard.rb +10 -0
  60. data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -1
  61. data/lib/pact_broker/api/resources/environments.rb +1 -1
  62. data/lib/pact_broker/api/resources/error_handler.rb +18 -52
  63. data/lib/pact_broker/api/resources/error_handling_methods.rb +40 -14
  64. data/lib/pact_broker/api/resources/error_response_generator.rb +23 -6
  65. data/lib/pact_broker/api/resources/event_methods.rb +15 -0
  66. data/lib/pact_broker/api/resources/filter_methods.rb +15 -0
  67. data/lib/pact_broker/api/resources/group.rb +11 -2
  68. data/lib/pact_broker/api/resources/index.rb +6 -0
  69. data/lib/pact_broker/api/resources/integrations.rb +18 -4
  70. data/lib/pact_broker/api/resources/latest_version.rb +2 -0
  71. data/lib/pact_broker/api/resources/pact.rb +11 -6
  72. data/lib/pact_broker/api/resources/pacticipant_branches.rb +67 -0
  73. data/lib/pact_broker/api/resources/pacticipants.rb +26 -7
  74. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +2 -2
  75. data/lib/pact_broker/api/resources/pagination_methods.rb +11 -1
  76. data/lib/pact_broker/api/resources/verifications.rb +9 -4
  77. data/lib/pact_broker/api/resources/versions.rb +12 -0
  78. data/lib/pact_broker/api.rb +5 -0
  79. data/lib/pact_broker/app.rb +10 -4
  80. data/lib/pact_broker/application_context.rb +29 -25
  81. data/lib/pact_broker/async/after_reply.rb +30 -0
  82. data/lib/pact_broker/config/runtime_configuration.rb +9 -21
  83. data/lib/pact_broker/config/runtime_configuration_coercion_methods.rb +32 -2
  84. data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
  85. data/lib/pact_broker/config/runtime_configuration_logging_methods.rb +15 -5
  86. data/lib/pact_broker/configuration.rb +29 -12
  87. data/lib/pact_broker/contracts/contracts_to_publish.rb +8 -0
  88. data/lib/pact_broker/contracts/service.rb +7 -1
  89. data/lib/pact_broker/dataset/page.rb +22 -0
  90. data/lib/pact_broker/dataset.rb +122 -0
  91. data/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb +47 -0
  92. data/lib/pact_broker/db/migrate_data.rb +1 -0
  93. data/lib/pact_broker/db/models.rb +1 -1
  94. data/lib/pact_broker/deployments/currently_deployed_version_id.rb +2 -5
  95. data/lib/pact_broker/deployments/deployed_version.rb +3 -3
  96. data/lib/pact_broker/deployments/environment.rb +0 -2
  97. data/lib/pact_broker/deployments/released_version.rb +4 -5
  98. data/lib/pact_broker/doc/views/index/pacticipant-branch.markdown +25 -0
  99. data/lib/pact_broker/domain/label.rb +6 -3
  100. data/lib/pact_broker/domain/pact.rb +10 -5
  101. data/lib/pact_broker/domain/pacticipant.rb +4 -34
  102. data/lib/pact_broker/domain/tag.rb +4 -5
  103. data/lib/pact_broker/domain/verification.rb +2 -4
  104. data/lib/pact_broker/domain/version.rb +12 -19
  105. data/lib/pact_broker/errors/error_reporter.rb +30 -0
  106. data/lib/pact_broker/errors.rb +2 -15
  107. data/lib/pact_broker/events/subscriber.rb +12 -4
  108. data/lib/pact_broker/feature_toggle.rb +1 -1
  109. data/lib/pact_broker/groups/service.rb +38 -5
  110. data/lib/pact_broker/index/service.rb +1 -2
  111. data/lib/pact_broker/integrations/event_listener.rb +23 -0
  112. data/lib/pact_broker/integrations/integration.rb +24 -2
  113. data/lib/pact_broker/integrations/repository.rb +34 -1
  114. data/lib/pact_broker/integrations/service.rb +17 -18
  115. data/lib/pact_broker/labels/repository.rb +4 -8
  116. data/lib/pact_broker/locale/en.yml +5 -0
  117. data/lib/pact_broker/matrix/every_row.rb +58 -40
  118. data/lib/pact_broker/matrix/integration_row.rb +95 -0
  119. data/lib/pact_broker/matrix/integrations_repository.rb +133 -0
  120. data/lib/pact_broker/matrix/matrix_row.rb +88 -0
  121. data/lib/pact_broker/matrix/matrix_row_dataset_module.rb +185 -0
  122. data/lib/pact_broker/matrix/matrix_row_instance_methods.rb +150 -0
  123. data/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +83 -0
  124. data/lib/pact_broker/matrix/parse_query.rb +1 -0
  125. data/lib/pact_broker/matrix/repository.rb +62 -285
  126. data/lib/pact_broker/matrix/resolved_selector.rb +13 -4
  127. data/lib/pact_broker/matrix/resolved_selector_builder.rb +84 -0
  128. data/lib/pact_broker/matrix/resolved_selectors_builder.rb +39 -0
  129. data/lib/pact_broker/matrix/row_ignorer.rb +36 -0
  130. data/lib/pact_broker/matrix/selector_ignorer.rb +59 -0
  131. data/lib/pact_broker/matrix/selector_resolver.rb +130 -0
  132. data/lib/pact_broker/metrics/service.rb +4 -9
  133. data/lib/pact_broker/pacticipants/latest_version_for_pacticipant_eager_loader.rb +33 -0
  134. data/lib/pact_broker/pacticipants/repository.rb +7 -9
  135. data/lib/pact_broker/pacticipants/service.rb +2 -2
  136. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +2 -4
  137. data/lib/pact_broker/pacts/metadata.rb +3 -1
  138. data/lib/pact_broker/pacts/pact_publication.rb +23 -5
  139. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +5 -1
  140. data/lib/pact_broker/pacts/pact_version.rb +2 -3
  141. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +2 -5
  142. data/lib/pact_broker/pacts/placeholder_pact.rb +3 -1
  143. data/lib/pact_broker/pacts/repository.rb +12 -12
  144. data/lib/pact_broker/pacts/service.rb +1 -1
  145. data/lib/pact_broker/repositories.rb +9 -1
  146. data/lib/pact_broker/string_refinements.rb +4 -0
  147. data/lib/pact_broker/tags/head_pact_tags.rb +2 -5
  148. data/lib/pact_broker/tags/repository.rb +3 -7
  149. data/lib/pact_broker/test/test_data_builder.rb +50 -1
  150. data/lib/pact_broker/ui/controllers/groups.rb +2 -1
  151. data/lib/pact_broker/ui/view_models/matrix_line.rb +4 -4
  152. data/lib/pact_broker/ui/views/groups/show.html.erb +2 -1
  153. data/lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb +0 -3
  154. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +2 -4
  155. data/lib/pact_broker/verifications/repository.rb +2 -5
  156. data/lib/pact_broker/verifications/sequence.rb +1 -4
  157. data/lib/pact_broker/verifications/service.rb +4 -0
  158. data/lib/pact_broker/version.rb +1 -1
  159. data/lib/pact_broker/versions/branch.rb +2 -5
  160. data/lib/pact_broker/versions/branch_head.rb +0 -3
  161. data/lib/pact_broker/versions/branch_repository.rb +76 -0
  162. data/lib/pact_broker/versions/branch_service.rb +13 -25
  163. data/lib/pact_broker/versions/branch_version.rb +0 -3
  164. data/lib/pact_broker/versions/branch_version_repository.rb +17 -0
  165. data/lib/pact_broker/versions/repository.rb +19 -2
  166. data/lib/pact_broker/versions/sequence.rb +1 -3
  167. data/lib/pact_broker/versions/service.rb +4 -0
  168. data/lib/pact_broker/webhooks/execution.rb +3 -7
  169. data/lib/pact_broker/webhooks/job.rb +16 -7
  170. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +2 -2
  171. data/lib/pact_broker/webhooks/repository.rb +0 -1
  172. data/lib/pact_broker/webhooks/trigger_service.rb +11 -12
  173. data/lib/pact_broker/webhooks/triggered_webhook.rb +3 -4
  174. data/lib/pact_broker/webhooks/webhook.rb +2 -2
  175. data/lib/pact_broker/webhooks/webhook_event.rb +2 -5
  176. data/lib/rack/pact_broker/add_cache_header.rb +14 -0
  177. data/lib/rack/pact_broker/application_context.rb +16 -0
  178. data/lib/rack/pact_broker/configurable_make_it_later.rb +1 -1
  179. data/lib/rack/pact_broker/invalid_uri_protection.rb +19 -3
  180. data/lib/webmachine/describe_routes.rb +55 -39
  181. data/lib/webmachine/render_error_monkey_patch.rb +13 -4
  182. data/pact_broker.gemspec +4 -4
  183. metadata +52 -29
  184. data/lib/pact_broker/api/decorators/decorator_context.rb +0 -22
  185. data/lib/pact_broker/matrix/query_builder.rb +0 -90
  186. data/lib/pact_broker/matrix/query_ids.rb +0 -40
  187. data/lib/pact_broker/matrix/quick_row.rb +0 -458
  188. data/lib/pact_broker/relationships/groupify.rb +0 -45
  189. data/lib/pact_broker/repositories/helpers.rb +0 -96
  190. data/lib/pact_broker/repositories/page.rb +0 -24
  191. data/lib/pact_broker/tags/tag_with_latest_flag.rb +0 -28
@@ -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
@@ -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}",