pact_broker 2.107.1 → 2.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/Gemfile +5 -6
  4. data/README.md +1 -0
  5. data/db/migrations/20230615_add_integrations_contract_data_updated_at.rb +13 -0
  6. data/db/migrations/20230616_set_integrations_contract_data_updated_at.rb +11 -0
  7. data/db/migrations/20231002_add_version_id_index_to_released_version.rb +21 -0
  8. data/db/migrations/20231003_add_version_id_index_to_deployed_version.rb +21 -0
  9. data/docs/CONFIGURATION.md +10 -0
  10. data/lib/pact_broker/api/contracts/base_contract.rb +2 -1
  11. data/lib/pact_broker/api/contracts/dry_validation_errors_formatter.rb +2 -0
  12. data/lib/pact_broker/api/contracts/pagination_query_params_schema.rb +19 -0
  13. data/lib/pact_broker/api/contracts/publish_contracts_contract_contract.rb +29 -18
  14. data/lib/pact_broker/api/decorators/base_decorator.rb +40 -1
  15. data/lib/pact_broker/api/decorators/branch_decorator.rb +35 -0
  16. data/lib/pact_broker/api/decorators/branch_version_decorator.rb +16 -0
  17. data/lib/pact_broker/api/decorators/configuration.rb +19 -0
  18. data/lib/pact_broker/api/decorators/custom_error_problem_json_decorator.rb +1 -1
  19. data/lib/pact_broker/api/decorators/decorator_context_creator.rb +47 -2
  20. data/lib/pact_broker/api/decorators/deployed_version_decorator.rb +2 -1
  21. data/lib/pact_broker/api/decorators/deployed_versions_decorator.rb +2 -2
  22. data/lib/pact_broker/api/decorators/dry_validation_errors_decorator.rb +32 -0
  23. data/lib/pact_broker/api/decorators/dry_validation_errors_problem_json_decorator.rb +24 -0
  24. data/lib/pact_broker/api/decorators/embedded_branch_version_decorator.rb +2 -2
  25. data/lib/pact_broker/api/decorators/embedded_deployed_version_decorator.rb +30 -0
  26. data/lib/pact_broker/api/decorators/embedded_error_problem_json_decorator.rb +84 -0
  27. data/lib/pact_broker/api/decorators/embedded_released_version_decorator.rb +27 -0
  28. data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +0 -3
  29. data/lib/pact_broker/api/decorators/error_decorator.rb +30 -0
  30. data/lib/pact_broker/api/decorators/extended_pact_decorator.rb +6 -1
  31. data/lib/pact_broker/api/decorators/integration_decorator.rb +3 -2
  32. data/lib/pact_broker/api/decorators/integrations_decorator.rb +3 -0
  33. data/lib/pact_broker/api/decorators/notices_decorator.rb +11 -0
  34. data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +1 -5
  35. data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +0 -1
  36. data/lib/pact_broker/api/decorators/pact_webhooks_status_decorator.rb +0 -1
  37. data/lib/pact_broker/api/decorators/pacticipant_branches_decorator.rb +32 -0
  38. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +11 -1
  39. data/lib/pact_broker/api/decorators/{pacticipant_collection_decorator.rb → pacticipants_decorator.rb} +8 -4
  40. data/lib/pact_broker/api/decorators/pagination_links.rb +2 -2
  41. data/lib/pact_broker/api/decorators/publish_contract_decorator.rb +6 -1
  42. data/lib/pact_broker/api/decorators/released_versions_decorator.rb +2 -2
  43. data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +2 -2
  44. data/lib/pact_broker/api/decorators/validation_errors_decorator.rb +30 -0
  45. data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +4 -6
  46. data/lib/pact_broker/api/decorators/version_decorator.rb +5 -3
  47. data/lib/pact_broker/api/decorators/versions_decorator.rb +24 -14
  48. data/lib/pact_broker/api/decorators/webhook_decorator.rb +1 -1
  49. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +2 -0
  50. data/lib/pact_broker/api/middleware/configuration.rb +2 -0
  51. data/lib/pact_broker/api/pact_broker_urls.rb +18 -2
  52. data/lib/pact_broker/api/resources/after_reply.rb +15 -0
  53. data/lib/pact_broker/api/resources/all_webhooks.rb +3 -3
  54. data/lib/pact_broker/api/resources/badge_methods.rb +2 -1
  55. data/lib/pact_broker/api/resources/base_resource.rb +6 -4
  56. data/lib/pact_broker/api/resources/branch.rb +40 -0
  57. data/lib/pact_broker/api/resources/branch_versions.rb +59 -0
  58. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -1
  59. data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +1 -1
  60. data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +1 -1
  61. data/lib/pact_broker/api/resources/dashboard.rb +10 -0
  62. data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -1
  63. data/lib/pact_broker/api/resources/environment.rb +4 -0
  64. data/lib/pact_broker/api/resources/environments.rb +1 -1
  65. data/lib/pact_broker/api/resources/error_handler.rb +18 -52
  66. data/lib/pact_broker/api/resources/error_handling_methods.rb +40 -14
  67. data/lib/pact_broker/api/resources/error_response_generator.rb +23 -6
  68. data/lib/pact_broker/api/resources/event_methods.rb +15 -0
  69. data/lib/pact_broker/api/resources/filter_methods.rb +15 -0
  70. data/lib/pact_broker/api/resources/group.rb +11 -2
  71. data/lib/pact_broker/api/resources/index.rb +6 -0
  72. data/lib/pact_broker/api/resources/integrations.rb +18 -4
  73. data/lib/pact_broker/api/resources/latest_version.rb +2 -0
  74. data/lib/pact_broker/api/resources/pact.rb +16 -7
  75. data/lib/pact_broker/api/resources/pacticipant_branches.rb +67 -0
  76. data/lib/pact_broker/api/resources/pacticipants.rb +26 -7
  77. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +2 -2
  78. data/lib/pact_broker/api/resources/pagination_methods.rb +11 -1
  79. data/lib/pact_broker/api/resources/publish_contracts.rb +1 -1
  80. data/lib/pact_broker/api/resources/verifications.rb +9 -4
  81. data/lib/pact_broker/api/resources/versions.rb +12 -0
  82. data/lib/pact_broker/api.rb +5 -0
  83. data/lib/pact_broker/app.rb +10 -9
  84. data/lib/pact_broker/application_context.rb +29 -25
  85. data/lib/pact_broker/async/after_reply.rb +30 -0
  86. data/lib/pact_broker/config/runtime_configuration.rb +9 -21
  87. data/lib/pact_broker/config/runtime_configuration_coercion_methods.rb +32 -2
  88. data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
  89. data/lib/pact_broker/config/runtime_configuration_logging_methods.rb +15 -5
  90. data/lib/pact_broker/configuration.rb +29 -12
  91. data/lib/pact_broker/contracts/contract_to_publish.rb +4 -5
  92. data/lib/pact_broker/contracts/contracts_to_publish.rb +8 -0
  93. data/lib/pact_broker/contracts/service.rb +9 -3
  94. data/lib/pact_broker/dataset/page.rb +22 -0
  95. data/lib/pact_broker/dataset.rb +122 -0
  96. data/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb +47 -0
  97. data/lib/pact_broker/db/migrate_data.rb +1 -0
  98. data/lib/pact_broker/db/models.rb +1 -1
  99. data/lib/pact_broker/deployments/currently_deployed_version_id.rb +2 -5
  100. data/lib/pact_broker/deployments/deployed_version.rb +3 -3
  101. data/lib/pact_broker/deployments/environment.rb +0 -2
  102. data/lib/pact_broker/deployments/released_version.rb +4 -5
  103. data/lib/pact_broker/doc/views/index/pacticipant-branch.markdown +25 -0
  104. data/lib/pact_broker/domain/label.rb +6 -3
  105. data/lib/pact_broker/domain/pact.rb +10 -5
  106. data/lib/pact_broker/domain/pacticipant.rb +4 -34
  107. data/lib/pact_broker/domain/tag.rb +4 -5
  108. data/lib/pact_broker/domain/verification.rb +2 -4
  109. data/lib/pact_broker/domain/version.rb +12 -19
  110. data/lib/pact_broker/errors/error_reporter.rb +30 -0
  111. data/lib/pact_broker/errors.rb +2 -15
  112. data/lib/pact_broker/events/subscriber.rb +12 -4
  113. data/lib/pact_broker/feature_toggle.rb +1 -1
  114. data/lib/pact_broker/groups/service.rb +38 -5
  115. data/lib/pact_broker/index/service.rb +1 -2
  116. data/lib/pact_broker/integrations/event_listener.rb +23 -0
  117. data/lib/pact_broker/integrations/integration.rb +24 -2
  118. data/lib/pact_broker/integrations/repository.rb +34 -1
  119. data/lib/pact_broker/integrations/service.rb +17 -18
  120. data/lib/pact_broker/labels/repository.rb +4 -8
  121. data/lib/pact_broker/locale/en.yml +5 -0
  122. data/lib/pact_broker/logging.rb +10 -0
  123. data/lib/pact_broker/matrix/every_row.rb +58 -40
  124. data/lib/pact_broker/matrix/integration_row.rb +95 -0
  125. data/lib/pact_broker/matrix/integrations_repository.rb +133 -0
  126. data/lib/pact_broker/matrix/matrix_row.rb +88 -0
  127. data/lib/pact_broker/matrix/matrix_row_dataset_module.rb +185 -0
  128. data/lib/pact_broker/matrix/matrix_row_instance_methods.rb +150 -0
  129. data/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +83 -0
  130. data/lib/pact_broker/matrix/parse_query.rb +1 -0
  131. data/lib/pact_broker/matrix/repository.rb +62 -285
  132. data/lib/pact_broker/matrix/resolved_selector.rb +13 -4
  133. data/lib/pact_broker/matrix/resolved_selector_builder.rb +84 -0
  134. data/lib/pact_broker/matrix/resolved_selectors_builder.rb +39 -0
  135. data/lib/pact_broker/matrix/row_ignorer.rb +36 -0
  136. data/lib/pact_broker/matrix/selector_ignorer.rb +59 -0
  137. data/lib/pact_broker/matrix/selector_resolver.rb +130 -0
  138. data/lib/pact_broker/metrics/service.rb +4 -9
  139. data/lib/pact_broker/pacticipants/latest_version_for_pacticipant_eager_loader.rb +33 -0
  140. data/lib/pact_broker/pacticipants/repository.rb +7 -9
  141. data/lib/pact_broker/pacticipants/service.rb +2 -2
  142. data/lib/pact_broker/pacts/generate_sha.rb +9 -4
  143. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +2 -4
  144. data/lib/pact_broker/pacts/metadata.rb +3 -1
  145. data/lib/pact_broker/pacts/pact_params.rb +1 -1
  146. data/lib/pact_broker/pacts/pact_publication.rb +23 -5
  147. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +5 -1
  148. data/lib/pact_broker/pacts/pact_version.rb +2 -3
  149. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +2 -5
  150. data/lib/pact_broker/pacts/placeholder_pact.rb +3 -1
  151. data/lib/pact_broker/pacts/repository.rb +12 -12
  152. data/lib/pact_broker/pacts/service.rb +12 -13
  153. data/lib/pact_broker/repositories.rb +9 -1
  154. data/lib/pact_broker/string_refinements.rb +4 -0
  155. data/lib/pact_broker/tags/head_pact_tags.rb +2 -5
  156. data/lib/pact_broker/tags/repository.rb +3 -7
  157. data/lib/pact_broker/test/test_data_builder.rb +57 -2
  158. data/lib/pact_broker/ui/controllers/base_controller.rb +4 -2
  159. data/lib/pact_broker/ui/controllers/groups.rb +2 -1
  160. data/lib/pact_broker/ui/view_models/matrix_line.rb +4 -4
  161. data/lib/pact_broker/ui/views/groups/show.html.erb +2 -1
  162. data/lib/pact_broker/ui.rb +20 -9
  163. data/lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb +0 -3
  164. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +2 -4
  165. data/lib/pact_broker/verifications/repository.rb +2 -5
  166. data/lib/pact_broker/verifications/sequence.rb +1 -4
  167. data/lib/pact_broker/verifications/service.rb +4 -0
  168. data/lib/pact_broker/version.rb +1 -1
  169. data/lib/pact_broker/versions/branch.rb +2 -5
  170. data/lib/pact_broker/versions/branch_head.rb +0 -3
  171. data/lib/pact_broker/versions/branch_repository.rb +76 -0
  172. data/lib/pact_broker/versions/branch_service.rb +13 -25
  173. data/lib/pact_broker/versions/branch_version.rb +0 -3
  174. data/lib/pact_broker/versions/branch_version_repository.rb +17 -0
  175. data/lib/pact_broker/versions/repository.rb +19 -2
  176. data/lib/pact_broker/versions/sequence.rb +1 -3
  177. data/lib/pact_broker/versions/service.rb +4 -0
  178. data/lib/pact_broker/webhooks/execution.rb +3 -7
  179. data/lib/pact_broker/webhooks/job.rb +16 -7
  180. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +2 -2
  181. data/lib/pact_broker/webhooks/repository.rb +0 -1
  182. data/lib/pact_broker/webhooks/trigger_service.rb +11 -12
  183. data/lib/pact_broker/webhooks/triggered_webhook.rb +3 -4
  184. data/lib/pact_broker/webhooks/webhook.rb +2 -2
  185. data/lib/pact_broker/webhooks/webhook_event.rb +2 -5
  186. data/lib/rack/pact_broker/add_cache_header.rb +14 -0
  187. data/lib/rack/pact_broker/application_context.rb +16 -0
  188. data/lib/rack/pact_broker/configurable_make_it_later.rb +1 -1
  189. data/lib/rack/pact_broker/invalid_uri_protection.rb +19 -3
  190. data/lib/webmachine/describe_routes.rb +55 -39
  191. data/lib/webmachine/render_error_monkey_patch.rb +13 -4
  192. data/pact_broker.gemspec +5 -5
  193. metadata +54 -31
  194. data/lib/pact_broker/api/decorators/decorator_context.rb +0 -22
  195. data/lib/pact_broker/matrix/query_builder.rb +0 -90
  196. data/lib/pact_broker/matrix/query_ids.rb +0 -40
  197. data/lib/pact_broker/matrix/quick_row.rb +0 -458
  198. data/lib/pact_broker/relationships/groupify.rb +0 -45
  199. data/lib/pact_broker/repositories/helpers.rb +0 -96
  200. data/lib/pact_broker/repositories/page.rb +0 -24
  201. data/lib/pact_broker/tags/tag_with_latest_flag.rb +0 -28
@@ -0,0 +1,150 @@
1
+ # The instance methods
2
+ module PactBroker
3
+ module Matrix
4
+ module MatrixRowInstanceMethods
5
+ def pact_version_sha
6
+ pact_version.sha
7
+ end
8
+
9
+ def pact_revision_number
10
+ pact_publication.revision_number
11
+ end
12
+
13
+ def verification_number
14
+ verification&.number
15
+ end
16
+
17
+ def success
18
+ verification&.success
19
+ end
20
+
21
+ def pact_created_at
22
+ pact_publication.created_at
23
+ end
24
+
25
+ def verification_executed_at
26
+ verification&.execution_date
27
+ end
28
+
29
+ # Add logic for ignoring case
30
+ def <=> other
31
+ comparisons = [
32
+ compare_name_asc(consumer_name, other.consumer_name),
33
+ compare_number_desc(consumer_version_order, other.consumer_version_order),
34
+ compare_number_desc(pact_revision_number, other.pact_revision_number),
35
+ compare_name_asc(provider_name, other.provider_name),
36
+ compare_number_desc(provider_version_order, other.provider_version_order),
37
+ compare_number_desc(verification_id, other.verification_id)
38
+ ]
39
+
40
+ comparisons.find{|c| c != 0 } || 0
41
+ end
42
+
43
+ def compare_name_asc name1, name2
44
+ name1 <=> name2
45
+ end
46
+
47
+ def to_s
48
+ "#{consumer_name} v#{consumer_version_number} #{provider_name} #{provider_version_number} #{success}"
49
+ end
50
+
51
+ def compare_number_desc number1, number2
52
+ if number1 && number2
53
+ number2 <=> number1
54
+ elsif number1
55
+ 1
56
+ else
57
+ -1
58
+ end
59
+ end
60
+
61
+ def eql?(obj)
62
+ (obj.class == model) && (obj.values == values)
63
+ end
64
+
65
+ def pacticipant_names
66
+ [consumer_name, provider_name]
67
+ end
68
+
69
+ def involves_pacticipant_with_name?(pacticipant_name)
70
+ pacticipant_name.include?(pacticipant_name)
71
+ end
72
+
73
+ def provider_version_id
74
+ # null when not verified
75
+ values[:provider_version_id]
76
+ end
77
+
78
+ def verification_id
79
+ # null when not verified
80
+ return_or_raise_if_not_set(:verification_id)
81
+ end
82
+
83
+ def consumer_name
84
+ consumer.name
85
+ end
86
+
87
+ def consumer_version_number
88
+ consumer_version.number
89
+ end
90
+
91
+ def consumer_version_branch_versions
92
+ consumer_version.branch_versions
93
+ end
94
+
95
+ def consumer_version_deployed_versions
96
+ consumer_version.current_deployed_versions
97
+ end
98
+
99
+ def consumer_version_released_versions
100
+ consumer_version.current_supported_released_versions
101
+ end
102
+
103
+ def consumer_version_order
104
+ consumer_version.order
105
+ end
106
+
107
+ def provider_name
108
+ provider.name
109
+ end
110
+
111
+ def provider_version_number
112
+ provider_version&.number
113
+ end
114
+
115
+ def provider_version_branch_versions
116
+ provider_version&.branch_versions || []
117
+ end
118
+
119
+ def provider_version_deployed_versions
120
+ provider_version&.current_deployed_versions || []
121
+ end
122
+
123
+ def provider_version_released_versions
124
+ provider_version&.current_supported_released_versions || []
125
+ end
126
+
127
+ def provider_version_order
128
+ provider_version&.order
129
+ end
130
+
131
+ def last_action_date
132
+ return_or_raise_if_not_set(:last_action_date)
133
+ end
134
+
135
+ def has_verification?
136
+ !!verification_id
137
+ end
138
+
139
+ # This model needs the verifications and pacticipants joined to it
140
+ # before it can be used, as it's not a "real" model.
141
+ def return_or_raise_if_not_set(key)
142
+ if values.key?(key)
143
+ values[key]
144
+ else
145
+ raise "Required table not joined"
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,83 @@
1
+ #
2
+ # The dataset methods to be included in the MatrixRow::Verification dataset module
3
+ # and the EveryRow::Verification dataset module.
4
+ # Expects the method `select_verification_columns_with_aliases` to be defined on the class
5
+ #
6
+ module PactBroker
7
+ module Matrix
8
+ module MatrixRowVerificationDatasetModule
9
+ # Verifications for the provider versions matching the given selectors, where the consumers match the pacticipants in the given selectors.
10
+ # @public
11
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] selectors
12
+ # @return [Sequel::Dataset<MatrixRow>]
13
+ def matching_only_selectors_as_provider(resolved_selectors)
14
+ [
15
+ matching_only_selectors_as_provider_where_only_pacticipant_name_in_selector(resolved_selectors),
16
+ matching_only_selectors_as_provider_where_not_only_pacticipant_name_in_selector(resolved_selectors)
17
+ ].compact.reduce(&:union)
18
+ end
19
+
20
+ # @public
21
+ # @return [Sequel::Dataset<Verification>, nil]
22
+ def matching_selectors_as_provider_for_any_consumer(resolved_selectors)
23
+ select_verification_columns_with_aliases
24
+ .inner_join_versions_for_selectors_as_provider(resolved_selectors)
25
+ end
26
+
27
+ # Return verifications where the provider is described by any of the resolved_selectors *that only specify the pacticipant NAME*,
28
+ # AND the consumer is described by any of the resolved selectors.
29
+ # If the original selector only specified the pacticipant name, we don't need to join to the versions table to identify the required verifications.
30
+ # Return nil if there are no resolved selectors where only the pacticipant name is specified.
31
+ # @private
32
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
33
+ # @return [Sequel::Dataset<Verification>, nil]
34
+ def matching_only_selectors_as_provider_where_only_pacticipant_name_in_selector(resolved_selectors)
35
+ all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq
36
+ pacticipant_ids_for_pacticipant_only_selectors = resolved_selectors.select(&:only_pacticipant_name_specified?).collect(&:pacticipant_id).uniq
37
+
38
+ if pacticipant_ids_for_pacticipant_only_selectors.any?
39
+ select_verification_columns_with_aliases
40
+ .where(provider_id: pacticipant_ids_for_pacticipant_only_selectors)
41
+ .where(consumer_id: all_pacticipant_ids)
42
+ end
43
+ end
44
+
45
+ # Return verifications where the provider *version* is described by any of the resolved_selectors
46
+ # *that specify more than just the pacticipant name*,
47
+ # AND the consumer is described by any of the resolved selectors.
48
+ # If the selector uses any of the tag/branch/environment/latest attributes, we need to join to the versions table to identify the required verifications.
49
+ # Return nil if there are no resolved selectors where anything other than the pacticipant name is specified.
50
+ # @private
51
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
52
+ # @return [Sequel::Dataset<Verification>, nil]
53
+ def matching_only_selectors_as_provider_where_not_only_pacticipant_name_in_selector(resolved_selectors)
54
+ # get the UnresolvedSelector objects back out of the resolved_selectors because the Version.for_selector() method uses the UnresolvedSelector
55
+ all_pacticipant_ids = resolved_selectors.collect(&:pacticipant_id).uniq
56
+ resolved_selectors_with_versions_specified = resolved_selectors.reject(&:only_pacticipant_name_specified?)
57
+
58
+ if resolved_selectors_with_versions_specified.any?
59
+ select_verification_columns_with_aliases
60
+ .inner_join_versions_for_selectors_as_provider(resolved_selectors_with_versions_specified)
61
+ .where(consumer_id: all_pacticipant_ids)
62
+ end
63
+ end
64
+
65
+ # Don't think it's worth splitting this into 2 different queries for selectors with only pacticipant name/selectors with version properties,
66
+ # as it's unlikely for there ever to be a query through the UI or CLI that results in 1 selector which only has a pacticipant name in it.
67
+ # @private
68
+ # @param [Array<PactBroker::Matrix::ResolvedSelector>] resolved_selectors
69
+ # @return [Sequel::Dataset<Verification>]
70
+ def inner_join_versions_for_selectors_as_provider(resolved_selectors)
71
+ # get the UnresolvedSelector objects back out of the resolved_selectors because the Version.for_selector() method uses the UnresolvedSelector
72
+ unresolved_selectors = resolved_selectors.collect(&:original_selector).uniq
73
+ versions = PactBroker::Domain::Version.ids_for_selectors(unresolved_selectors)
74
+ join_versions_dataset(versions)
75
+ end
76
+
77
+ # @private
78
+ def join_versions_dataset(versions_dataset)
79
+ join(versions_dataset, { Sequel[self.model.table_name][:provider_version_id] => Sequel[:versions][:id] }, table_alias: :versions)
80
+ end
81
+ end
82
+ end
83
+ end
@@ -26,6 +26,7 @@ module PactBroker
26
26
  options[:latestby] = params["latestby"]
27
27
  end
28
28
 
29
+ # Don't think this is used anywhere...
29
30
  if params.key?("days") && params["days"] != ""
30
31
  options[:days] = params["days"].to_i
31
32
  end
@@ -1,71 +1,50 @@
1
- require "pact_broker/logging"
2
- require "pact_broker/repositories/helpers"
3
- require "pact_broker/matrix/quick_row"
1
+ require "pact_broker/matrix/matrix_row"
4
2
  require "pact_broker/matrix/every_row"
5
- require "pact_broker/error"
6
3
  require "pact_broker/matrix/query_results"
7
4
  require "pact_broker/matrix/integration"
8
5
  require "pact_broker/matrix/query_results_with_deployment_status_summary"
9
- require "pact_broker/matrix/resolved_selector"
10
6
  require "pact_broker/matrix/unresolved_selector"
11
7
  require "pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version"
8
+ require "pact_broker/matrix/integrations_repository"
9
+ require "pact_broker/matrix/resolved_selectors_builder"
10
+ require "pact_broker/matrix/row_ignorer"
11
+ require "pact_broker/matrix/integrations_repository"
12
12
 
13
13
  module PactBroker
14
14
  module Matrix
15
-
16
- class Error < PactBroker::Error; end
17
-
18
15
  class Repository
19
- include PactBroker::Repositories::Helpers
20
16
  include PactBroker::Repositories
21
- include PactBroker::Logging
22
-
23
- # TODO move latest verification logic in to database
24
17
 
25
18
  # Used when using table_print to output query results
26
19
  TP_COLS = [ :consumer_version_number, :pact_revision_number, :provider_version_number, :verification_number]
27
20
 
28
- GROUP_BY_PROVIDER_VERSION_NUMBER = [:consumer_name, :consumer_version_number, :provider_name, :provider_version_number]
29
- GROUP_BY_PROVIDER = [:consumer_name, :consumer_version_number, :provider_name]
30
- GROUP_BY_PACT = [:consumer_name, :provider_name]
31
-
32
- # rubocop: disable Metrics/CyclomaticComplexity
33
- def find_ids_for_pacticipant_names params
34
- criteria = {}
35
-
36
- if params[:consumer_name] || params[:provider_name]
37
- if params[:consumer_name]
38
- pacticipant = PactBroker::Domain::Pacticipant.where(name_like(:name, params[:consumer_name])).single_record
39
- criteria[:consumer_id] = pacticipant.id if pacticipant
40
- end
41
-
42
- if params[:provider_name]
43
- pacticipant = PactBroker::Domain::Pacticipant.where(name_like(:name, params[:provider_name])).single_record
44
- criteria[:provider_id] = pacticipant.id if pacticipant
45
- end
46
- end
47
-
48
- if params[:pacticipant_name]
49
- pacticipant = PactBroker::Domain::Pacticipant.where(name_like(:name, params[:pacticipant_name])).single_record
50
- criteria[:pacticipant_id] = pacticipant.id if pacticipant
51
- end
52
-
53
- criteria[:tag_name] = params[:tag_name] if params[:tag_name].is_a?(String) # Could be a sym from resource parameters in api.rb
54
- criteria
55
- end
56
- # rubocop: enable Metrics/CyclomaticComplexity
57
-
58
- # Return the latest matrix row (pact/verification) for each consumer_version_number/provider_version_number
59
- def find specified_selectors, options = {}
60
- resolved_ignore_selectors = resolve_ignore_selectors(options)
61
- resolved_specified_selectors = resolve_versions_and_add_ids(specified_selectors, :specified, resolved_ignore_selectors)
62
- integrations = find_integrations_for_specified_selectors(resolved_specified_selectors, options)
63
- resolved_selectors = add_any_inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
64
- considered_rows, ignored_rows = find_considered_and_ignored_rows(resolved_selectors, resolved_ignore_selectors, options)
65
- QueryResults.new(considered_rows.sort, ignored_rows.sort, specified_selectors, options, resolved_selectors, resolved_ignore_selectors, integrations)
66
- end
67
-
68
- def find_for_consumer_and_provider pacticipant_1_name, pacticipant_2_name
21
+ GROUP_BY_CONSUMER_VERSION_AND_PROVIDER_VERSION = [:consumer_name, :consumer_version_number, :provider_name, :provider_version_number]
22
+ GROUP_BY_CONSUMER_VERSION_AND_PROVIDER = [:consumer_name, :consumer_version_number, :provider_name]
23
+ GROUP_BY_CONSUMER_AND_PROVIDER = [:consumer_name, :provider_name]
24
+
25
+ # THE METHOD for querying the Matrix
26
+ # @param [Array<PactBroker::Matrix::UnresolvedSelector>] unresolved_specified_selectors
27
+ # @param [Hash] options
28
+ def find(unresolved_specified_selectors, options = {})
29
+ infer_selectors = infer_selectors_for_integrations?(options)
30
+ resolved_selectors_builder = ResolvedSelectorsBuilder.new
31
+ resolved_selectors_builder.resolve_selectors(unresolved_specified_selectors, options.fetch(:ignore_selectors, []))
32
+ integrations = matrix_integration_repository.find_integrations_for_specified_selectors(resolved_selectors_builder.specified_selectors, infer_selectors)
33
+ resolved_selectors_builder.resolve_inferred_selectors(integrations, options) if infer_selectors
34
+
35
+ considered_rows, ignored_rows = find_considered_and_ignored_rows(resolved_selectors_builder.all_selectors, resolved_selectors_builder.ignore_selectors, options)
36
+ QueryResults.new(
37
+ considered_rows.sort,
38
+ ignored_rows.sort,
39
+ unresolved_specified_selectors,
40
+ options,
41
+ resolved_selectors_builder.all_selectors,
42
+ resolved_selectors_builder.ignore_selectors,
43
+ integrations
44
+ )
45
+ end
46
+
47
+ def find_for_consumer_and_provider(pacticipant_1_name, pacticipant_2_name)
69
48
  selectors = [ UnresolvedSelector.new(pacticipant_name: pacticipant_1_name), UnresolvedSelector.new(pacticipant_name: pacticipant_2_name)]
70
49
  options = { latestby: "cvpv" }
71
50
  find(selectors, options)
@@ -73,88 +52,26 @@ module PactBroker
73
52
 
74
53
  private
75
54
 
76
- def find_considered_and_ignored_rows(resolved_selectors, resolved_ignore_selectors, options)
77
- rows = query_matrix(resolved_selectors, options)
78
- rows = apply_latestby(options, rows)
79
- rows = apply_success_filter(rows, options)
80
- considered_rows, ignored_rows = split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors)
81
- return considered_rows, ignored_rows
55
+ def matrix_integration_repository
56
+ PactBroker::Matrix::IntegrationsRepository.new
82
57
  end
83
58
 
84
- def find_integrations_for_specified_selectors(resolved_specified_selectors, options)
85
- if infer_selectors_for_integrations?(options)
86
- find_integrations_for_specified_selectors_with_inferred_integrations(resolved_specified_selectors)
87
- else
88
- find_integrations_for_specified_selectors_only(resolved_specified_selectors)
89
- end
90
- end
91
-
92
- def find_integrations_for_specified_selectors_only(resolved_specified_selectors)
93
- specified_pacticipant_names = resolved_specified_selectors.collect(&:pacticipant_name)
94
- base_model_for_integrations
95
- .distinct_integrations(resolved_specified_selectors, false)
96
- .collect(&:to_hash)
97
- .collect do | integration_hash |
98
- required = is_a_row_for_this_integration_required?(specified_pacticipant_names, integration_hash[:consumer_name])
99
- Integration.from_hash(integration_hash.merge(required: required))
100
- end
101
- end
102
-
103
- def find_integrations_for_specified_selectors_with_inferred_integrations(resolved_specified_selectors)
104
- integrations = integrations_where_specified_selector_is_consumer(resolved_specified_selectors) +
105
- integrations_where_specified_selector_is_provider(resolved_specified_selectors)
106
- deduplicate_integrations(integrations)
107
- end
108
-
109
- def integrations_where_specified_selector_is_consumer(resolved_specified_selectors)
110
- resolved_specified_selectors.flat_map do | selector |
111
- base_model_for_integrations
112
- .distinct_integrations_for_selector_as_consumer(selector)
113
- .all
114
- .collect do | integration |
115
- Integration.from_hash(
116
- consumer_id: integration[:consumer_id],
117
- consumer_name: integration[:consumer_name],
118
- provider_id: integration[:provider_id],
119
- provider_name: integration[:provider_name],
120
- required: true # synchronous consumer requires the provider to be present
121
- )
122
- end
123
- end
124
- end
125
-
126
- # Why does the consumer equivalent of this method use the QuickRow distinct_integrations_for_selector_as_consumer
127
- # while this method uses the Integration?
128
- def integrations_where_specified_selector_is_provider(resolved_specified_selectors)
129
- integrations_involving_specified_providers = PactBroker::Integrations::Integration
130
- .where(provider_id: resolved_specified_selectors.collect(&:pacticipant_id))
131
- .eager(:consumer, :provider)
132
- .all
133
-
134
- integrations_involving_specified_providers.collect do | integration |
135
- Integration.from_hash(
136
- consumer_id: integration.consumer.id,
137
- consumer_name: integration.consumer.name,
138
- provider_id: integration.provider.id,
139
- provider_name: integration.provider.name,
140
- required: false # synchronous provider does not require the consumer to be present
141
- )
142
- end
143
- end
144
-
145
- def deduplicate_integrations(integrations)
146
- integrations
147
- .group_by{ | integration| [integration.consumer_id, integration.provider_id] }
148
- .values
149
- .collect { | duplicate_integrations | duplicate_integrations.find(&:required?) || duplicate_integrations.first }
59
+ # If the user has specified --to TAG or --to-environment ENVIRONMENT in the CLI
60
+ # (or nothing, which to defaults to latest=true - "with the latest version of the other integrated applications")
61
+ # we need to work out what the integrations are between the specified selectors and the other pacticipant versions
62
+ # in the target environment/branches/tags.
63
+ # @param [Hash] options the matrix options
64
+ # @return [Boolean]
65
+ def infer_selectors_for_integrations?(options)
66
+ options[:latest] || !!options[:tag] || !!options[:branch] || !!options[:environment_name] || options[:main_branch]
150
67
  end
151
68
 
152
- def add_any_inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
153
- if infer_selectors_for_integrations?(options)
154
- resolved_specified_selectors + inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
155
- else
156
- resolved_specified_selectors
157
- end
69
+ def find_considered_and_ignored_rows(all_resolved_selectors, resolved_ignore_selectors, options)
70
+ rows = query_matrix(all_resolved_selectors, options)
71
+ rows = apply_latestby(options, rows)
72
+ rows = apply_success_filter(rows, options)
73
+ considered_rows, ignored_rows = RowIgnorer.split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors)
74
+ return considered_rows, ignored_rows
158
75
  end
159
76
 
160
77
  def apply_success_filter(rows_with_latest_by_applied, options)
@@ -167,36 +84,32 @@ module PactBroker
167
84
  end
168
85
  end
169
86
 
170
- # If a specified pacticipant is a consumer, then its provider is required to be deployed
171
- # to the same environment before the consumer can be deployed.
172
- # If a specified pacticipant is a provider only, then it may be deployed
173
- # without the consumer being present.
174
- def is_a_row_for_this_integration_required?(specified_pacticipant_names, consumer_name)
175
- specified_pacticipant_names.include?(consumer_name)
176
- end
177
-
178
87
  # rubocop: disable Metrics/CyclomaticComplexity
179
88
  # It would be nicer to do this in the SQL, but it requires time that I don't have at the moment
180
- def apply_latestby options, lines
89
+ def apply_latestby(options, lines)
181
90
  return lines unless options[:latestby]
182
91
  group_by_columns = case options[:latestby]
183
- when "cvpv" then GROUP_BY_PROVIDER_VERSION_NUMBER
184
- when "cvp" then GROUP_BY_PROVIDER
185
- when "cp" then GROUP_BY_PACT
92
+ when "cvpv" then GROUP_BY_CONSUMER_VERSION_AND_PROVIDER_VERSION
93
+ when "cvp" then GROUP_BY_CONSUMER_VERSION_AND_PROVIDER
94
+ when "cp" then GROUP_BY_CONSUMER_AND_PROVIDER
186
95
  end
187
96
 
188
97
  # The group with the nil provider_version_numbers will be the results of the left outer join
189
98
  # that don't have verifications, so we need to include them all.
190
- lines.group_by{|line| group_by_columns.collect{|key| line.send(key) }}
99
+
100
+ lines
101
+ .group_by{ |line| group_by_columns.collect{ |key| line.send(key) } }
191
102
  .values
192
- .collect{ | line | line.first.provider_version_number.nil? ? line : line.sort_by(&:provider_version_order).last }
103
+ .collect { | grouped_lines |
104
+ grouped_lines.first.provider_version_number.nil? ? grouped_lines.first : grouped_lines.sort_by(&:provider_version_order).last
105
+ }
193
106
  .flatten
194
107
  end
195
108
  # rubocop: enable Metrics/CyclomaticComplexity
196
109
 
197
- def query_matrix selectors, options
198
- query = base_model(options).select_all_columns
199
- .matching_selectors(selectors)
110
+ def query_matrix(all_resolved_selectors, options)
111
+ query = base_model(options)
112
+ .matching_selectors(all_resolved_selectors, limit: options[:limit])
200
113
  .order_by_last_action_date
201
114
 
202
115
  query = query.limit(options[:limit]) if options[:limit]
@@ -204,143 +117,7 @@ module PactBroker
204
117
  end
205
118
 
206
119
  def base_model(options = {})
207
- options[:latestby] ? QuickRow : EveryRow
208
- end
209
-
210
- def resolve_ignore_selectors(options)
211
- resolve_versions_and_add_ids(options.fetch(:ignore_selectors, []), :ignored)
212
- end
213
-
214
- # To make it easy for pf to override
215
- def base_model_for_integrations
216
- QuickRow
217
- end
218
-
219
- # Find the version number for selectors with the latest and/or tag specified
220
- def resolve_versions_and_add_ids(unresolved_selectors, selector_type, resolved_ignore_selectors = [])
221
- names = unresolved_selectors.collect(&:pacticipant_name)
222
- pacticipants = PactBroker::Domain::Pacticipant.where(name: names).to_a.group_by(&:name).transform_values(&:first)
223
- unresolved_selectors.collect do | unresolved_selector |
224
- pacticipant = pacticipants[unresolved_selector.pacticipant_name]
225
- if pacticipant
226
- versions = find_versions_for_selector(unresolved_selector)
227
- build_resolved_selectors(pacticipant, versions, unresolved_selector, selector_type, resolved_ignore_selectors)
228
- else
229
- selector_for_non_existing_pacticipant(unresolved_selector, selector_type)
230
- end
231
- end.flatten
232
- end
233
-
234
- def find_versions_for_selector(selector)
235
- # For selectors that just set the pacticipant name, there's no need to resolve the version -
236
- # only the pacticipant ID will be used in the query
237
- return nil if selector.all_for_pacticipant?
238
- versions = version_repository.find_versions_for_selector(selector)
239
-
240
- if selector.latest
241
- [versions.first]
242
- else
243
- versions.empty? ? [nil] : versions
244
- end
245
- end
246
-
247
- # When a single selector specifies multiple versions (eg. "all prod pacts"), this expands
248
- # the single selector into one selector for each version.
249
- def build_resolved_selectors(pacticipant, versions, unresolved_selector, selector_type, resolved_ignore_selectors)
250
- if versions
251
- one_of_many = versions.compact.size > 1
252
- versions.collect do | version |
253
- if version
254
- selector_for_found_version(pacticipant, version, unresolved_selector, selector_type, one_of_many, resolved_ignore_selectors)
255
- else
256
- selector_for_non_existing_version(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
257
- end
258
- end
259
- else
260
- selector_for_all_versions_of_a_pacticipant(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
261
- end
262
- end
263
-
264
- # The user has specified --to TAG or --to-environment ENVIRONMENT in the CLI
265
- # (or nothing, which to defaults to latest=true - "with the latest version of the other integrated applications")
266
- def infer_selectors_for_integrations?(options)
267
- options[:latest] || options[:tag] || options[:branch] || options[:environment_name] || options[:main_branch]
268
- end
269
-
270
- # When only one selector is specified, (eg. checking to see if Foo version 2 can be deployed to prod),
271
- # need to look up all integrated pacticipants, and determine their relevant (eg. latest prod) versions
272
- def inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
273
- all_pacticipant_names = integrations.collect(&:pacticipant_names).flatten.uniq
274
- specified_names = resolved_specified_selectors.collect{ |s| s[:pacticipant_name] }
275
- inferred_pacticipant_names = all_pacticipant_names - specified_names
276
- build_inferred_selectors(inferred_pacticipant_names, resolved_ignore_selectors, options)
277
- end
278
-
279
- def build_inferred_selectors(inferred_pacticipant_names, resolved_ignore_selectors, options)
280
- selectors = inferred_pacticipant_names.collect do | pacticipant_name |
281
- selector = UnresolvedSelector.new(pacticipant_name: pacticipant_name)
282
- selector.tag = options[:tag] if options[:tag]
283
- selector.branch = options[:branch] if options[:branch]
284
- selector.main_branch = options[:main_branch] if options[:main_branch]
285
- selector.latest = options[:latest] if options[:latest]
286
- selector.environment_name = options[:environment_name] if options[:environment_name]
287
- selector
288
- end
289
- resolve_versions_and_add_ids(selectors, :inferred, resolved_ignore_selectors)
290
- end
291
-
292
- def selector_for_non_existing_version(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
293
- ignore = resolved_ignore_selectors.any? do | s |
294
- s.pacticipant_id == pacticipant.id && s.only_pacticipant_name_specified?
295
- end
296
- ResolvedSelector.for_pacticipant_and_non_existing_version(pacticipant, unresolved_selector, selector_type, ignore)
297
- end
298
-
299
- # rubocop: disable Metrics/ParameterLists
300
- def selector_for_found_version(pacticipant, version, unresolved_selector, selector_type, one_of_many, resolved_ignore_selectors)
301
- ignore = resolved_ignore_selectors.any? do | s |
302
- s.pacticipant_id == pacticipant.id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == version.id)
303
- end
304
- ResolvedSelector.for_pacticipant_and_version(pacticipant, version, unresolved_selector, selector_type, ignore, one_of_many)
305
- end
306
- # rubocop: enable Metrics/ParameterLists
307
-
308
- def selector_for_all_versions_of_a_pacticipant(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
309
- # Doesn't make sense to ignore this, as you can't have a can-i-deploy query
310
- # for "all versions of a pacticipant". But whatever.
311
- ignore = resolved_ignore_selectors.any? do | s |
312
- s.pacticipant_id == pacticipant.id && s.only_pacticipant_name_specified?
313
- end
314
- ResolvedSelector.for_pacticipant(pacticipant, unresolved_selector, selector_type, ignore)
315
- end
316
-
317
- # only relevant for ignore selectors, validation stops this happening for the normal
318
- # selectors
319
- def selector_for_non_existing_pacticipant(unresolved_selector, selector_type)
320
- ResolvedSelector.for_non_existing_pacticipant(unresolved_selector, selector_type, false)
321
- end
322
-
323
- def split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors)
324
- if resolved_ignore_selectors.any?
325
- considered, ignored = [], []
326
- rows.each do | row |
327
- if ignore_row?(resolved_ignore_selectors, row)
328
- ignored << row
329
- else
330
- considered << row
331
- end
332
- end
333
- return considered, ignored
334
- else
335
- return rows, []
336
- end
337
- end
338
-
339
- def ignore_row?(resolved_ignore_selectors, row)
340
- resolved_ignore_selectors.any? do | s |
341
- s.pacticipant_id == row.consumer_id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == row.consumer_version_id) ||
342
- s.pacticipant_id == row.provider_id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == row.provider_version_id)
343
- end
120
+ options[:latestby] ? MatrixRow : EveryRow
344
121
  end
345
122
  end
346
123
  end