pact_broker 2.107.1 → 2.108.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 (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
@@ -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)
75
+ else
76
+ @pact = pact_service.create_or_update_pact(pact_params)
77
+ end
73
78
  end
74
79
  end
75
80
  response.body = to_json
@@ -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
@@ -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" }
@@ -21,14 +21,17 @@ require "rack/pact_broker/ui_authentication"
21
21
  require "rack/pact_broker/configurable_make_it_later"
22
22
  require "rack/pact_broker/no_auth"
23
23
  require "rack/pact_broker/reset_thread_data"
24
+ require "rack/pact_broker/add_cache_header"
24
25
  require "rack/pact_broker/add_vary_header"
25
26
  require "rack/pact_broker/use_when"
27
+ require "rack/pact_broker/application_context"
26
28
  require "sucker_punch"
27
29
  require "pact_broker/api/middleware/configuration"
28
30
  require "pact_broker/api/middleware/basic_auth"
29
31
  require "pact_broker/config/basic_auth_configuration"
30
32
  require "pact_broker/api/authorization/resource_access_policy"
31
33
  require "pact_broker/api/middleware/http_debug_logs"
34
+ require "pact_broker/application_context"
32
35
 
33
36
  module PactBroker
34
37
 
@@ -36,15 +39,16 @@ module PactBroker
36
39
  include PactBroker::Logging
37
40
  using Rack::PactBroker::UseWhen
38
41
 
39
- attr_accessor :configuration
42
+ attr_accessor :configuration, :application_context
40
43
 
41
- def initialize
44
+ def initialize(application_context = PactBroker::ApplicationContext.default_application_context)
45
+ @application_context = application_context
42
46
  @app_builder = ::Rack::Builder.new
43
47
  @cascade_apps = []
44
48
  @make_it_later_api_auth = ::Rack::PactBroker::ConfigurableMakeItLater.new(Rack::PactBroker::NoAuth)
45
49
  @make_it_later_ui_auth = ::Rack::PactBroker::ConfigurableMakeItLater.new(Rack::PactBroker::NoAuth)
46
50
  # 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 }
51
+ @create_pact_broker_api_block = ->() { require "pact_broker/api"; PactBroker.build_api(application_context) }
48
52
  @configuration = PactBroker.configuration
49
53
  yield configuration if block_given?
50
54
  post_configure
@@ -55,7 +59,7 @@ module PactBroker
55
59
  @configuration.freeze
56
60
  end
57
61
 
58
- # Allows middleware to be inserted at the bottom of the shared middlware stack
62
+ # Allows middleware to be inserted at the bottom of the shared middleware stack
59
63
  # (ie just before the cascade is called for diagnostic, UI and API).
60
64
  # To insert middleware at the top of the stack, initialize
61
65
  # the middleware with the app, and run it manually.
@@ -177,12 +181,14 @@ module PactBroker
177
181
  @app_builder.use PactBroker::Api::Middleware::HttpDebugLogs if configuration.http_debug_logging_enabled
178
182
  configure_basic_auth
179
183
  configure_rack_protection
184
+ @app_builder.use Rack::PactBroker::ApplicationContext, application_context
180
185
  @app_builder.use Rack::PactBroker::InvalidUriProtection
181
186
  @app_builder.use Rack::PactBroker::ResetThreadData
182
187
  @app_builder.use Rack::PactBroker::AddPactBrokerVersionHeader
183
188
  @app_builder.use Rack::PactBroker::AddVaryHeader
184
189
  @app_builder.use Rack::Static, :urls => ["/stylesheets", "/css", "/fonts", "/js", "/javascripts", "/images"], :root => PactBroker.project_root.join("public")
185
190
  @app_builder.use Rack::Static, :urls => ["/favicon.ico"], :root => PactBroker.project_root.join("public/images"), header_rules: [[:all, {"Content-Type" => "image/x-icon"}]]
191
+ @app_builder.use Rack::PactBroker::AddCacheHeader
186
192
  @app_builder.use Rack::PactBroker::ConvertFileExtensionToAcceptHeader
187
193
  # Rack::PactBroker::SetBaseUrl needs to be before the Rack::PactBroker::HalBrowserRedirect
188
194
  @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
@@ -1,15 +1,45 @@
1
1
  require "pact_broker/config/space_delimited_string_list"
2
2
  require "pact_broker/config/space_delimited_integer_list"
3
+ require "pact_broker/hash_refinements"
4
+ require "pact_broker/error"
3
5
 
4
6
  module PactBroker
5
7
  module Config
6
8
  module RuntimeConfigurationCoercionMethods
7
9
 
8
- def all_keys_are_number_strings?(hash)
10
+ using PactBroker::HashRefinements
11
+
12
+ COERCE_FEATURES = lambda { | value |
13
+ if value.is_a?(String)
14
+ value.split(" ").each_with_object({}) { | k, h | h[k.downcase.to_sym] = true }
15
+ elsif value.is_a?(Array)
16
+ value.each_with_object({}) { | k, h | h[k.downcase.to_sym] = true }
17
+ elsif value.is_a?(Hash)
18
+ value.each_with_object({}) { | (k, v), new_hash | new_hash[k.downcase.to_sym] = Anyway::AutoCast.call(v) }
19
+ else
20
+ raise PactBroker::ConfigurationError, "Expected a String, Hash or Array for features but got a #{value.class.name}"
21
+ end
22
+ }
23
+
24
+ COERCE_WEBHOOKS = lambda { | value |
25
+ if value.is_a?(Hash) # from env vars
26
+ if RuntimeConfigurationCoercionMethods.all_keys_are_number_strings?(value)
27
+ RuntimeConfigurationCoercionMethods.convert_hash_with_number_string_keys_to_array(value).collect(&:symbolize_keys)
28
+ else
29
+ raise PactBroker::ConfigurationError, "Could not coerce #{value} into an array of webhook configurations. Please check docs for the expected format."
30
+ end
31
+ elsif value.is_a?(Array) # from YAML
32
+ value.collect(&:symbolize_keys)
33
+ else
34
+ raise PactBroker::ConfigurationError, "Webhook certificates cannot be set using a #{value.class}"
35
+ end
36
+ }
37
+
38
+ def self.all_keys_are_number_strings?(hash)
9
39
  hash.keys.all? { | k | k.to_s.to_i.to_s == k } # is an integer as a string
10
40
  end
11
41
 
12
- def convert_hash_with_number_string_keys_to_array(hash)
42
+ def self.convert_hash_with_number_string_keys_to_array(hash)
13
43
  hash.keys.collect{ |k| [k, k.to_i]}.sort_by(&:last).collect(&:first).collect do | key |
14
44
  hash[key]
15
45
  end
@@ -27,7 +27,7 @@ module PactBroker
27
27
  allow_missing_migration_files: true,
28
28
  validate_database_connection_config: true,
29
29
  database_statement_timeout: 15,
30
- metrics_sql_statement_timeout: 30,
30
+ metrics_sql_statement_timeout: 30, # TODO get rid of this in next major version
31
31
  database_connection_validation_timeout: nil
32
32
  )
33
33