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
@@ -0,0 +1,36 @@
1
+ module PactBroker
2
+ module Matrix
3
+ class RowIgnorer
4
+
5
+ class << self
6
+ # Splits the matrix rows into considered rows and ignored rows, based on the
7
+ # ignore selectors specified by the user in the can-i-deploy command (eg. --ignore SomeProviderThatIsNotReadyYet).
8
+ # @param [Array<MatrixRow, EveryRow>] rows
9
+ # @param [<PactBroker::Matrix::ResolvedSelector>] resolved_ignore_selectors
10
+ # @return [Array<MatrixRow, EveryRow>] considered_rows, [Array<MatrixRow, EveryRow>] ignored_rows
11
+ def split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors)
12
+ if resolved_ignore_selectors.any?
13
+ considered, ignored = [], []
14
+ rows.each do | row |
15
+ if ignore_row?(resolved_ignore_selectors, row)
16
+ ignored << row
17
+ else
18
+ considered << row
19
+ end
20
+ end
21
+ return considered, ignored
22
+ else
23
+ return rows, []
24
+ end
25
+ end
26
+
27
+ def ignore_row?(resolved_ignore_selectors, row)
28
+ resolved_ignore_selectors.any? do | s |
29
+ s.pacticipant_id == row.consumer_id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == row.consumer_version_id) ||
30
+ s.pacticipant_id == row.provider_id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == row.provider_version_id)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,59 @@
1
+ # This class determines whether or not a resolved selector (both specified and inferred)
2
+ # that is about to be created should be marked as "ignored".
3
+ # It uses the ignore selectors are provided in the can-i-deploy CLI command like so:
4
+ # can-i-deploy --pacticipant Foo --version 234243 --to-environment prod --ignore SomeProviderThatIsNotReadyYet [--version SomeOptionalVersion]
5
+ # The ignored flag on the ResolvedSelector is used to determine whether or not a failing/missing row
6
+ # in the can-i-deploy matrix should be ignored.
7
+ # This allows can-i-deploy to pass successfully when a dependency is known to be not ready,
8
+ # but the developer wants to deploy the application anyway.
9
+
10
+ # The only reason why we need to resolve the ignore selectors is that we check in PactBroker::Matrix::DeploymentStatusSummary
11
+ # whether or not the pacticipant or version they specify actually exist.
12
+ # We could actually have performed the ignore checks just using the name and version number.
13
+
14
+ module PactBroker
15
+ module Matrix
16
+ class SelectorIgnorer
17
+
18
+ # @param [Array<PactBroker::Matrix::UnresolvedSelector>] resolved_ignore_selectors
19
+ def initialize(resolved_ignore_selectors)
20
+ @resolved_ignore_selectors = resolved_ignore_selectors
21
+ end
22
+
23
+ # Whether the pacticipant should be ignored if the verification results are missing/failed.
24
+ # @param [PactBroker::Domain::Pacticipant] pacticipant
25
+ # @return [Boolean]
26
+ def ignore_pacticipant?(pacticipant)
27
+ resolved_ignore_selectors.any? do | s |
28
+ s.pacticipant_id == pacticipant.id && s.only_pacticipant_name_specified?
29
+ end
30
+ end
31
+
32
+ # Whether the pacticipant version should be ignored if the verification results are missing/failed.
33
+ # @param [PactBroker::Domain::Pacticipant] pacticipant
34
+ # @param [PactBroker::Domain::Version] version
35
+ # @return [Boolean]
36
+ def ignore_pacticipant_version?(pacticipant, version)
37
+ resolved_ignore_selectors.any? do | s |
38
+ s.pacticipant_id == pacticipant.id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == version.id)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :resolved_ignore_selectors
45
+ end
46
+
47
+ # Used when resolving the ignore selecors in the first place - the process for resolving normal selectors
48
+ # and ignore selectors is almost the same, but it makes no sense to ignore an ignore selector.
49
+ class NilSelectorIgnorer
50
+ def ignore_pacticipant?(*)
51
+ false
52
+ end
53
+
54
+ def ignore_pacticipant_version?(*)
55
+ false
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,130 @@
1
+ require "pact_broker/repositories"
2
+ require "pact_broker/matrix/resolved_selector"
3
+ require "pact_broker/matrix/resolved_selector_builder"
4
+ require "pact_broker/matrix/selector_ignorer"
5
+
6
+
7
+ # Take the selectors and options provided by the user (eg. [{ pacticipant_name: "Foo", pacticipant_version_number: "1" }], { to_environment: "prod" })
8
+ # that use pacticipant/version/branch/environment names,
9
+ # and look up the IDs of all the objects, and return them as ResolvedSelector objects.
10
+ # For unresolved selectors that specify a collection of versions (eg. { branch: "main" }) a ResolvedSelector
11
+ # will be returned for every pacticipant version found. This will eventually be used in the can-i-deploy
12
+ # logic in PactBroker::Matrix::DeploymentStatusSummary to work out if there are any missing verifications.
13
+
14
+ module PactBroker
15
+ module Matrix
16
+ class SelectorResolver
17
+ class << self
18
+ include PactBroker::Repositories
19
+
20
+ # Resolve any ignore selectors used in the can-i-deploy command e.g `--ignore SomeProviderThatIsNotReadyYet`
21
+ # @param [Array<PactBroker::Matrix::UnresolvedSelector>] unresolved_ignore_selectors
22
+ # @return [Array<PactBroker::Matrix::ResolvedSelector>]
23
+ def resolved_ignore_selectors(unresolved_ignore_selectors)
24
+ # When resolving the ignore_selectors, use the NilSelectorIgnorer because it doesn't make sense to ignore
25
+ # the ignore selectors.
26
+ resolve_versions_and_add_ids(unresolved_ignore_selectors, :ignored, NilSelectorIgnorer.new)
27
+ end
28
+
29
+ # Resolve the selectors that were specified in the can-i-deploy command eg. `--pacticipant Foo --version 43434`
30
+ # There may be one or multiple.
31
+ # @param [Array<PactBroker::Matrix::UnresolvedSelector>] unresolved_specified_selectors
32
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_ignore_selectors previously resolved selectors for the versions to ignore
33
+ # @return [Array<PactBroker::Matrix::ResolvedSelector>]
34
+ def resolve_specified_selectors(unresolved_specified_selectors, resolved_ignore_selectors)
35
+ resolve_versions_and_add_ids(unresolved_specified_selectors, :specified, SelectorIgnorer.new(resolved_ignore_selectors))
36
+ end
37
+
38
+ # When the can-i-deploy command uses any of the `--to` options (eg. `--to-environment ENV` or `--to TAG`)
39
+ # we need to create the inferred selectors for the pacticipant versions in that environment/with that tag/branch.
40
+ # eg. if A -> B, and the CLI command is `can-i-deploy --pacticipant A --version 3434 --to-environment prod`,
41
+ # then we need to make the inferred selector for pacticipant B with the version that is in prod.
42
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_specified_selectors
43
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_ignore_selectors
44
+ # @param [Array<PactBroker::Matrix::Integration>] integrations
45
+ # @param [Hash] options
46
+ # @return [Array<PactBroker::Matrix::ResolvedSelector>]
47
+ def resolve_inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
48
+ all_pacticipant_names = integrations.collect(&:pacticipant_names).flatten.uniq
49
+ specified_names = resolved_specified_selectors.collect{ |s| s[:pacticipant_name] }
50
+ inferred_pacticipant_names = all_pacticipant_names - specified_names
51
+ unresolved_selectors = build_unresolved_selectors_for_inferred_pacticipants(inferred_pacticipant_names, options)
52
+ resolve_versions_and_add_ids(unresolved_selectors, :inferred, SelectorIgnorer.new(resolved_ignore_selectors))
53
+ end
54
+
55
+ # Find the IDs of every pacticipant and version in the UnresolvedSelectors, and return them as ResolvedSelectors,
56
+ # expanding selectors for multiple versions.
57
+ # This gets called first for the ignore selectors, then the specified selectors, and then the inferred selectors.
58
+ # When it gets called for the first time for the ignore selectors, they will be passed in as the unresolved_selectors, and the resolved_ignore_selectors
59
+ # will be empty.
60
+ # The next times it is called with the specified selectors and the inferred selectors, the previously resolved ignore selectors will be passed in
61
+ # as resolved_ignore_selectors so we can work out which of those selectors needs to be ignored.
62
+ #
63
+ # @param [Array<PactBroker::Matrix::UnresolvedSelector>] unresolved_selectors
64
+ # @param [Symbol] selector_type which may be :specified or :inferred
65
+ # @param [SelectorIgnorer] selector_ignorer
66
+ # @return [Array<PactBroker::Matrix::ResolvedSelector>]
67
+ def resolve_versions_and_add_ids(unresolved_selectors, selector_type, selector_ignorer)
68
+ pacticipants_hash = find_pacticipants_for_selectors(unresolved_selectors)
69
+ unresolved_selectors.collect do | unresolved_selector |
70
+ build_selectors(pacticipants_hash, unresolved_selector, selector_type, selector_ignorer)
71
+ end.flatten
72
+ end
73
+
74
+ private :resolve_versions_and_add_ids
75
+
76
+ # Return a Hash of the pacticipant names used in the selectors, where the key is the name and the value is the pacticipant
77
+ # @return [Hash<String, PactBroker::Domain::Pacticipant>]
78
+ def find_pacticipants_for_selectors(unresolved_selectors)
79
+ names = unresolved_selectors.collect(&:pacticipant_name)
80
+ PactBroker::Domain::Pacticipant.where(name: names).all.group_by(&:name).transform_values(&:first)
81
+ end
82
+
83
+ private :find_pacticipants_for_selectors
84
+
85
+ def build_selectors(pacticipants_hash, unresolved_selector, selector_type, selector_ignorer)
86
+ selector_builder = ResolvedSelectorBuilder.new(unresolved_selector, selector_type, selector_ignorer)
87
+ selector_builder.pacticipant = pacticipants_hash[unresolved_selector.pacticipant_name]
88
+ if selector_builder.pacticipant
89
+ versions = find_versions_for_selector(unresolved_selector)
90
+ selector_builder.versions = versions
91
+ end
92
+ selector_builder.build
93
+ end
94
+
95
+ # Find the pacticipant versions for the unresolved selector.
96
+ # @param [PactBroker::Matrix::UnresolvedSelector] unresolved_selector
97
+ def find_versions_for_selector(unresolved_selector)
98
+ # For selectors that just set the pacticipant name, there's no need to resolve the version -
99
+ # only the pacticipant ID will be used in the query
100
+ return nil if unresolved_selector.all_for_pacticipant?
101
+ versions = version_repository.find_versions_for_selector(unresolved_selector)
102
+
103
+ if unresolved_selector.latest
104
+ [versions.first]
105
+ else
106
+ versions.empty? ? [nil] : versions
107
+ end
108
+ end
109
+
110
+ private :find_versions_for_selector
111
+
112
+ # Build an unresolved selector for the integrations that we have inferred for the target environment/branch/tag
113
+ # @param [Array<String>] inferred_pacticipant_names the names of the pacticipants that we have determined to be integrated with the versions for the specified selectors
114
+ def build_unresolved_selectors_for_inferred_pacticipants(inferred_pacticipant_names, options)
115
+ inferred_pacticipant_names.collect do | pacticipant_name |
116
+ selector = UnresolvedSelector.new(pacticipant_name: pacticipant_name)
117
+ selector.tag = options[:tag] if options[:tag]
118
+ selector.branch = options[:branch] if options[:branch]
119
+ selector.main_branch = options[:main_branch] if options[:main_branch]
120
+ selector.latest = options[:latest] if options[:latest]
121
+ selector.environment_name = options[:environment_name] if options[:environment_name]
122
+ selector
123
+ end
124
+ end
125
+
126
+ private :build_unresolved_selectors_for_inferred_pacticipants
127
+ end
128
+ end
129
+ end
130
+ end
@@ -101,8 +101,8 @@ module PactBroker
101
101
  end
102
102
 
103
103
  def pact_revision_counts
104
- query = "select revision_count as number_of_revisions, count(consumer_version_id) as consumer_version_count
105
- from (select consumer_version_id, count(*) as revision_count from pact_publications group by consumer_version_id) foo
104
+ query = "select revision_count as number_of_revisions, count(*) as consumer_version_count
105
+ from (select count(*) as revision_count from pact_publications group by consumer_version_id, provider_id) foo
106
106
  group by revision_count
107
107
  order by 1"
108
108
  PactBroker::Pacts::PactPublication.db[query].all.each_with_object({}) { |row, hash| hash[row[:number_of_revisions]] = row[:consumer_version_count] }
@@ -116,14 +116,9 @@ module PactBroker
116
116
  PactBroker::Pacts::PactPublication.db[query].all.each_with_object({}) { |row, hash| hash[row[:number_of_verifications]] = row[:pact_version_count] }
117
117
  end
118
118
 
119
+ # This count has no real meaning and takes too long to do. Hardcode it to -1
119
120
  def matrix_count
120
- begin
121
- PactBroker::Matrix::EveryRow.db.with_statement_timeout(PactBroker.configuration.metrics_sql_statement_timeout) do
122
- PactBroker::Matrix::EveryRow.default_scope.count
123
- end
124
- rescue Sequel::DatabaseError => _ex
125
- -1
126
- end
121
+ -1
127
122
  end
128
123
  end
129
124
  end
@@ -0,0 +1,33 @@
1
+ module PactBroker
2
+ module Pacticipants
3
+ class LatestVersionForPacticipantEagerLoader
4
+ def self.call(eo, **_other)
5
+ populate_associations(eo[:rows])
6
+ end
7
+
8
+ def self.populate_associations(pacticipants)
9
+ pacticipants.each { | pacticipant | pacticipant.associations[:latest_version] = nil }
10
+ pacticipant_ids = pacticipants.collect(&:id)
11
+
12
+ max_orders = PactBroker::Domain::Version
13
+ .where(pacticipant_id: pacticipant_ids)
14
+ .select_group(:pacticipant_id)
15
+ .select_append { max(order).as(latest_order) }
16
+
17
+ max_orders_join = {
18
+ Sequel[:max_orders][:latest_order] => Sequel[:versions][:order],
19
+ Sequel[:max_orders][:pacticipant_id] => Sequel[:versions][:pacticipant_id]
20
+ }
21
+
22
+ latest_versions = PactBroker::Domain::Version
23
+ .select_all_qualified
24
+ .join(max_orders, max_orders_join, { table_alias: :max_orders})
25
+
26
+ latest_versions.each do | version |
27
+ pacticipant = pacticipants.find{ | p | p.id == version.pacticipant_id }
28
+ pacticipant.associations[:latest_version] = version
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,4 @@
1
- require "sequel"
2
1
  require "pact_broker/domain/pacticipant"
3
- require "pact_broker/repositories/helpers"
4
2
  require "pact_broker/error"
5
3
  require "pact_broker/repositories/scopes"
6
4
 
@@ -9,11 +7,10 @@ module PactBroker
9
7
  class Repository
10
8
 
11
9
  include PactBroker::Repositories
12
- include PactBroker::Repositories::Helpers
13
10
  include PactBroker::Repositories::Scopes
14
11
 
15
12
  def find_by_name name
16
- pacticipants = PactBroker::Domain::Pacticipant.where(name_like(:name, name)).all
13
+ pacticipants = PactBroker::Domain::Pacticipant.where(Sequel.name_like(:name, name)).all
17
14
  handle_multiple_pacticipants_found(name, pacticipants) if pacticipants.size > 1
18
15
  pacticipants.first
19
16
  end
@@ -27,7 +24,7 @@ module PactBroker
27
24
  # @param [Array<String>] the array of names by which to find the pacticipants
28
25
  def find_by_names(names)
29
26
  return [] if names.empty?
30
- name_likes = names.collect{ | name | name_like(:name, name) }
27
+ name_likes = names.collect{ | name | Sequel.name_like(:name, name) }
31
28
  scope_for(PactBroker::Domain::Pacticipant).where(Sequel.|(*name_likes)).all
32
29
  end
33
30
 
@@ -35,14 +32,15 @@ module PactBroker
35
32
  PactBroker::Domain::Pacticipant.where(id: id).single_record
36
33
  end
37
34
 
38
- def find_all(pagination_options = {})
39
- find({}, pagination_options)
35
+ def find_all(options = {}, pagination_options = {}, eager_load_associations = [])
36
+ find(options, pagination_options, eager_load_associations)
40
37
  end
41
38
 
42
- def find(options = {}, pagination_options = {})
39
+ def find(options = {}, pagination_options = {}, eager_load_associations = [])
43
40
  query = scope_for(PactBroker::Domain::Pacticipant).select_all_qualified
41
+ query = query.filter(:name, options[:query_string]) if options[:query_string]
44
42
  query = query.label(options[:label_name]) if options[:label_name]
45
- query.order_ignore_case(Sequel[:pacticipants][:name]).eager(:labels).eager(:latest_version).all_with_pagination_options(pagination_options)
43
+ query.order_ignore_case(Sequel[:pacticipants][:name]).eager(*eager_load_associations).all_with_pagination_options(pagination_options)
46
44
  end
47
45
 
48
46
  def find_by_name_or_create name
@@ -32,8 +32,8 @@ module PactBroker
32
32
  } .collect{ | name | pacticipant_repository.find_by_name(name) }
33
33
  end
34
34
 
35
- def self.find_all_pacticipants(pagination_options = {})
36
- pacticipant_repository.find_all(pagination_options)
35
+ def self.find_all_pacticipants(filter_options = {}, pagination_options = {}, eager_load_associations = [])
36
+ pacticipant_repository.find_all(filter_options, pagination_options, eager_load_associations)
37
37
  end
38
38
 
39
39
  def self.find_pacticipant_by_name(name)
@@ -1,4 +1,4 @@
1
- require "pact_broker/repositories/helpers"
1
+ require "pact_broker/dataset"
2
2
 
3
3
  module PactBroker
4
4
  module Pacts
@@ -7,9 +7,7 @@ module PactBroker
7
7
  unrestrict_primary_key
8
8
  plugin :upsert, identifying_columns: [:provider_id, :consumer_version_id]
9
9
 
10
- dataset_module do
11
- include PactBroker::Repositories::Helpers
12
- end
10
+ dataset_module PactBroker::Dataset
13
11
  end
14
12
  end
15
13
  end
@@ -12,6 +12,7 @@ module PactBroker
12
12
  [:consumer_version_selectors, "s"],
13
13
  [:tag, "t"],
14
14
  [:branch, "b"],
15
+ [:environment, "e"],
15
16
  [:latest, "l"]
16
17
  ]
17
18
 
@@ -45,6 +46,7 @@ module PactBroker
45
46
  # parameters. This is part of ensuring that verification results webhooks
46
47
  # go back to the correct consumer version number (eg for git statuses)
47
48
  def build_metadata_for_webhook_triggered_by_pact_publication(pact)
49
+ # Should probably put the branch in here, but I don't think the tags are used for anything
48
50
  metadata = {
49
51
  "cvn" => pact.consumer_version_number,
50
52
  "cvt" => pact.consumer_version_tag_names
@@ -60,13 +62,13 @@ module PactBroker
60
62
  "w" => true
61
63
  }
62
64
  else
63
- # TODO support deployed and released
64
65
  {
65
66
  "s" => verifiable_pact.selectors.collect do | selector |
66
67
  {
67
68
  "b" => selector.branch,
68
69
  "t" => selector.tag,
69
70
  "l" => selector.latest,
71
+ "e" => selector.environment_name,
70
72
  "cv" => selector.consumer_version.id
71
73
  }.compact
72
74
  end,
@@ -1,7 +1,7 @@
1
1
  require "forwardable"
2
+ require "pact_broker/dataset"
2
3
  require "pact_broker/domain/pact"
3
4
  require "pact_broker/pacts/pact_version"
4
- require "pact_broker/repositories/helpers"
5
5
  require "pact_broker/integrations/integration"
6
6
  require "pact_broker/tags/head_pact_tags"
7
7
  require "pact_broker/pacts/pact_publication_dataset_module"
@@ -52,16 +52,22 @@ module PactBroker
52
52
  plugin :timestamps, update_on_create: true
53
53
 
54
54
  dataset_module do
55
- include PactBroker::Repositories::Helpers
55
+ include PactBroker::Dataset
56
56
  include PactPublicationDatasetModule
57
57
  include PactPublicationCleanSelectorDatasetModule
58
58
  include PactPublicationWipDatasetModule
59
59
 
60
60
  def eager_for_domain_with_content
61
- eager(:tags, :consumer, :provider, :consumer_version, :pact_version)
61
+ eager(:tags, :consumer, :provider, :pact_version, { consumer_version: :branch_versions })
62
62
  end
63
63
  end
64
64
 
65
+ def with_version_branches_and_tags
66
+ consumer_version.tags
67
+ consumer_version.branch_versions
68
+ self
69
+ end
70
+
65
71
  def self.subtract(a, *b)
66
72
  b_ids = b.flat_map{ |pact_publications| pact_publications.collect(&:id) }
67
73
  a.reject{ |pact_publication| b_ids.include?(pact_publication.id) }
@@ -125,8 +131,9 @@ module PactBroker
125
131
  @latest_for_branch
126
132
  end
127
133
 
134
+ # rubocop:disable Metrics/CyclomaticComplexity
128
135
  def to_domain
129
- PactBroker::Domain::Pact.new(
136
+ attributes = {
130
137
  id: id,
131
138
  provider: provider,
132
139
  consumer: consumer,
@@ -139,8 +146,19 @@ module PactBroker
139
146
  created_at: created_at,
140
147
  head_tag_names: [],
141
148
  db_model: self
142
- )
149
+ }
150
+
151
+ if associations[:tags] || consumer_version.associations[:tags]
152
+ attributes[:consumer_version_tag_names] = associations[:tags]&.collect(&:name) || consumer_version.associations[:tags]&.collect(&:name)
153
+ end
154
+
155
+ if consumer_version.associations[:branch_versions]
156
+ attributes[:consumer_version_branch_names] = consumer_version.branch_versions.collect(&:branch_name)
157
+ end
158
+
159
+ PactBroker::Domain::Pact.new(attributes)
143
160
  end
161
+ # rubocop:enable Metrics/CyclomaticComplexity
144
162
 
145
163
  def to_domain_lightweight
146
164
  PactBroker::Domain::Pact.new(
@@ -132,10 +132,14 @@ module PactBroker
132
132
  .remove_overridden_revisions_from_complete_query
133
133
  end
134
134
 
135
+ # The pact that belongs to the branch head.
136
+ # May return nil if the branch head does not have a pact published for it.
135
137
  def latest_for_consumer_branch(branch_name)
136
138
  for_branch_heads(branch_name)
137
139
  end
138
140
 
141
+ # The latest pact that belongs to a version on the specified branch (might not be the version that is the branch head)
142
+ # Always returns a pact, if any pacts exist for this branch.
139
143
  def old_latest_for_consumer_branch(branch_name)
140
144
  branch_versions_join = {
141
145
  Sequel[:pact_publications][:consumer_version_id] => Sequel[:branch_versions][:version_id]
@@ -148,7 +152,7 @@ module PactBroker
148
152
 
149
153
  max_orders = join(:branch_versions, branch_versions_join)
150
154
  .join(:branches, branches_join)
151
- .select_group(:consumer_id, :provider_id, Sequel[:branches][:name].as(:branch_name))
155
+ .select_group(Sequel[:pact_publications][:consumer_id], Sequel[:pact_publications][:provider_id], Sequel[:branches][:name].as(:branch_name))
152
156
  .select_append{ max(consumer_version_order).as(latest_consumer_version_order) }
153
157
 
154
158
  max_join = {
@@ -1,5 +1,4 @@
1
- require "sequel"
2
- require "pact_broker/repositories/helpers"
1
+ require "pact_broker/dataset"
3
2
  require "pact_broker/pacts/content"
4
3
  require "pact_broker/pacts/pact_version_association_loaders"
5
4
 
@@ -52,7 +51,7 @@ module PactBroker
52
51
  )
53
52
 
54
53
  dataset_module do
55
- include PactBroker::Repositories::Helpers
54
+ include PactBroker::Dataset
56
55
 
57
56
  def for_pact_domain(pact_domain)
58
57
  where(
@@ -1,9 +1,7 @@
1
- require "sequel"
2
1
  require "pact_broker/logging"
3
2
  require "pact_broker/pacts/pact_publication"
4
3
  require "pact_broker/domain"
5
4
  require "pact_broker/pacts/verifiable_pact"
6
- require "pact_broker/repositories/helpers"
7
5
  require "pact_broker/pacts/selected_pact"
8
6
  require "pact_broker/pacts/selector"
9
7
  require "pact_broker/pacts/selectors"
@@ -17,7 +15,6 @@ module PactBroker
17
15
  include PactBroker::Logging
18
16
  include PactBroker::Repositories
19
17
  include PactBroker::Services
20
- include PactBroker::Repositories::Helpers
21
18
  include PactBroker::Repositories::Scopes
22
19
 
23
20
  PUBLICATION_ASSOCIATIONS_FOR_EAGER_LOAD = [
@@ -221,9 +218,9 @@ module PactBroker
221
218
 
222
219
  def create_selectors_for_wip_pact(pact_publication)
223
220
  if pact_publication.values[:tag_name]
224
- Selectors.create_for_latest_for_tag(pact_publication.values[:tag_name])
221
+ Selectors.create_for_latest_for_tag(pact_publication.values[:tag_name]).resolve(pact_publication.consumer_version)
225
222
  else
226
- Selectors.create_for_latest_for_branch(pact_publication.values.fetch(:branch_name))
223
+ Selectors.create_for_latest_for_branch(pact_publication.values.fetch(:branch_name)).resolve(pact_publication.consumer_version)
227
224
  end
228
225
  end
229
226
 
@@ -6,11 +6,13 @@ module PactBroker
6
6
  def initialize
7
7
  consumer = OpenStruct.new(name: "placeholder-consumer", labels: [OpenStruct.new(name: "placeholder-consumer-label")])
8
8
  @provider = OpenStruct.new(name: "placeholder-provider", labels: [OpenStruct.new(name: "placeholder-provider-label")])
9
- @consumer_version = OpenStruct.new(number: "gggghhhhjjjjkkkkllll66667777888899990000", pacticipant: consumer, tags: [OpenStruct.new(name: "master")])
9
+ @consumer_version = OpenStruct.new(number: "gggghhhhjjjjkkkkllll66667777888899990000", pacticipant: consumer)
10
10
  @consumer_version_number = @consumer_version.number
11
11
  @created_at = DateTime.now
12
12
  @revision_number = 1
13
13
  @pact_version_sha = "5d445a4612743728dfd99ccd4210423c052bb9db"
14
+ @consumer_version_tag_names = ["dev"]
15
+ @consumer_version_branch_names = ["main"]
14
16
  end
15
17
  end
16
18
  end
@@ -1,4 +1,3 @@
1
- require "sequel"
2
1
  require "ostruct"
3
2
  require "pact_broker/logging"
4
3
  require "pact_broker/pacts/generate_sha"
@@ -9,7 +8,6 @@ require "pact_broker/domain"
9
8
  require "pact_broker/pacts/parse"
10
9
  require "pact_broker/pacts/latest_pact_publication_id_for_consumer_version"
11
10
  require "pact_broker/pacts/verifiable_pact"
12
- require "pact_broker/repositories/helpers"
13
11
  require "pact_broker/pacts/selected_pact"
14
12
  require "pact_broker/pacts/selector"
15
13
  require "pact_broker/pacts/selectors"
@@ -24,7 +22,6 @@ module PactBroker
24
22
  class Repository
25
23
  include PactBroker::Logging
26
24
  include PactBroker::Repositories
27
- include PactBroker::Repositories::Helpers
28
25
 
29
26
  def scope_for(scope)
30
27
  PactBroker.policy_scope!(scope)
@@ -53,7 +50,7 @@ module PactBroker
53
50
  consumer_version_order: params.fetch(:version).order,
54
51
  ).upsert
55
52
  update_latest_pact_publication_ids(pact_publication)
56
- pact_publication.to_domain
53
+ pact_publication.with_version_branches_and_tags.to_domain
57
54
  end
58
55
 
59
56
  def update id, params
@@ -75,9 +72,9 @@ module PactBroker
75
72
  created_at: Sequel.datetime_class.now
76
73
  ).upsert
77
74
  update_latest_pact_publication_ids(pact_publication)
78
- pact_publication.to_domain
75
+ pact_publication.with_version_branches_and_tags.to_domain
79
76
  else
80
- existing_model.to_domain
77
+ existing_model.with_version_branches_and_tags.to_domain
81
78
  end
82
79
  end
83
80
 
@@ -201,7 +198,7 @@ module PactBroker
201
198
  .sort
202
199
  end
203
200
 
204
- def find_latest_pact(consumer_name, provider_name, tag = nil)
201
+ def find_latest_pact(consumer_name, provider_name, tag = nil, branch_name = nil)
205
202
  query = scope_for(PactPublication)
206
203
  .eager_for_domain_with_content
207
204
  .select_all_qualified
@@ -212,6 +209,8 @@ module PactBroker
212
209
  query = query.untagged
213
210
  elsif tag
214
211
  query = query.for_consumer_version_tag_all_revisions(tag)
212
+ elsif branch_name
213
+ query = query.old_latest_for_consumer_branch(branch_name)
215
214
  end
216
215
  query.latest_by_consumer_version_order.all.collect(&:to_domain_with_content)[0]
217
216
  end
@@ -253,23 +252,24 @@ module PactBroker
253
252
  .reverse_order(:consumer_version_order, :revision_number)
254
253
  .limit(1)
255
254
 
255
+ # Must use .all to trigger the eager loading
256
256
  if consumer_version_number && !pact_version_sha
257
257
  pact_publication_by_consumer_version
258
- .first&.to_domain_with_content
258
+ .all.first&.to_domain_with_content
259
259
  elsif pact_version_sha && !consumer_version_number
260
260
  latest_pact_publication_by_sha
261
- .first&.to_domain_with_content
261
+ .all.first&.to_domain_with_content
262
262
  elsif consumer_version_number && pact_version_sha
263
- pact_publication = pact_publication_by_consumer_version.first
263
+ pact_publication = pact_publication_by_consumer_version.all.first
264
264
  if pact_publication && pact_publication.pact_version.sha == pact_version_sha
265
265
  pact_publication.to_domain_with_content
266
266
  else
267
- latest_pact_publication_by_sha.first&.to_domain_with_content
267
+ latest_pact_publication_by_sha.all.first&.to_domain_with_content
268
268
  end
269
269
  else
270
270
  pact_publication_by_consumer_version
271
271
  .reverse_order(:consumer_version_order, :revision_number)
272
- .first&.to_domain_with_content
272
+ .all.first&.to_domain_with_content
273
273
  end
274
274
  end
275
275
  # rubocop: enable Metrics/CyclomaticComplexity, Metrics/MethodLength
@@ -23,7 +23,7 @@ module PactBroker
23
23
  extend SquashPactsForVerification
24
24
 
25
25
  def find_latest_pact params
26
- pact_repository.find_latest_pact(params[:consumer_name], params[:provider_name], params[:tag])
26
+ pact_repository.find_latest_pact(params[:consumer_name], params[:provider_name], params[:tag], params[:branch_name])
27
27
  end
28
28
 
29
29
  def search_for_latest_pact params