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
@@ -1,26 +1,9 @@
1
- require "pact_broker/db"
1
+ require "pact_broker/dataset"
2
2
  require "pact_broker/domain/order_versions"
3
- require "pact_broker/repositories/helpers"
4
- require "pact_broker/tags/tag_with_latest_flag"
5
3
  require "pact_broker/versions/eager_loaders"
6
4
 
7
5
  module PactBroker
8
6
  module Domain
9
-
10
- # Same attributes as PactBroker::Tags::TagWithLatestFlag
11
- class EagerTagWithLatestFlag < SimpleDelegator
12
- attr_reader :latest
13
-
14
- def initialize(tag, latest)
15
- super(tag)
16
- @latest = latest
17
- end
18
-
19
- def latest?
20
- latest
21
- end
22
- end
23
-
24
7
  VERSION_COLUMNS = [:id, :number, :repository_ref, :pacticipant_id, :order, :created_at, :updated_at, :build_url]
25
8
 
26
9
  class Version < Sequel::Model(Sequel::Model.db[:versions].select(*VERSION_COLUMNS.collect{ | column | Sequel.qualify(:versions, column) }))
@@ -51,7 +34,7 @@ module PactBroker
51
34
  eager_loader: PactBroker::Versions::EagerLoaders::LatestVersionForPacticipant
52
35
 
53
36
  dataset_module do
54
- include PactBroker::Repositories::Helpers
37
+ include PactBroker::Dataset
55
38
 
56
39
  def with_branch
57
40
  where(id: PactBroker::Versions::BranchVersion.select(:version_id))
@@ -214,6 +197,7 @@ module PactBroker
214
197
  end
215
198
 
216
199
  # rubocop: disable Metrics/CyclomaticComplexity
200
+ # @param [PactBroker::Matrix::UnresolvedSelector] selector
217
201
  def for_selector(selector)
218
202
  query = self
219
203
  query = query.where_pacticipant_name(selector.pacticipant_name) if selector.pacticipant_name
@@ -252,6 +236,15 @@ module PactBroker
252
236
  end
253
237
  # rubocop: enable Metrics/CyclomaticComplexity
254
238
 
239
+ # Return the IDs of the versions described by the given unresolved selectors
240
+ # @return Sequel::Dataset<PactBroker::Domain::Version>
241
+ def ids_for_selectors(unresolved_selectors)
242
+ # Need the select at the start and at the end to stop extra columns being returned (eg. branch name, environment name)
243
+ unresolved_selectors
244
+ .collect{ |selector| self.select(Sequel[:versions][:id]).for_selector(selector).select(:id) }
245
+ .reduce(&:union)
246
+ end
247
+
255
248
  def pacticipants_set
256
249
  from_self(alias: :v)
257
250
  .select_group(Sequel[:v][:pacticipant_id])
@@ -0,0 +1,30 @@
1
+ require "pact_broker/logging"
2
+ require "pact_broker/errors"
3
+
4
+ module PactBroker
5
+ module Errors
6
+ class ErrorReporter
7
+ include PactBroker::Logging
8
+
9
+ def initialize(api_error_reporters)
10
+ @api_error_reporters = api_error_reporters
11
+ end
12
+
13
+ def call error, error_reference, env
14
+ if PactBroker::Errors.reportable_error?(error)
15
+ api_error_reporters.each do | error_reporter |
16
+ begin
17
+ error_reporter.call(error, env: env, error_reference: error_reference)
18
+ rescue StandardError => e
19
+ log_error(e, "Error executing api_error_reporter")
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :api_error_reporters
28
+ end
29
+ end
30
+ end
@@ -1,28 +1,15 @@
1
- require "pact_broker/configuration"
2
- require "pact_broker/error"
3
- require "pact_broker/logging"
4
1
  require "securerandom"
5
2
 
6
3
  module PactBroker
7
4
  module Errors
8
- include PactBroker::Logging
9
-
10
5
  def self.generate_error_reference
11
6
  SecureRandom.urlsafe_base64.gsub(/[^a-z]/i, "")[0,10]
12
7
  end
13
8
 
9
+ # Return true if the error is one that should be reported to an external bug tracking system
10
+ # @return [Boolean]
14
11
  def self.reportable_error?(error)
15
12
  error.is_a?(PactBroker::TestError) || (!error.is_a?(PactBroker::Error) && !error.is_a?(JSON::JSONError))
16
13
  end
17
-
18
- def self.report error, error_reference, env
19
- PactBroker.configuration.api_error_reporters.each do | error_notifier |
20
- begin
21
- error_notifier.call(error, env: env, error_reference: error_reference)
22
- rescue StandardError => e
23
- log_error(e, "Error executing api_error_reporter")
24
- end
25
- end
26
- end
27
14
  end
28
15
  end
@@ -32,11 +32,19 @@ module PactBroker
32
32
  extend self
33
33
 
34
34
  def subscribe(*args)
35
- result = nil
36
- TemporaryListeners.subscribe(*args) do
37
- result = yield
35
+ if block_given?
36
+ result = nil
37
+ TemporaryListeners.subscribe(*args) do
38
+ result = yield
39
+ end
40
+ result
41
+ else
42
+ Wisper.subscribe(*args)
38
43
  end
39
- result
44
+ end
45
+
46
+ def unsubscribe(*args)
47
+ Wisper.unsubscribe(*args)
40
48
  end
41
49
  end
42
50
  end
@@ -15,7 +15,7 @@ module PactBroker
15
15
  end
16
16
 
17
17
  def self.feature_in_env_var?(feature)
18
- PactBroker.configuration.features.include?(feature.to_s.downcase)
18
+ PactBroker.configuration.features[feature.to_s.downcase.to_sym] == true
19
19
  end
20
20
  end
21
21
 
@@ -1,5 +1,5 @@
1
1
  require "pact_broker/repositories"
2
- require "pact_broker/relationships/groupify"
2
+ require "pact_broker/domain/index_item"
3
3
 
4
4
  module PactBroker
5
5
  module Groups
@@ -8,13 +8,46 @@ module PactBroker
8
8
  extend PactBroker::Repositories
9
9
  extend PactBroker::Services
10
10
 
11
- def find_group_containing pacticipant
12
- groups.find { | group | group.include_pacticipant? pacticipant }
11
+ # Returns a list of all the integrations (PactBroker::Domain::IndexItem) that are connected to the given pacticipant.
12
+ # @param pacticipant [PactBroker::Domain::Pacticipant] the pacticipant for which to return the connected pacticipants
13
+ # @option max_pacticipants [Integer] the maximum number of pacticipants to return, or nil for no maximum. 40 is about the most applications you can meaningfully show in the circle network diagram.
14
+ # @return [PactBroker::Domain::Group]
15
+ def find_group_containing(pacticipant, max_pacticipants: nil)
16
+ PactBroker::Domain::Group.new(build_index_items(integrations_connected_to(pacticipant, max_pacticipants)))
13
17
  end
14
18
 
15
- def groups
16
- Relationships::Groupify.call(index_service.find_all_index_items)
19
+ def integrations_connected_to(pacticipant, max_pacticipants)
20
+ PactBroker::Integrations::Integration
21
+ .eager(:consumer, :provider)
22
+ .where(id: ids_of_integrations_connected_to(pacticipant, max_pacticipants))
23
+ .all
17
24
  end
25
+ private_class_method :integrations_connected_to
26
+
27
+ def build_index_items(integrations)
28
+ integrations.collect do | integration |
29
+ PactBroker::Domain::IndexItem.new(integration.consumer, integration.provider)
30
+ end
31
+ end
32
+ private_class_method :build_index_items
33
+
34
+ def ids_of_integrations_connected_to(pacticipant, max_pacticipants)
35
+ integrations = []
36
+ connected_pacticipants = Set.new([pacticipant.id])
37
+ new_connected_pacticipants = Set.new([pacticipant.id])
38
+
39
+ loop do
40
+ new_integrations = PactBroker::Integrations::Integration.including_pacticipant_id(new_connected_pacticipants.to_a).exclude(id: integrations.collect(&:id)).all
41
+ integrations.concat(new_integrations)
42
+ pacticipant_ids_for_new_integrations = Set.new(new_integrations.flat_map(&:pacticipant_ids))
43
+ new_connected_pacticipants = pacticipant_ids_for_new_integrations - connected_pacticipants
44
+ connected_pacticipants.merge(pacticipant_ids_for_new_integrations)
45
+ break if new_connected_pacticipants.empty? || (max_pacticipants && connected_pacticipants.size >= max_pacticipants)
46
+ end
47
+
48
+ integrations.collect(&:id).uniq
49
+ end
50
+ private_class_method :ids_of_integrations_connected_to
18
51
  end
19
52
  end
20
53
  end
@@ -1,7 +1,6 @@
1
1
  require "pact_broker/repositories"
2
2
  require "pact_broker/logging"
3
3
  require "pact_broker/domain/index_item"
4
- require "pact_broker/repositories/helpers"
5
4
  require "pact_broker/index/page"
6
5
  require "pact_broker/verifications/latest_verification_for_consumer_version_tag"
7
6
  require "pact_broker/string_refinements"
@@ -74,7 +73,7 @@ module PactBroker
74
73
  .eager(:provider)
75
74
  .eager(pact_version: { latest_verification: { provider_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :branch_heads, { tags: :head_tag } ] } })
76
75
  .eager(integration: [{latest_verification: :provider_version}, :latest_triggered_webhooks])
77
- .eager(consumer_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :branch_heads, { tags: :head_tag }])
76
+ .eager(consumer_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :branch_versions, :branch_heads, { tags: :head_tag }])
78
77
  .eager(:head_pact_publications_for_tags)
79
78
 
80
79
  index_items = pact_publications.all.collect do | pact_publication |
@@ -0,0 +1,23 @@
1
+ require "pact_broker/services"
2
+
3
+ # Listens for events that happen in the Pact Broker that are relevant to the Integrations objects.
4
+
5
+ module PactBroker
6
+ module Integrations
7
+ class EventListener
8
+ include PactBroker::Services
9
+
10
+ # @param [Hash] params the params from the broadcast event
11
+ # @option params [PactBroker::Domain::Pact] :pact the newly published pact
12
+ def contract_published(params)
13
+ integration_service.handle_contract_data_published(params.fetch(:pact).consumer, params.fetch(:pact).provider)
14
+ end
15
+
16
+ # @param [Hash] params the params from the broadcast event
17
+ # @option params [PactBroker::Domain::Verification] :verification the newly published verification
18
+ def provider_verification_published(params)
19
+ integration_service.handle_contract_data_published(params.fetch(:verification).consumer, params.fetch(:verification).provider)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,4 +1,4 @@
1
- require "pact_broker/db"
1
+ require "pact_broker/dataset"
2
2
  require "pact_broker/verifications/pseudo_branch_status"
3
3
  require "pact_broker/domain/verification"
4
4
  require "pact_broker/webhooks/latest_triggered_webhook"
@@ -7,7 +7,7 @@ require "pact_broker/verifications/latest_verification_for_consumer_and_provider
7
7
 
8
8
  module PactBroker
9
9
  module Integrations
10
- class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :consumer_id, :provider_id))
10
+ class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :consumer_id, :provider_id, :contract_data_updated_at))
11
11
  set_primary_key :id
12
12
  plugin :insert_ignore, identifying_columns: [:consumer_id, :provider_id]
13
13
  associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
@@ -75,6 +75,20 @@ module PactBroker
75
75
  end
76
76
  end)
77
77
 
78
+ dataset_module do
79
+ include PactBroker::Dataset
80
+
81
+ def filter_by_pacticipant(query_string)
82
+ matching_pacticipants = PactBroker::Domain::Pacticipant.filter(:name, query_string)
83
+ pacticipants_join = Sequel.|({ Sequel[:integrations][:consumer_id] => Sequel[:p][:id] }, { Sequel[:integrations][:provider_id] => Sequel[:p][:id] })
84
+ join(matching_pacticipants, pacticipants_join, table_alias: :p)
85
+ end
86
+
87
+ def including_pacticipant_id(pacticipant_id)
88
+ where(consumer_id: pacticipant_id).or(provider_id: pacticipant_id)
89
+ end
90
+ end
91
+
78
92
  def self.compare_by_last_action_date a, b
79
93
  if b.latest_pact_or_verification_publication_date && a.latest_pact_or_verification_publication_date
80
94
  b.latest_pact_or_verification_publication_date <=> a.latest_pact_or_verification_publication_date
@@ -111,6 +125,14 @@ module PactBroker
111
125
  def provider_name
112
126
  provider.name
113
127
  end
128
+
129
+ def pacticipant_ids
130
+ [consumer_id, provider_id]
131
+ end
132
+
133
+ def to_s
134
+ "Integration: consumer #{associations[:consumer]&.name || consumer_id}/provider #{associations[:provider]&.name || provider_id}"
135
+ end
114
136
  end
115
137
  end
116
138
  end
@@ -1,14 +1,28 @@
1
1
  require "pact_broker/integrations/integration"
2
+ require "pact_broker/repositories/scopes"
2
3
 
3
4
  module PactBroker
4
5
  module Integrations
5
6
  class Repository
7
+
8
+ include PactBroker::Repositories::Scopes
9
+
10
+ def find(filter_options = {}, pagination_options = {}, eager_load_associations = [])
11
+ query = scope_for(PactBroker::Integrations::Integration).select_all_qualified
12
+ query = query.filter_by_pacticipant(filter_options[:query_string]) if filter_options[:query_string]
13
+ query
14
+ .eager(*eager_load_associations)
15
+ .order(Sequel.desc(:contract_data_updated_at, nulls: :last))
16
+ .all_with_pagination_options(pagination_options)
17
+ end
18
+
6
19
  def create_for_pact(consumer_id, provider_id)
7
20
  if Integration.where(consumer_id: consumer_id, provider_id: provider_id).empty?
8
21
  Integration.new(
9
22
  consumer_id: consumer_id,
10
23
  provider_id: provider_id,
11
- created_at: Sequel.datetime_class.now
24
+ created_at: Sequel.datetime_class.now,
25
+ contract_data_updated_at: Sequel.datetime_class.now
12
26
  ).insert_ignore
13
27
  end
14
28
  nil
@@ -17,6 +31,25 @@ module PactBroker
17
31
  def delete(consumer_id, provider_id)
18
32
  Integration.where(consumer_id: consumer_id, provider_id: provider_id).delete
19
33
  end
34
+
35
+ # Sets the contract_data_updated_at for the integration(s) as specified by the consumer and provider
36
+ # @param [PactBroker::Domain::Pacticipant, nil] consumer the consumer for the integration, or nil if for a provider-only event (eg. Pactflow provider contract published)
37
+ # @param [PactBroker::Domain::Pacticipant] provider the provider for the integration
38
+ def set_contract_data_updated_at(consumer, provider)
39
+ Integration
40
+ .where({ consumer_id: consumer&.id, provider_id: provider.id }.compact )
41
+ .update(contract_data_updated_at: Sequel.datetime_class.now)
42
+ end
43
+
44
+
45
+ # Sets the contract_data_updated_at for the integrations as specified by an array of objects which each have a consumer and provider
46
+ # @param [Array<Object>] where each object has a consumer and a provider
47
+ def set_contract_data_updated_at_for_multiple_integrations(objects_with_consumer_and_provider)
48
+ consumer_and_provider_ids = objects_with_consumer_and_provider.collect{ | object | [object.consumer.id, object.provider.id] }.uniq
49
+ Integration
50
+ .where([:consumer_id, :provider_id] => consumer_and_provider_ids)
51
+ .update(contract_data_updated_at: Sequel.datetime_class.now)
52
+ end
20
53
  end
21
54
  end
22
55
  end
@@ -3,7 +3,6 @@ require "pact_broker/repositories"
3
3
  require "pact_broker/logging"
4
4
  require "pact_broker/integrations/integration"
5
5
  require "pact_broker/db/models"
6
- require "pact_broker/repositories/helpers"
7
6
  require "pact_broker/repositories/scopes"
8
7
 
9
8
  module PactBroker
@@ -14,22 +13,22 @@ module PactBroker
14
13
  include PactBroker::Logging
15
14
  extend PactBroker::Repositories::Scopes
16
15
 
17
- def self.find_all
18
- # The only reason the pact_version needs to be loaded is that
19
- # the Verification::PseudoBranchStatus uses it to determine if
20
- # the pseudo branch is 'stale'.
21
- # Because this is the status for a pact, and not a pseudo branch,
22
- # the status can never be 'stale',
23
- # so it would be better to create a Verification::PactStatus class
24
- # that doesn't have the 'stale' logic in it.
25
- # Then we can remove the eager loading of the pact_version
26
- scope_for(PactBroker::Integrations::Integration)
27
- .eager(:consumer)
28
- .eager(:provider)
29
- .eager(:latest_pact) # latest_pact eager loader is custom, can't take any more options
30
- .eager(:latest_verification)
31
- .all
32
- .sort { | a, b| Integration.compare_by_last_action_date(a, b) }
16
+ def self.find_all(filter_options = {}, pagination_options = {}, eager_load_associations = [])
17
+ integration_repository.find(filter_options, pagination_options, eager_load_associations)
18
+ end
19
+
20
+ # Callback to invoke when a consumer contract, verification result (or provider contract in Pactflow) is published
21
+ # @param [PactBroker::Domain::Pacticipant] consumer or nil
22
+ # @param [PactBroker::Domain::Pacticipant] provider
23
+ def self.handle_contract_data_published(consumer, provider)
24
+ integration_repository.set_contract_data_updated_at(consumer, provider)
25
+ end
26
+
27
+
28
+ # Callback to invoke when a batch of contract data is published (eg. the publish contracts endpoint)
29
+ # @param [Array<Object>] where each object has a consumer and a provider
30
+ def self.handle_bulk_contract_data_published(objects_with_consumer_and_provider)
31
+ integration_repository.set_contract_data_updated_at_for_multiple_integrations(objects_with_consumer_and_provider)
33
32
  end
34
33
 
35
34
  def self.delete(consumer_name, provider_name)
@@ -49,7 +48,7 @@ module PactBroker
49
48
  def self.delete_all
50
49
  # TODO move all these into their own repositories
51
50
  PactBroker::DB.each_integration_model do | model |
52
- if PactBroker::Repositories::Helpers.postgres?
51
+ if PactBroker::Dataset::Helpers.postgres?
53
52
  logger.info("Truncating ", model.table_name)
54
53
  model.truncate(cascade: true)
55
54
  else
@@ -1,22 +1,18 @@
1
1
  require "pact_broker/domain/label"
2
- require "pact_broker/repositories/helpers"
3
2
 
4
3
  module PactBroker
5
4
  module Labels
6
5
  class Repository
7
-
8
- include PactBroker::Repositories::Helpers
9
-
10
6
  def create args
11
7
  Domain::Label.new(name: args.fetch(:name), pacticipant: args.fetch(:pacticipant)).save
12
8
  end
13
9
 
14
10
  def find args
15
11
  PactBroker::Domain::Label
16
- .select(Sequel.qualify("labels", "name"), Sequel.qualify("labels", "pacticipant_id"), Sequel.qualify("labels", "created_at"), Sequel.qualify("labels", "updated_at"))
17
- .join(:pacticipants, {id: :pacticipant_id})
18
- .where(name_like(Sequel.qualify("labels", "name"), args.fetch(:label_name)))
19
- .where(name_like(Sequel.qualify("pacticipants", "name"), args.fetch(:pacticipant_name)))
12
+ .select_all_qualified
13
+ .join(:pacticipants, { id: :pacticipant_id })
14
+ .where(Sequel.name_like(Sequel.qualify("labels", "name"), args.fetch(:label_name)))
15
+ .where(Sequel.name_like(Sequel.qualify("pacticipants", "name"), args.fetch(:pacticipant_name)))
20
16
  .single_record
21
17
  end
22
18
 
@@ -59,7 +59,11 @@ en:
59
59
  verifications: Add Pact verification tests to the %{provider_name} build. See https://docs.pact.io/go/provider_verification
60
60
  webhooks: Configure separate %{provider_name} pact verification build and webhook to trigger it when the pact content changes. See https://docs.pact.io/go/webhooks
61
61
  version_branch: Configure the version branch to be the value of your repository branch.
62
+ branch:
63
+ bulk_delete: "Scheduled deletion of %{count} branches for pacticipant %{pacticipant_name}. Remaining branches are: %{remaining}"
62
64
  errors:
65
+ runtime:
66
+ with_error_reference: "An error has occurred. The details have been logged with the reference %{error_reference}"
63
67
  validation:
64
68
  no_spaces: "cannot contain spaces"
65
69
  single_line: "cannot contain multiple lines"
@@ -89,6 +93,7 @@ en:
89
93
  environment_with_name_not_found: "Environment with name '%{name}' does not exist"
90
94
  cannot_modify_version_branch: "The branch for a pacticipant version cannot be changed once set (currently '%{old_branch}', proposed value '%{new_branch}'). Your pact publication/verification publication configurations may be in conflict with each other if you are seeing this error. If the current branch value is incorrect, you must delete the pacticipant version resource at %{version_url} and publish the pacts/verification results again with a consistent branch name."
91
95
  invalid_content_for_content_type: "content could not be parsed as %{content_type}"
96
+ invalid_parsed_contract_class: "parsed content was expected to be a Hash but was a %{actual_class}"
92
97
  cannot_set_currently_deployed_true: The currentlyDeployed property cannot be set back to true. Please record a new deployment.
93
98
  cannot_set_currently_supported_true: The currentlySupported property cannot be set back to true. Please record a new deployment.
94
99
  invalid_limit: The limit must be 1 or greater.
@@ -1,13 +1,45 @@
1
- require "pact_broker/matrix/quick_row"
1
+ require "pact_broker/matrix/matrix_row"
2
+ require "pact_broker/matrix/matrix_row_dataset_module"
3
+ require "pact_broker/matrix/matrix_row_instance_methods"
4
+ require "pact_broker/matrix/matrix_row_verification_dataset_module"
2
5
 
6
+ # Same as PactBroker::Matrix::MatrixRow
7
+ # except the data is sourced from the pact_publications table, and contains
8
+ # every pact publication, not just the latest publication for the consumer version.
9
+ # This is used when there is no "latestby" in the matrix query.
3
10
  module PactBroker
4
11
  module Matrix
5
- class EveryRow < PactBroker::Matrix::QuickRow
6
- set_dataset(Sequel.as(:pact_publications, :p))
12
+ class EveryRow < Sequel::Model(Sequel.as(:pact_publications, :p))
7
13
 
8
- P_V_JOIN = { Sequel[:p][:pact_version_id] => Sequel[:v][:pact_version_id] }
14
+ # Same as PactBroker::Matrix::MatrixRow::Verification
15
+ # except the data is sourced from the verifications table, and contains
16
+ # every verification, not just the latest verification for the pact version and the provider version.
17
+ # This is used when there is no "latestby" in the matrix query.
18
+ class Verification < Sequel::Model(:verifications)
19
+ dataset_module do
20
+ select(:select_verification_columns_with_aliases,
21
+ Sequel[:verifications][:id].as(:verification_id),
22
+ Sequel[:verifications][:provider_version_id],
23
+ Sequel[:verifications][:created_at].as(:provider_version_created_at),
24
+ Sequel[:verifications][:pact_version_id]
25
+ )
9
26
 
10
- PACT_COLUMNS = [
27
+ include PactBroker::Matrix::MatrixRowVerificationDatasetModule
28
+ end
29
+ end
30
+
31
+ # Must be kept in sync with PactBroker::Matrix::MatrixRow
32
+ associate(:many_to_one, :pact_publication, :class => "PactBroker::Pacts::PactPublication", :key => :pact_publication_id, :primary_key => :id)
33
+ associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
34
+ associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
35
+ associate(:many_to_one, :consumer_version, :class => "PactBroker::Domain::Version", :key => :consumer_version_id, :primary_key => :id)
36
+ associate(:many_to_one, :provider_version, :class => "PactBroker::Domain::Version", :key => :provider_version_id, :primary_key => :id)
37
+ associate(:many_to_one, :pact_version, class: "PactBroker::Pacts::PactVersion", :key => :pact_version_id, :primary_key => :id)
38
+ associate(:many_to_one, :verification, class: "PactBroker::Domain::Verification", :key => :verification_id, :primary_key => :id)
39
+ associate(:one_to_many, :consumer_version_tags, :class => "PactBroker::Domain::Tag", primary_key: :consumer_version_id, key: :version_id)
40
+ associate(:one_to_many, :provider_version_tags, :class => "PactBroker::Domain::Tag", primary_key: :provider_version_id, key: :version_id)
41
+
42
+ PACT_COLUMNS_WITH_ALIASES = [
11
43
  Sequel[:p][:consumer_id],
12
44
  Sequel[:p][:provider_id],
13
45
  Sequel[:p][:consumer_version_id],
@@ -18,50 +50,36 @@ module PactBroker
18
50
  Sequel[:p][:id].as(:pact_order)
19
51
  ]
20
52
 
21
- VERIFICATION_COLUMNS = [
22
- Sequel[:v][:id].as(:verification_id),
53
+ ALL_COLUMNS_AFTER_JOIN = [
54
+ Sequel[:p][:consumer_id],
55
+ Sequel[:p][:provider_id],
56
+ Sequel[:p][:consumer_version_id],
57
+ Sequel[:p][:pact_publication_id],
58
+ Sequel[:p][:pact_version_id],
59
+ Sequel[:p][:pact_revision_number],
60
+ Sequel[:p][:consumer_version_created_at],
61
+ Sequel[:p][:pact_order],
62
+ Sequel[:v][:verification_id],
23
63
  Sequel[:v][:provider_version_id],
24
- Sequel[:v][:created_at].as(:provider_version_created_at)
64
+ Sequel[:v][:provider_version_created_at]
25
65
  ]
26
66
 
27
- JOINED_VERIFICATION_COLUMNS = [:id, :pact_version_id, :provider_id, :provider_version_id, :created_at]
28
-
29
- ALL_COLUMNS = PACT_COLUMNS + VERIFICATION_COLUMNS
30
-
31
- SELECT_ALL_COLUMN_ARGS = [:select_all_columns] + ALL_COLUMNS
32
-
33
67
  dataset_module do
34
- select(*SELECT_ALL_COLUMN_ARGS)
68
+ include PactBroker::Matrix::MatrixRowDatasetModule
35
69
 
36
- def join_verifications
37
- left_outer_join(:verifications, P_V_JOIN, { table_alias: :v } )
38
- end
70
+ select(:select_pact_columns_with_aliases, *PACT_COLUMNS_WITH_ALIASES)
71
+ select(:select_all_columns_after_join, *ALL_COLUMNS_AFTER_JOIN)
39
72
 
40
- def inner_join_verifications
41
- join(:verifications, P_V_JOIN, { table_alias: :v } )
42
- end
43
-
44
- def inner_join_verifications_matching_one_selector_provider_or_provider_version(query_ids)
45
- verifications = db[:verifications]
46
- .select(*JOINED_VERIFICATION_COLUMNS)
47
- .where {
48
- QueryBuilder.provider_or_provider_version_matches(query_ids)
49
- }
50
-
51
- join(verifications, P_V_JOIN, { table_alias: :v } )
73
+ def verification_dataset
74
+ EveryRow::Verification
52
75
  end
76
+ end
53
77
 
54
- def verifications_for(query_ids)
55
- db[:verifications]
56
- .select(*JOINED_VERIFICATION_COLUMNS)
57
- .where {
58
- Sequel.&(
59
- QueryBuilder.consumer_in_pacticipant_ids(query_ids),
60
- QueryBuilder.provider_or_provider_version_matches(query_ids)
61
- )
62
- }
63
- end
78
+ def pact_publication_id
79
+ return_or_raise_if_not_set(:pact_publication_id)
64
80
  end
81
+
82
+ include PactBroker::Matrix::MatrixRowInstanceMethods
65
83
  end
66
84
  end
67
85
  end