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,11 +1,17 @@
1
1
  require "pact_broker/api/resources/base_resource"
2
2
  require "pact_broker/api/renderers/integrations_dot_renderer"
3
3
  require "pact_broker/api/decorators/integrations_decorator"
4
+ require "pact_broker/api/resources/filter_methods"
5
+ require "pact_broker/api/resources/pagination_methods"
6
+ require "pact_broker/api/contracts/pagination_query_params_schema"
4
7
 
5
8
  module PactBroker
6
9
  module Api
7
10
  module Resources
8
11
  class Integrations < BaseResource
12
+ include PaginationMethods
13
+ include FilterMethods
14
+
9
15
  def content_types_provided
10
16
  [
11
17
  ["text/vnd.graphviz", :to_dot],
@@ -17,18 +23,20 @@ module PactBroker
17
23
  ["GET", "OPTIONS", "DELETE"]
18
24
  end
19
25
 
26
+ def malformed_request?
27
+ super || (request.get? && validation_errors_for_schema?(schema, request.query))
28
+ end
29
+
20
30
  def to_dot
31
+ integrations = integration_service.find_all(filter_options, pagination_options)
21
32
  PactBroker::Api::Renderers::IntegrationsDotRenderer.call(integrations)
22
33
  end
23
34
 
24
35
  def to_json
36
+ integrations = integration_service.find_all(filter_options, pagination_options, decorator_class(:integrations_decorator).eager_load_associations)
25
37
  decorator_class(:integrations_decorator).new(integrations).to_json(**decorator_options)
26
38
  end
27
39
 
28
- def integrations
29
- @integrations ||= integration_service.find_all
30
- end
31
-
32
40
  def delete_resource
33
41
  integration_service.delete_all
34
42
  true
@@ -37,6 +45,12 @@ module PactBroker
37
45
  def policy_name
38
46
  :'integrations::integrations'
39
47
  end
48
+
49
+ def schema
50
+ if request.get?
51
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema
52
+ end
53
+ end
40
54
  end
41
55
  end
42
56
  end
@@ -17,6 +17,8 @@ module PactBroker
17
17
  def version
18
18
  if identifier_from_path[:tag]
19
19
  @version ||= version_service.find_by_pacticipant_name_and_latest_tag(identifier_from_path[:pacticipant_name], identifier_from_path[:tag])
20
+ elsif identifier_from_path[:branch_name]
21
+ @version ||= version_service.find_latest_by_pacticipant_name_and_branch_name(identifier_from_path[:pacticipant_name], identifier_from_path[:branch_name])
20
22
  else
21
23
  @version ||= version_service.find_latest_by_pacticpant_name(identifier_from_path)
22
24
  end
@@ -9,14 +9,17 @@ require "pact_broker/api/contracts/put_pact_params_contract"
9
9
  require "pact_broker/webhooks/execution_configuration"
10
10
  require "pact_broker/api/resources/webhook_execution_methods"
11
11
  require "pact_broker/api/resources/pact_resource_methods"
12
+ require "pact_broker/api/resources/event_methods"
13
+ require "pact_broker/integrations/event_listener"
12
14
 
13
15
  module PactBroker
14
16
  module Api
15
17
  module Resources
16
18
  class Pact < BaseResource
19
+ include EventMethods
17
20
  include PacticipantResourceMethods
18
- include WebhookExecutionMethods
19
21
  include PactResourceMethods
22
+ include WebhookExecutionMethods
20
23
  include PactBroker::Messages
21
24
 
22
25
  def content_types_provided
@@ -65,11 +68,13 @@ module PactBroker
65
68
  def from_json
66
69
  response_code = pact ? 200 : 201
67
70
 
68
- handle_webhook_events do
69
- if request.patch? && resource_exists?
70
- @pact = pact_service.merge_pact(pact_params)
71
- else
72
- @pact = pact_service.create_or_update_pact(pact_params)
71
+ subscribe(PactBroker::Integrations::EventListener.new) do
72
+ handle_webhook_events do
73
+ if request.patch? && resource_exists?
74
+ @pact = pact_service.merge_pact(pact_params.merge(pact_version_sha: pact_version_sha))
75
+ else
76
+ @pact = pact_service.create_or_update_pact(pact_params.merge(pact_version_sha: pact_version_sha))
77
+ end
73
78
  end
74
79
  end
75
80
  response.body = to_json
@@ -109,7 +114,7 @@ module PactBroker
109
114
  end
110
115
 
111
116
  def disallowed_modification?
112
- if request.really_put? && pact_service.disallowed_modification?(pact, pact_params.json_content)
117
+ if request.really_put? && pact_service.disallowed_modification?(pact, pact_version_sha)
113
118
  message_params = { consumer_name: pact_params.consumer_name, consumer_version_number: pact_params.consumer_version_number, provider_name: pact_params.provider_name }
114
119
  set_json_error_message(message("errors.validation.pact_content_modification_not_allowed", message_params))
115
120
  true
@@ -121,6 +126,10 @@ module PactBroker
121
126
  def schema
122
127
  api_contract_class(:put_pact_params_contract)
123
128
  end
129
+
130
+ def pact_version_sha
131
+ @pact_version_sha ||= pact_service.generate_sha(pact_params.json_content)
132
+ end
124
133
  end
125
134
  end
126
135
  end
@@ -0,0 +1,67 @@
1
+ require "pact_broker/api/resources/base_resource"
2
+ require "pact_broker/api/resources/pagination_methods"
3
+ require "pact_broker/api/resources/filter_methods"
4
+ require "pact_broker/api/resources/after_reply"
5
+ require "rack/utils"
6
+
7
+ module PactBroker
8
+ module Api
9
+ module Resources
10
+ class PacticipantBranches < BaseResource
11
+ include PaginationMethods
12
+ include FilterMethods
13
+ include AfterReply
14
+
15
+ def content_types_provided
16
+ [["application/hal+json", :to_json]]
17
+ end
18
+
19
+ def allowed_methods
20
+ ["GET", "DELETE", "OPTIONS"]
21
+ end
22
+
23
+ def resource_exists?
24
+ !!pacticipant
25
+ end
26
+
27
+ def to_json
28
+ decorator_class(:pacticipant_branches_decorator).new(branches).to_json(**decorator_options(pacticipant: pacticipant))
29
+ end
30
+
31
+ def policy_name
32
+ :'versions::branches'
33
+ end
34
+
35
+ # Allows bulk deletion of pacticipant branches, keeping the specified branches and the main branch.
36
+ # Deletes the branches asyncronously, after the response has been sent, for performance reasons.
37
+ def delete_resource
38
+ after_reply do
39
+ branch_service.delete_branches_for_pacticipant(pacticipant, exclude: exclude)
40
+ end
41
+ notices = branch_service.branch_deletion_notices(pacticipant, exclude: exclude)
42
+ response.body = decorator_class(:notices_decorator).new(notices).to_json(**decorator_options)
43
+ 202
44
+ end
45
+
46
+ private
47
+
48
+ def branches
49
+ @branches ||= branch_service.find_all_branches_for_pacticipant(
50
+ pacticipant,
51
+ filter_options,
52
+ default_pagination_options.merge(pagination_options),
53
+ eager_load_associations
54
+ )
55
+ end
56
+
57
+ def exclude
58
+ Rack::Utils.parse_nested_query(request.uri.query)["exclude"] || []
59
+ end
60
+
61
+ def eager_load_associations
62
+ decorator_class(:pacticipant_branches_decorator).eager_load_associations
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -12,7 +12,6 @@ module PactBroker
12
12
  using PactBroker::HashRefinements
13
13
  include PaginationMethods
14
14
 
15
-
16
15
  def content_types_provided
17
16
  [["application/hal+json", :to_json]]
18
17
  end
@@ -26,7 +25,15 @@ module PactBroker
26
25
  end
27
26
 
28
27
  def malformed_request?
29
- super || (request.post? && validation_errors_for_schema?)
28
+ if super
29
+ true
30
+ elsif request.post? && validation_errors_for_schema?
31
+ true
32
+ elsif request.get? && validation_errors_for_schema?(schema, request.query)
33
+ true
34
+ else
35
+ false
36
+ end
30
37
  end
31
38
 
32
39
  def request_body_required?
@@ -47,11 +54,11 @@ module PactBroker
47
54
  end
48
55
 
49
56
  def to_json
50
- generate_json(pacticipant_service.find_all_pacticipants(pagination_options))
57
+ generate_json(pacticipant_service.find_all_pacticipants(filter_options, pagination_options, eager_load_associations))
51
58
  end
52
59
 
53
60
  def generate_json pacticipants
54
- decorator_class(:deprecated_pacticipant_collection_decorator).new(pacticipants).to_json(**decorator_options)
61
+ decorator_class(:deprecated_pacticipants_decorator).new(pacticipants).to_json(**decorator_options)
55
62
  end
56
63
 
57
64
  def decorator_for model
@@ -69,11 +76,23 @@ module PactBroker
69
76
  private
70
77
 
71
78
  def schema
72
- PactBroker::Api::Contracts::PacticipantCreateSchema
79
+ if request.get?
80
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema
81
+ else
82
+ PactBroker::Api::Contracts::PacticipantCreateSchema
83
+ end
84
+ end
85
+
86
+ def eager_load_associations
87
+ decorator_class(:deprecated_pacticipants_decorator).eager_load_associations
73
88
  end
74
89
 
75
- def pacticipants
76
- @pacticipants ||= pacticipant_service.find_all_pacticipants
90
+ def filter_options
91
+ if (request.query.has_key?("q"))
92
+ { query_string: request.query["q"] }
93
+ else
94
+ {}
95
+ end
77
96
  end
78
97
  end
79
98
  end
@@ -1,5 +1,5 @@
1
1
  require "pact_broker/api/resources/base_resource"
2
- require "pact_broker/api/decorators/pacticipant_collection_decorator"
2
+ require "pact_broker/api/decorators/pacticipants_decorator"
3
3
 
4
4
  module PactBroker
5
5
  module Api
@@ -19,7 +19,7 @@ module PactBroker
19
19
  end
20
20
 
21
21
  def generate_json pacticipants
22
- decorator_class(:pacticipant_collection_decorator).new(pacticipants).to_json(**decorator_options)
22
+ decorator_class(:pacticipants_decorator).new(pacticipants).to_json(**decorator_options)
23
23
  end
24
24
 
25
25
  def policy_name
@@ -2,8 +2,14 @@ module PactBroker
2
2
  module Api
3
3
  module Resources
4
4
  module PaginationMethods
5
+ # rubocop: disable Metrics/CyclomaticComplexity
5
6
  def pagination_options
6
- if request.query["pageNumber"] || request.query["pageSize"]
7
+ if request.query["page"] || request.query["size"]
8
+ {
9
+ page_number: request.query["page"]&.to_i || 1,
10
+ page_size: request.query["size"]&.to_i || 100
11
+ }
12
+ elsif request.query["pageNumber"] || request.query["pageSize"]
7
13
  {
8
14
  page_number: request.query["pageNumber"]&.to_i || 1,
9
15
  page_size: request.query["pageSize"]&.to_i || 100
@@ -12,6 +18,10 @@ module PactBroker
12
18
  {}
13
19
  end
14
20
  end
21
+
22
+ def default_pagination_options
23
+ { page_number: 1, page_size: 100 }
24
+ end
15
25
  end
16
26
  end
17
27
  end
@@ -53,7 +53,7 @@ module PactBroker
53
53
  private
54
54
 
55
55
  def parsed_contracts
56
- @parsed_contracts ||= decorator_class(:publish_contracts_decorator).new(PactBroker::Contracts::ContractsToPublish.new).from_hash(params)
56
+ @parsed_contracts ||= decorator_class(:publish_contracts_decorator).new(PactBroker::Contracts::ContractsToPublish.new).from_hash(params, { user_options: { sha_generator: PactBroker.configuration.sha_generator } } )
57
57
  end
58
58
 
59
59
  def params
@@ -5,6 +5,8 @@ require "pact_broker/api/contracts/verification_contract"
5
5
  require "pact_broker/api/decorators/verification_decorator"
6
6
  require "pact_broker/api/resources/webhook_execution_methods"
7
7
  require "pact_broker/api/resources/metadata_resource_methods"
8
+ require "pact_broker/api/resources/event_methods"
9
+ require "pact_broker/integrations/event_listener"
8
10
 
9
11
  module PactBroker
10
12
  module Api
@@ -12,6 +14,7 @@ module PactBroker
12
14
  class Verifications < BaseResource
13
15
  include WebhookExecutionMethods
14
16
  include MetadataResourceMethods
17
+ include EventMethods
15
18
 
16
19
  def content_types_accepted
17
20
  [["application/json", :from_json]]
@@ -42,10 +45,12 @@ module PactBroker
42
45
  end
43
46
 
44
47
  def from_json
45
- handle_webhook_events(build_url: verification_params["buildUrl"]) do
46
- verified_pacts = pact_service.find_for_verification_publication(pact_params, event_context[:consumer_version_selectors])
47
- verification = verification_service.create(next_verification_number, verification_params, verified_pacts, event_context)
48
- response.body = decorator_for(verification).to_json(**decorator_options)
48
+ subscribe(PactBroker::Integrations::EventListener.new) do
49
+ handle_webhook_events(build_url: verification_params["buildUrl"]) do
50
+ verified_pacts = pact_service.find_for_verification_publication(pact_params, event_context[:consumer_version_selectors])
51
+ verification = verification_service.create(next_verification_number, verification_params, verified_pacts, event_context)
52
+ response.body = decorator_for(verification).to_json(**decorator_options)
53
+ end
49
54
  end
50
55
  true
51
56
  end
@@ -18,6 +18,10 @@ module PactBroker
18
18
  ["GET", "OPTIONS"]
19
19
  end
20
20
 
21
+ def malformed_request?
22
+ super || request.get? && validation_errors_for_schema?(schema, request.query)
23
+ end
24
+
21
25
  def resource_exists?
22
26
  !!pacticipant
23
27
  end
@@ -33,6 +37,14 @@ module PactBroker
33
37
  def policy_name
34
38
  :'versions::versions'
35
39
  end
40
+
41
+ private
42
+
43
+ def schema
44
+ if request.get?
45
+ PactBroker::Api::Contracts::PaginationQueryParamsSchema
46
+ end
47
+ end
36
48
  end
37
49
  end
38
50
  end
@@ -64,6 +64,7 @@ module PactBroker
64
64
  # Latest pacts
65
65
  add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest"], Api::Resources::LatestPact, {resource_name: "latest_pact_publication"}
66
66
  add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest", :tag], Api::Resources::LatestPact, {resource_name: "latest_tagged_pact_publication"}
67
+ add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "branch", :branch_name, "latest"], Api::Resources::LatestPact, {resource_name: "latest_pact_publication_for_branch"}
67
68
  add ["pacts", "provider", :provider_name], Api::Resources::ProviderPacts, {resource_name: "provider_pact_publications"}
68
69
  add ["pacts", "provider", :provider_name, "tag", :tag], Api::Resources::ProviderPacts, {resource_name: "tagged_provider_pact_publications"}
69
70
  add ["pacts", "provider", :provider_name, "consumer", :consumer_name, "latest-untagged"], Api::Resources::LatestPact, {resource_name: "latest_untagged_pact_publication", tag: :untagged}
@@ -86,12 +87,16 @@ module PactBroker
86
87
 
87
88
  # Versions
88
89
  add ["pacticipants", :pacticipant_name, "versions"], Api::Resources::Versions, {resource_name: "pacticipant_versions"}
90
+ add ["pacticipants", :pacticipant_name, "branches", :branch_name, "versions"], Api::Resources::BranchVersions, {resource_name: "pacticipant_branch_versions"}
89
91
  add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number], Api::Resources::Version, {resource_name: "pacticipant_version"}
90
92
  add ["pacticipants", :pacticipant_name, "latest-version", :tag], Api::Resources::LatestVersion, {resource_name: "latest_tagged_pacticipant_version"}
91
93
  add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to], Api::Resources::CanIDeployPacticipantVersionByTagToTag, { resource_name: "can_i_deploy_latest_tagged_version_to_tag" }
92
94
  add ["pacticipants", :pacticipant_name, "latest-version", :tag, "can-i-deploy", "to", :to, "badge"], Api::Resources::CanIDeployPacticipantVersionByTagToTagBadge, { resource_name: "can_i_deploy_latest_tagged_version_to_tag_badge" }
93
95
  add ["pacticipants", :pacticipant_name, "latest-version"], Api::Resources::LatestVersion, {resource_name: "latest_pacticipant_version"}
94
96
  add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "tags", :tag_name], Api::Resources::Tag, {resource_name: "pacticipant_version_tag"}
97
+ add ["pacticipants", :pacticipant_name, "branches"], Api::Resources::PacticipantBranches, {resource_name: "pacticipant_branches"}
98
+ add ["pacticipants", :pacticipant_name, "branches", :branch_name], Api::Resources::Branch, { resource_name: "branch" }
99
+ add ["pacticipants", :pacticipant_name, "branches", :branch_name, "latest-version"], Api::Resources::LatestVersion, { resource_name: "latest_pacticipant_version_for_branch" }
95
100
  add ["pacticipants", :pacticipant_name, "branches", :branch_name, "versions", :version_number], Api::Resources::BranchVersion, { resource_name: "branch_version" }
96
101
  add ["pacticipants", :pacticipant_name, "branches", :branch_name, "latest-version", "can-i-deploy", "to-environment", :environment_name], Api::Resources::CanIDeployPacticipantVersionByBranchToEnvironment, { resource_name: "can_i_deploy_latest_branch_version_to_environment" }
97
102
  add ["pacticipants", :pacticipant_name, "branches", :branch_name, "latest-version", "can-i-deploy", "to-environment", :environment_name, "badge"], Api::Resources::CanIDeployPacticipantVersionByBranchToEnvironmentBadge, { resource_name: "can_i_deploy_latest_branch_version_to_environment_badge" }
@@ -1,8 +1,3 @@
1
- # Must be defined before loading Padrino
2
- PADRINO_LOGGER ||= {
3
- ENV.fetch("RACK_ENV", "production").to_sym => { log_level: :error, stream: :stdout, format_datetime: "%Y-%m-%dT%H:%M:%S.000%:z" }
4
- }
5
-
6
1
  require "pact_broker/configuration"
7
2
  require "pact_broker/db"
8
3
  require "pact_broker/initializers/database_connection"
@@ -21,14 +16,17 @@ require "rack/pact_broker/ui_authentication"
21
16
  require "rack/pact_broker/configurable_make_it_later"
22
17
  require "rack/pact_broker/no_auth"
23
18
  require "rack/pact_broker/reset_thread_data"
19
+ require "rack/pact_broker/add_cache_header"
24
20
  require "rack/pact_broker/add_vary_header"
25
21
  require "rack/pact_broker/use_when"
22
+ require "rack/pact_broker/application_context"
26
23
  require "sucker_punch"
27
24
  require "pact_broker/api/middleware/configuration"
28
25
  require "pact_broker/api/middleware/basic_auth"
29
26
  require "pact_broker/config/basic_auth_configuration"
30
27
  require "pact_broker/api/authorization/resource_access_policy"
31
28
  require "pact_broker/api/middleware/http_debug_logs"
29
+ require "pact_broker/application_context"
32
30
 
33
31
  module PactBroker
34
32
 
@@ -36,15 +34,16 @@ module PactBroker
36
34
  include PactBroker::Logging
37
35
  using Rack::PactBroker::UseWhen
38
36
 
39
- attr_accessor :configuration
37
+ attr_accessor :configuration, :application_context
40
38
 
41
- def initialize
39
+ def initialize(application_context = PactBroker::ApplicationContext.default_application_context)
40
+ @application_context = application_context
42
41
  @app_builder = ::Rack::Builder.new
43
42
  @cascade_apps = []
44
43
  @make_it_later_api_auth = ::Rack::PactBroker::ConfigurableMakeItLater.new(Rack::PactBroker::NoAuth)
45
44
  @make_it_later_ui_auth = ::Rack::PactBroker::ConfigurableMakeItLater.new(Rack::PactBroker::NoAuth)
46
45
  # Can only be required after database connection has been made because the decorators rely on the Sequel models
47
- @create_pact_broker_api_block = ->() { require "pact_broker/api"; PactBroker::API }
46
+ @create_pact_broker_api_block = ->() { require "pact_broker/api"; PactBroker.build_api(application_context) }
48
47
  @configuration = PactBroker.configuration
49
48
  yield configuration if block_given?
50
49
  post_configure
@@ -55,7 +54,7 @@ module PactBroker
55
54
  @configuration.freeze
56
55
  end
57
56
 
58
- # Allows middleware to be inserted at the bottom of the shared middlware stack
57
+ # Allows middleware to be inserted at the bottom of the shared middleware stack
59
58
  # (ie just before the cascade is called for diagnostic, UI and API).
60
59
  # To insert middleware at the top of the stack, initialize
61
60
  # the middleware with the app, and run it manually.
@@ -177,12 +176,14 @@ module PactBroker
177
176
  @app_builder.use PactBroker::Api::Middleware::HttpDebugLogs if configuration.http_debug_logging_enabled
178
177
  configure_basic_auth
179
178
  configure_rack_protection
179
+ @app_builder.use Rack::PactBroker::ApplicationContext, application_context
180
180
  @app_builder.use Rack::PactBroker::InvalidUriProtection
181
181
  @app_builder.use Rack::PactBroker::ResetThreadData
182
182
  @app_builder.use Rack::PactBroker::AddPactBrokerVersionHeader
183
183
  @app_builder.use Rack::PactBroker::AddVaryHeader
184
184
  @app_builder.use Rack::Static, :urls => ["/stylesheets", "/css", "/fonts", "/js", "/javascripts", "/images"], :root => PactBroker.project_root.join("public")
185
185
  @app_builder.use Rack::Static, :urls => ["/favicon.ico"], :root => PactBroker.project_root.join("public/images"), header_rules: [[:all, {"Content-Type" => "image/x-icon"}]]
186
+ @app_builder.use Rack::PactBroker::AddCacheHeader
186
187
  @app_builder.use Rack::PactBroker::ConvertFileExtensionToAcceptHeader
187
188
  # Rack::PactBroker::SetBaseUrl needs to be before the Rack::PactBroker::HalBrowserRedirect
188
189
  @app_builder.use Rack::PactBroker::SetBaseUrl, configuration.base_urls
@@ -1,10 +1,3 @@
1
- require "pact_broker/api/decorators/configuration"
2
- require "pact_broker/api/contracts/configuration"
3
- require "pact_broker/api/decorators/decorator_context_creator"
4
- require "pact_broker/webhooks/execution_configuration_creator"
5
- require "pact_broker/errors/error_logger"
6
- require "pact_broker/api/resources/error_response_generator"
7
-
8
1
  module PactBroker
9
2
  class ApplicationContext
10
3
  attr_reader :decorator_configuration,
@@ -15,32 +8,43 @@ module PactBroker
15
8
  :before_resource,
16
9
  :after_resource,
17
10
  :error_logger,
11
+ :error_reporter,
18
12
  :error_response_generator
19
13
 
20
- def initialize(params = {})
21
- params_with_defaults = {
14
+ def initialize(params)
15
+ @decorator_configuration = params[:decorator_configuration]
16
+ @api_contract_configuration = params[:api_contract_configuration]
17
+ @decorator_context_creator = params[:decorator_context_creator]
18
+ @webhook_execution_configuration_creator = params[:webhook_execution_configuration_creator]
19
+ @resource_authorizer = params[:resource_authorizer]
20
+ @before_resource = params[:before_resource]
21
+ @after_resource = params[:after_resource]
22
+ @error_logger = params[:error_logger]
23
+ @error_reporter = params[:error_reporter]
24
+ @error_response_generator = params[:error_response_generator]
25
+ end
26
+
27
+ # TODO pass in configuration
28
+ def self.default_application_context(overrides = {})
29
+ require "pact_broker/api/decorators/configuration"
30
+ require "pact_broker/api/contracts/configuration"
31
+ require "pact_broker/api/decorators/decorator_context_creator"
32
+ require "pact_broker/webhooks/execution_configuration_creator"
33
+ require "pact_broker/errors/error_logger"
34
+ require "pact_broker/errors/error_reporter"
35
+ require "pact_broker/api/resources/error_response_generator"
36
+
37
+ params = {
22
38
  decorator_configuration: PactBroker::Api::Decorators::Configuration.default_configuration,
23
39
  api_contract_configuration: PactBroker::Api::Contracts::Configuration.default_configuration,
24
40
  decorator_context_creator: PactBroker::Api::Decorators::DecoratorContextCreator,
25
41
  webhook_execution_configuration_creator: PactBroker::Webhooks::ExecutionConfigurationCreator,
26
42
  error_logger: PactBroker::Errors::ErrorLogger,
27
- error_response_generator: PactBroker::Api::Resources::ErrorResponseGenerator
28
- }.merge(params)
43
+ error_reporter: PactBroker::Errors::ErrorReporter.new(PactBroker.configuration.api_error_reporters),
44
+ error_response_generator: PactBroker::Api::Resources::ErrorResponseGenerator,
45
+ }.merge(overrides)
29
46
 
30
- @decorator_configuration = params_with_defaults[:decorator_configuration]
31
- @api_contract_configuration = params_with_defaults[:api_contract_configuration]
32
- @decorator_context_creator = params_with_defaults[:decorator_context_creator]
33
- @webhook_execution_configuration_creator = params_with_defaults[:webhook_execution_configuration_creator]
34
- @resource_authorizer = params_with_defaults[:resource_authorizer]
35
- @before_resource = params_with_defaults[:before_resource]
36
- @after_resource = params_with_defaults[:after_resource]
37
- @error_logger = params_with_defaults[:error_logger]
38
- @error_response_generator = params_with_defaults[:error_response_generator]
39
-
40
- end
41
-
42
- def self.default_application_context(overrides = {})
43
- ApplicationContext.new(overrides)
47
+ ApplicationContext.new(params)
44
48
  end
45
49
  end
46
50
  end
@@ -0,0 +1,30 @@
1
+ # Saves a block for execution after the HTTP response has been sent to the user.
2
+ # When the block is executed, it connects to the database before executing the code.
3
+ # This is good for doing things that might take a while and don't have to be done before
4
+ # the response is sent, and don't need retries (in which case, it might be better to use a SuckerPunch Job).
5
+ #
6
+ # This leverages a feature of Puma which I'm not sure is meant to be public or not.
7
+ # There are serveral mentions of it on the internet, so I assume it's ok to use it.
8
+ # Puma itself uses the rack.after_reply for http request logging.
9
+ #
10
+ # https://github.com/search?q=repo%3Apuma%2Fpuma%20rack.after_reply&type=code
11
+
12
+ module PactBroker
13
+ module Async
14
+ class AfterReply
15
+ def initialize(rack_env)
16
+ @rack_env = rack_env
17
+ @database_connector = rack_env.fetch("pactbroker.database_connector")
18
+ end
19
+
20
+ def execute(&block)
21
+ dc = @database_connector
22
+ @rack_env["rack.after_reply"] << lambda {
23
+ dc.call do
24
+ block.call
25
+ end
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -31,6 +31,8 @@ module PactBroker
31
31
  include RuntimeConfigurationDatabaseMethods
32
32
  include RuntimeConfigurationBasicAuthMethods
33
33
 
34
+ config_name :pact_broker
35
+
34
36
  # logging attributes
35
37
  attr_config(
36
38
  log_dir: File.expand_path("./log"),
@@ -91,7 +93,8 @@ module PactBroker
91
93
  allow_dangerous_contract_modification: false,
92
94
  semver_formats: ["%M.%m.%p%s%d", "%M.%m", "%M"],
93
95
  seed_example_data: true,
94
- features: []
96
+ network_diagram_max_pacticipants: 150,
97
+ features: {}
95
98
  )
96
99
 
97
100
  def self.getter_and_setter_method_names
@@ -105,8 +108,11 @@ module PactBroker
105
108
  config_attributes + config_attributes.collect{ |k| "#{k}=".to_sym } + extra_methods - [:base_url]
106
109
  end
107
110
 
108
- config_name :pact_broker
109
-
111
+ coerce_types(
112
+ features: COERCE_FEATURES,
113
+ network_diagram_max_pacticipants: :integer,
114
+ webhook_certificates: COERCE_WEBHOOKS
115
+ )
110
116
  sensitive_values(:database_url, :database_password)
111
117
 
112
118
  def log_level= log_level
@@ -179,10 +185,6 @@ module PactBroker
179
185
  super(value_to_string_array(main_branch_candidates, "main_branch_candidates"))
180
186
  end
181
187
 
182
- def features= features
183
- super(value_to_string_array(features, "features").collect(&:downcase))
184
- end
185
-
186
188
  def rack_protection_use= rack_protection_use
187
189
  super(value_to_string_array(rack_protection_use, "rack_protection_use")&.collect(&:to_sym))
188
190
  end
@@ -191,20 +193,6 @@ module PactBroker
191
193
  super(value_to_string_array(rack_protection_except, "rack_protection_except")&.collect(&:to_sym))
192
194
  end
193
195
 
194
- def webhook_certificates= webhook_certificates
195
- if webhook_certificates.is_a?(Array)
196
- super(webhook_certificates.collect(&:symbolize_keys))
197
- elsif webhook_certificates.is_a?(Hash)
198
- if all_keys_are_number_strings?(webhook_certificates)
199
- super(convert_hash_with_number_string_keys_to_array(webhook_certificates).collect(&:symbolize_keys))
200
- else
201
- raise_validation_error("webhook_certificates must be an array, or a hash where each key is an integer in string format.")
202
- end
203
- elsif !webhook_certificates.nil?
204
- raise_validation_error("webhook_certificates cannot be set using a #{webhook_certificates.class}")
205
- end
206
- end
207
-
208
196
  def warning_error_classes
209
197
  warning_error_class_names.collect do | class_name |
210
198
  begin