pact_broker 2.105.0 → 2.107.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +57 -1
  3. data/Gemfile +3 -0
  4. data/README.md +1 -1
  5. data/db/migrations/20221130_add_provider_version_id_index_to_verifications.rb +28 -0
  6. data/db/migrations/20221208_add_index_to_pact_version_provider_tag_successful_verifications.rb +21 -0
  7. data/db/migrations/20221215_add_prov_ver_id_ndx_to_latest_verifi_id_for_pact_ver_and_prov_ver.rb +21 -0
  8. data/db/migrations/20230131_add_cons_ver_id_ndx_to_latest_pp_id_for_cons_ver.rb +21 -0
  9. data/db/migrations/20230216_add_branch_heads_branch_version_id_index.rb +21 -0
  10. data/db/migrations/20230428_add_index_for_webhook_executions_pact_publication_id.rb +17 -0
  11. data/docs/CONFIGURATION.md +1 -1
  12. data/docs/api/PAGINATION.md +43 -0
  13. data/lib/pact_broker/api/contracts/base_contract.rb +22 -7
  14. data/lib/pact_broker/api/contracts/can_i_deploy_query_schema.rb +34 -0
  15. data/lib/pact_broker/api/contracts/configuration.rb +2 -0
  16. data/lib/pact_broker/api/contracts/consumer_version_selector_contract.rb +140 -0
  17. data/lib/pact_broker/api/contracts/dry_validation_errors_formatter.rb +50 -0
  18. data/lib/pact_broker/api/contracts/dry_validation_macros.rb +79 -0
  19. data/lib/pact_broker/api/contracts/dry_validation_methods.rb +71 -0
  20. data/lib/pact_broker/api/contracts/environment_schema.rb +19 -33
  21. data/lib/pact_broker/api/contracts/pacticipant_create_schema.rb +4 -17
  22. data/lib/pact_broker/api/contracts/pacticipant_schema.rb +15 -24
  23. data/lib/pact_broker/api/contracts/pacts_for_verification_json_query_schema.rb +37 -146
  24. data/lib/pact_broker/api/contracts/pacts_for_verification_query_string_schema.rb +7 -20
  25. data/lib/pact_broker/api/contracts/publish_contracts_contract_contract.rb +62 -0
  26. data/lib/pact_broker/api/contracts/publish_contracts_schema.rb +25 -112
  27. data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +21 -26
  28. data/lib/pact_broker/api/contracts/utf_8_validation.rb +19 -0
  29. data/lib/pact_broker/api/contracts/validation_helpers.rb +71 -0
  30. data/lib/pact_broker/api/contracts/verification_contract.rb +10 -29
  31. data/lib/pact_broker/api/contracts/webhook_contract.rb +20 -170
  32. data/lib/pact_broker/api/contracts/webhook_pacticipant_contract.rb +33 -0
  33. data/lib/pact_broker/api/contracts/webhook_request_contract.rb +125 -0
  34. data/lib/pact_broker/api/contracts.rb +3 -0
  35. data/lib/pact_broker/api/decorators/custom_error_problem_json_decorator.rb +36 -0
  36. data/lib/pact_broker/api/decorators/dashboard_text_decorator.rb +2 -2
  37. data/lib/pact_broker/api/decorators/extended_pact_decorator.rb +1 -1
  38. data/lib/pact_broker/api/decorators/matrix_decorator.rb +4 -4
  39. data/lib/pact_broker/api/decorators/matrix_text_decorator.rb +1 -1
  40. data/lib/pact_broker/api/decorators/pact_decorator.rb +1 -1
  41. data/lib/pact_broker/api/decorators/pacticipant_collection_decorator.rb +5 -2
  42. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +2 -1
  43. data/lib/pact_broker/api/decorators/pacts_for_verification_query_decorator.rb +4 -1
  44. data/lib/pact_broker/api/decorators/pagination_links.rb +11 -0
  45. data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +34 -0
  46. data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +66 -0
  47. data/lib/pact_broker/api/decorators/verifiable_pact_decorator.rb +4 -4
  48. data/lib/pact_broker/api/decorators/webhook_decorator.rb +2 -3
  49. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +5 -12
  50. data/lib/pact_broker/api/resources/all_webhooks.rb +5 -11
  51. data/lib/pact_broker/api/resources/badge_methods.rb +1 -1
  52. data/lib/pact_broker/api/resources/base_resource.rb +31 -68
  53. data/lib/pact_broker/api/resources/branch_version.rb +3 -3
  54. data/lib/pact_broker/api/resources/can_i_deploy.rb +4 -19
  55. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb +1 -4
  56. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag.rb +0 -2
  57. data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge.rb +1 -2
  58. data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +2 -2
  59. data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +2 -2
  60. data/lib/pact_broker/api/resources/dashboard.rb +3 -3
  61. data/lib/pact_broker/api/resources/deployed_version.rb +1 -1
  62. data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +2 -2
  63. data/lib/pact_broker/api/resources/environment.rb +1 -1
  64. data/lib/pact_broker/api/resources/environments.rb +2 -2
  65. data/lib/pact_broker/api/resources/error_handling_methods.rb +57 -0
  66. data/lib/pact_broker/api/resources/error_response_generator.rb +70 -0
  67. data/lib/pact_broker/api/resources/integrations.rb +1 -1
  68. data/lib/pact_broker/api/resources/label.rb +1 -1
  69. data/lib/pact_broker/api/resources/latest_pact.rb +2 -2
  70. data/lib/pact_broker/api/resources/latest_pacts.rb +1 -1
  71. data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +2 -2
  72. data/lib/pact_broker/api/resources/matrix.rb +2 -2
  73. data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +1 -1
  74. data/lib/pact_broker/api/resources/pact.rb +7 -4
  75. data/lib/pact_broker/api/resources/pact_triggered_webhooks.rb +1 -1
  76. data/lib/pact_broker/api/resources/pact_version.rb +1 -1
  77. data/lib/pact_broker/api/resources/pact_versions.rb +1 -1
  78. data/lib/pact_broker/api/resources/pact_webhooks.rb +7 -14
  79. data/lib/pact_broker/api/resources/pact_webhooks_status.rb +6 -2
  80. data/lib/pact_broker/api/resources/pacticipant.rb +1 -1
  81. data/lib/pact_broker/api/resources/pacticipant_webhooks.rb +7 -5
  82. data/lib/pact_broker/api/resources/pacticipants.rb +6 -3
  83. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +1 -1
  84. data/lib/pact_broker/api/resources/pagination_methods.rb +8 -4
  85. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +1 -1
  86. data/lib/pact_broker/api/resources/provider_pacts.rb +1 -1
  87. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +4 -13
  88. data/lib/pact_broker/api/resources/publish_contracts.rb +8 -3
  89. data/lib/pact_broker/api/resources/released_version.rb +1 -1
  90. data/lib/pact_broker/api/resources/released_versions_for_version_and_environment.rb +2 -2
  91. data/lib/pact_broker/api/resources/tag.rb +1 -1
  92. data/lib/pact_broker/api/resources/tagged_pact_versions.rb +1 -1
  93. data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +2 -2
  94. data/lib/pact_broker/api/resources/verification.rb +2 -2
  95. data/lib/pact_broker/api/resources/verification_triggered_webhooks.rb +1 -1
  96. data/lib/pact_broker/api/resources/verifications.rb +4 -6
  97. data/lib/pact_broker/api/resources/version.rb +1 -1
  98. data/lib/pact_broker/api/resources/versions.rb +6 -13
  99. data/lib/pact_broker/api/resources/webhook.rb +7 -6
  100. data/lib/pact_broker/api/resources/webhook_execution.rb +6 -4
  101. data/lib/pact_broker/api.rb +3 -13
  102. data/lib/pact_broker/app.rb +0 -2
  103. data/lib/pact_broker/application_context.rb +4 -4
  104. data/lib/pact_broker/certificates/certificate.rb +1 -0
  105. data/lib/pact_broker/config/setting.rb +1 -0
  106. data/lib/pact_broker/contracts/service.rb +1 -0
  107. data/lib/pact_broker/date_helper.rb +1 -1
  108. data/lib/pact_broker/db/clean_incremental.rb +67 -59
  109. data/lib/pact_broker/db/delete_overwritten_data.rb +6 -2
  110. data/lib/pact_broker/deployments/currently_deployed_version_id.rb +2 -0
  111. data/lib/pact_broker/deployments/deployed_version.rb +2 -0
  112. data/lib/pact_broker/deployments/deployed_version_service.rb +5 -1
  113. data/lib/pact_broker/deployments/environment.rb +2 -0
  114. data/lib/pact_broker/deployments/environment_service.rb +4 -3
  115. data/lib/pact_broker/deployments/released_version.rb +2 -0
  116. data/lib/pact_broker/deployments/released_version_service.rb +4 -0
  117. data/lib/pact_broker/diagnostic/resources/base_resource.rb +1 -1
  118. data/lib/pact_broker/doc/views/index/publish-contracts.markdown +5 -5
  119. data/lib/pact_broker/domain/label.rb +1 -0
  120. data/lib/pact_broker/domain/tag.rb +2 -0
  121. data/lib/pact_broker/domain/verification.rb +1 -1
  122. data/lib/pact_broker/domain/version.rb +4 -1
  123. data/lib/pact_broker/domain/webhook.rb +1 -1
  124. data/lib/pact_broker/index/service.rb +2 -2
  125. data/lib/pact_broker/integrations/integration.rb +1 -0
  126. data/lib/pact_broker/locale/en.yml +36 -14
  127. data/lib/pact_broker/matrix/query_ids.rb +4 -4
  128. data/lib/pact_broker/matrix/resolved_selector.rb +6 -1
  129. data/lib/pact_broker/matrix/service.rb +1 -0
  130. data/lib/pact_broker/messages.rb +5 -1
  131. data/lib/pact_broker/pacticipants/repository.rb +16 -14
  132. data/lib/pact_broker/pacticipants/service.rb +11 -8
  133. data/lib/pact_broker/pacts/pact_params.rb +6 -17
  134. data/lib/pact_broker/pacts/pact_version.rb +1 -0
  135. data/lib/pact_broker/pacts/selected_pact.rb +4 -0
  136. data/lib/pact_broker/policies.rb +4 -4
  137. data/lib/pact_broker/repositories/helpers.rb +11 -0
  138. data/lib/pact_broker/repositories/page.rb +24 -0
  139. data/lib/pact_broker/string_refinements.rb +4 -0
  140. data/lib/pact_broker/tasks/clean_task.rb +7 -3
  141. data/lib/pact_broker/test/http_test_data_builder.rb +46 -2
  142. data/lib/pact_broker/ui/app.rb +2 -2
  143. data/lib/pact_broker/ui/views/matrix/show.haml +1 -1
  144. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +1 -1
  145. data/lib/pact_broker/verifications/pact_version_provider_tag_successful_verification.rb +1 -0
  146. data/lib/pact_broker/verifications/repository.rb +14 -11
  147. data/lib/pact_broker/verifications/service.rb +0 -6
  148. data/lib/pact_broker/version.rb +1 -1
  149. data/lib/pact_broker/versions/branch.rb +1 -0
  150. data/lib/pact_broker/versions/branch_head.rb +2 -1
  151. data/lib/pact_broker/versions/branch_version.rb +11 -0
  152. data/lib/pact_broker/versions/repository.rb +12 -0
  153. data/lib/pact_broker/versions/service.rb +4 -0
  154. data/lib/pact_broker/webhooks/execution.rb +1 -1
  155. data/lib/pact_broker/webhooks/repository.rb +1 -1
  156. data/lib/pact_broker/webhooks/service.rb +3 -25
  157. data/lib/pact_broker/webhooks/triggered_webhook.rb +1 -0
  158. data/lib/pact_broker/webhooks/webhook_event.rb +1 -0
  159. data/lib/pact_broker/webmachine.rb +22 -0
  160. data/lib/rack/pact_broker/invalid_uri_protection.rb +1 -1
  161. data/lib/rack/pact_broker/request_target.rb +1 -1
  162. data/lib/rack/pact_broker/use_when.rb +6 -5
  163. data/lib/sequel/plugins/age.rb +13 -0
  164. data/lib/webmachine/application_monkey_patch.rb +15 -0
  165. data/lib/webmachine/describe_routes.rb +35 -8
  166. data/lib/webmachine/render_error_monkey_patch.rb +70 -0
  167. data/pact_broker.gemspec +7 -18
  168. metadata +72 -76
  169. data/lib/pact/doc/README.md +0 -5
  170. data/lib/pact_broker/api/contracts/dry_validation_predicates.rb +0 -36
  171. data/lib/pact_broker/api/contracts/dry_validation_workarounds.rb +0 -39
  172. data/lib/pact_broker/api/contracts/pacticipant_name_contract.rb +0 -24
  173. data/lib/pact_broker/api/contracts/pacticipant_name_validation.rb +0 -30
  174. data/lib/pact_broker/api/contracts/request_validations.rb +0 -33
  175. data/lib/pact_broker/api/resources/error_response_body_generator.rb +0 -41
  176. data/lib/pact_broker/api/resources/webhook_resource_methods.rb +0 -17
  177. data/lib/pact_broker/matrix/can_i_deploy_query_schema.rb +0 -46
  178. data/lib/rack/pact_broker/convert_404_to_hal.rb +0 -20
@@ -1,6 +1,7 @@
1
1
  module PactBroker
2
2
  module Config
3
3
  class Setting < Sequel::Model(:config)
4
+ set_primary_key :id
4
5
 
5
6
  def set_value_from(object)
6
7
  self.type = Setting.get_db_type(object)
@@ -7,6 +7,7 @@ require "pact_broker/contracts/notice"
7
7
  require "pact_broker/events/subscriber"
8
8
  require "pact_broker/api/pact_broker_urls"
9
9
  require "pact_broker/pacts/create_formatted_diff"
10
+ require "pact_broker/pacts/pact_params"
10
11
 
11
12
  module PactBroker
12
13
  module Contracts
@@ -14,7 +14,7 @@ module PactBroker
14
14
  end
15
15
 
16
16
  def t path, options
17
- I18n.t path, @options.merge(options)
17
+ I18n.t(path, **@options.merge(options))
18
18
  end
19
19
  end
20
20
 
@@ -25,6 +25,36 @@ module PactBroker
25
25
  @options = options
26
26
  end
27
27
 
28
+ def call
29
+ require "pact_broker/db/models"
30
+
31
+ if dry_run?
32
+ dry_run_results
33
+ else
34
+ execute_clean
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :db, :options
41
+
42
+ def execute_clean
43
+ db.transaction do
44
+ before_counts = current_counts
45
+ PactBroker::Domain::Version.where(id: versions_to_delete.from_self.select_map(:id)).delete
46
+ delete_orphan_pact_versions
47
+ after_counts = current_counts
48
+
49
+ TABLES.each_with_object({}) do | table_name, comparison_counts |
50
+ comparison_counts[table_name.to_s] = {
51
+ "deleted" => before_counts[table_name] - after_counts[table_name],
52
+ "kept" => after_counts[table_name]
53
+ }
54
+ end
55
+ end
56
+ end
57
+
28
58
  def logger
29
59
  options[:logger] || PactBroker.logger
30
60
  end
@@ -42,47 +72,20 @@ module PactBroker
42
72
  options[:limit] || 1000
43
73
  end
44
74
 
45
- def resolve_ids(query, column_name = :id)
46
- query.collect { |h| h[column_name] }
47
- end
48
-
49
- def version_ids_to_delete
50
- db[:versions].where(id: version_ids_to_keep).invert.limit(limit).order(Sequel.asc(:id))
75
+ def versions_to_delete(columns = [:id])
76
+ fully_qualified_columns = columns.collect { |col| Sequel[:versions][col] }
77
+ PactBroker::Domain::Version
78
+ .select(*fully_qualified_columns)
79
+ .left_outer_join(version_ids_to_keep, { Sequel[:versions][:id] => Sequel[:keep_versions][:id] }, table_alias: :keep_versions)
80
+ .where(Sequel[:keep_versions][:id] => nil)
81
+ .order(Sequel.asc( Sequel[:versions][:id]))
82
+ .limit(limit)
51
83
  end
52
84
 
53
85
  def version_ids_to_keep
54
- @version_ids_to_keep ||= selected_versions_to_keep.reduce(&:union)
86
+ @version_ids_to_keep ||= keep.collect { |selector| PactBroker::Domain::Version.for_selector(selector).select(:id) }.reduce(&:union)
55
87
  end
56
88
 
57
- def selected_versions_to_keep
58
- keep.collect do | selector |
59
- PactBroker::Domain::Version.select(:id).for_selector(selector)
60
- end
61
- end
62
-
63
- def call
64
- require "pact_broker/db/models"
65
-
66
- if dry_run?
67
- dry_run_results
68
- else
69
- db.transaction do
70
- before_counts = current_counts
71
- PactBroker::Domain::Version.where(id: resolve_ids(version_ids_to_delete)).delete
72
- delete_orphan_pact_versions
73
- after_counts = current_counts
74
-
75
- TABLES.each_with_object({}) do | table_name, comparison_counts |
76
- comparison_counts[table_name.to_s] = { "deleted" => before_counts[table_name] - after_counts[table_name], "kept" => after_counts[table_name] }
77
- end
78
- end
79
- end
80
- end
81
-
82
- private
83
-
84
- attr_reader :db, :options
85
-
86
89
  def current_counts
87
90
  TABLES.each_with_object({}) do | table_name, counts |
88
91
  counts[table_name] = db[table_name].count
@@ -122,8 +125,8 @@ module PactBroker
122
125
  end
123
126
 
124
127
  def dry_run_results
125
- to_delete = dry_run_to_delete
126
- to_keep = dry_run_to_keep
128
+ to_delete = dry_run_to_delete_by_pacticipant
129
+ to_keep = dry_run_to_keep_by_pacticipant
127
130
 
128
131
  kept_per_selector = keep.collect do | selector |
129
132
  {
@@ -141,7 +144,7 @@ module PactBroker
141
144
 
142
145
  total_versions_count = PactBroker::Domain::Version.count
143
146
  versions_to_keep_count = version_ids_to_keep.count
144
- versions_to_delete_count = version_ids_to_delete.count
147
+ versions_to_delete_count = versions_to_delete.count
145
148
 
146
149
  {
147
150
  "counts" => {
@@ -155,9 +158,15 @@ module PactBroker
155
158
  }
156
159
  end
157
160
 
158
- def dry_run_latest_versions_to_keep
159
- latest_undeleted_versions_by_order = PactBroker::Domain::Version.where(id: version_ids_to_delete.select(:id))
160
- .invert
161
+ def expected_remaining_versions
162
+ PactBroker::Domain::Version
163
+ .left_outer_join(versions_to_delete, { Sequel[:versions][:id] => Sequel[:delete_versions][:id] }, table_alias: :delete_versions )
164
+ .where(Sequel[:delete_versions][:id] => nil)
165
+ end
166
+
167
+ # Returns the latest version that will be kept for each pacticipant
168
+ def dry_run_latest_versions_to_keep_by_pacticipant
169
+ latest_undeleted_versions_by_order = expected_remaining_versions
161
170
  .select_group(:pacticipant_id)
162
171
  .select_append{ max(order).as(latest_order) }
163
172
 
@@ -171,9 +180,9 @@ module PactBroker
171
180
  .join(latest_undeleted_versions_by_order, lv_versions_join, { table_alias: :lv })
172
181
  end
173
182
 
174
- def dry_run_earliest_versions_to_keep
175
- earliest_undeleted_versions_by_order = PactBroker::Domain::Version.where(id: version_ids_to_delete.select(:id))
176
- .invert
183
+ # Returns the earliest version that will be kept for each pacticipant
184
+ def dry_run_earliest_versions_to_keep_by_pacticipant
185
+ earliest_undeleted_versions_by_order = expected_remaining_versions
177
186
  .select_group(:pacticipant_id)
178
187
  .select_append{ min(order).as(first_order) }
179
188
 
@@ -187,13 +196,16 @@ module PactBroker
187
196
  .join(earliest_undeleted_versions_by_order, ev_versions_join, { table_alias: :lv })
188
197
  end
189
198
 
190
- def dry_run_to_delete
191
- PactBroker::Domain::Version
192
- .where(id: version_ids_to_delete.select(:id))
199
+ # Returns Hash of pacticipant name => Hash, where the Hash value contains the count, fromVersion and toVersion
200
+ # that will be deleted.
201
+ # @return Hash
202
+ def dry_run_to_delete_by_pacticipant
203
+ versions_to_delete
204
+ .select(Sequel[:versions].*)
193
205
  .all
194
206
  .group_by{ | v | v.pacticipant_id }
195
- .each_with_object({}) do | (_pacticipant_id, versions), thing |
196
- thing[versions.first.pacticipant.name] = {
207
+ .each_with_object({}) do | (_pacticipant_id, versions), hash |
208
+ hash[versions.first.pacticipant.name] = {
197
209
  "count" => versions.count,
198
210
  "fromVersion" => version_info(versions.first),
199
211
  "toVersion" => version_info(versions.last)
@@ -202,14 +214,14 @@ module PactBroker
202
214
  end
203
215
 
204
216
  # rubocop: disable Metrics/CyclomaticComplexity
205
- def dry_run_to_keep
206
- latest_to_keep = dry_run_latest_versions_to_keep.eager(:tags).each_with_object({}) do | version, r |
217
+ def dry_run_to_keep_by_pacticipant
218
+ latest_to_keep = dry_run_latest_versions_to_keep_by_pacticipant.eager(:tags).each_with_object({}) do | version, r |
207
219
  r[version.pacticipant_id] = {
208
220
  "firstVersion" => version_info(version)
209
221
  }
210
222
  end
211
223
 
212
- earliest_to_keep = dry_run_earliest_versions_to_keep.eager(:tags).each_with_object({}) do | version, r |
224
+ earliest_to_keep = dry_run_earliest_versions_to_keep_by_pacticipant.eager(:tags).each_with_object({}) do | version, r |
213
225
  r[version.pacticipant_id] = {
214
226
  "latestVersion" => version_info(version)
215
227
  }
@@ -226,14 +238,10 @@ module PactBroker
226
238
  # rubocop: enable Metrics/CyclomaticComplexity
227
239
 
228
240
  def counts_to_keep
229
- db[:versions].where(id: version_ids_to_delete.select(:id))
230
- .invert
241
+ expected_remaining_versions
231
242
  .select_group(:pacticipant_id)
232
243
  .select_append{ count(1).as(count) }
233
- .all
234
- .each_with_object({}) do | row, counts |
235
- counts[row[:pacticipant_id]] = row[:count]
236
- end
244
+ .as_hash(:pacticipant_id, :count)
237
245
  end
238
246
 
239
247
  def pacticipants
@@ -36,9 +36,13 @@ module PactBroker
36
36
  to_keep = deleted_counts.keys.each_with_object({}) do | table_name, new_counts |
37
37
  new_counts[table_name] = kept_counts[table_name] - deleted_counts[table_name]
38
38
  end
39
- { toDelete: deleted_counts, toKeep: to_keep }
39
+ deleted_counts.each_with_object({}) do | (key, value), new_hash |
40
+ new_hash[key] = { toDelete: value, toKeep: to_keep[key] }
41
+ end
40
42
  else
41
- { deleted: deleted_counts, kept: kept_counts }
43
+ deleted_counts.each_with_object({}) do | (key, value), new_hash |
44
+ new_hash[key] = { deleted: value, kept: kept_counts[key] }
45
+ end
42
46
  end
43
47
  end
44
48
 
@@ -4,6 +4,8 @@ require "pact_broker/repositories/helpers"
4
4
  module PactBroker
5
5
  module Deployments
6
6
  class CurrentlyDeployedVersionId < Sequel::Model
7
+ set_primary_key :id
8
+
7
9
  plugin :upsert, identifying_columns: [:pacticipant_id, :environment_id, :target_for_index]
8
10
 
9
11
  dataset_module do
@@ -4,6 +4,8 @@ require "pact_broker/deployments/currently_deployed_version_id"
4
4
  module PactBroker
5
5
  module Deployments
6
6
  class DeployedVersion < Sequel::Model
7
+ set_primary_key :id
8
+
7
9
  many_to_one :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id
8
10
  many_to_one :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id
9
11
  many_to_one :environment, :class => "PactBroker::Deployments::Environment", :key => :environment_id, :primary_key => :id
@@ -1,5 +1,6 @@
1
1
  require "pact_broker/deployments/deployed_version"
2
2
  require "pact_broker/repositories/scopes"
3
+ require "pact_broker/events/publisher"
3
4
 
4
5
  module PactBroker
5
6
  module Deployments
@@ -7,6 +8,7 @@ module PactBroker
7
8
  include PactBroker::Logging
8
9
  extend PactBroker::Repositories::Scopes
9
10
  extend PactBroker::Services
11
+ extend PactBroker::Events::Publisher
10
12
 
11
13
  def self.next_uuid
12
14
  SecureRandom.uuid
@@ -22,7 +24,7 @@ module PactBroker
22
24
  deployed_version
23
25
  else
24
26
  record_previous_version_undeployed(version.pacticipant, environment, target)
25
- DeployedVersion.create(
27
+ deployed_version = DeployedVersion.create(
26
28
  uuid: uuid,
27
29
  version: version,
28
30
  pacticipant_id: version.pacticipant_id,
@@ -30,6 +32,8 @@ module PactBroker
30
32
  target: target,
31
33
  auto_created: auto_created
32
34
  )
35
+ broadcast(:deployed_version_created, { deployed_version: deployed_version })
36
+ deployed_version
33
37
  end
34
38
  end
35
39
 
@@ -5,6 +5,8 @@ require "sequel/plugins/serialization"
5
5
  module PactBroker
6
6
  module Deployments
7
7
  class Environment < Sequel::Model
8
+ set_primary_key :id
9
+
8
10
  OPEN_STRUCT_TO_JSON = lambda { | open_struct | Sequel.object_to_json(open_struct.collect(&:to_h)) }
9
11
  JSON_TO_OPEN_STRUCT = lambda { | json | Sequel.parse_json(json).collect{ | hash| OpenStruct.new(hash) } }
10
12
  plugin :upsert, identifying_columns: [:uuid]
@@ -9,7 +9,6 @@ module PactBroker
9
9
  module EnvironmentService
10
10
  using PactBroker::StringRefinements
11
11
  extend PactBroker::Repositories::Scopes
12
-
13
12
  extend self
14
13
 
15
14
  def self.included(base)
@@ -36,8 +35,6 @@ module PactBroker
36
35
  environment.upsert
37
36
  end
38
37
 
39
- alias_method :update, :replace # For PF
40
-
41
38
  def find_all
42
39
  scope_for(PactBroker::Deployments::Environment).order(Sequel.function(:lower, :display_name)).all
43
40
  end
@@ -57,6 +54,10 @@ module PactBroker
57
54
  def find_for_pacticipant(_pacticipant)
58
55
  find_all
59
56
  end
57
+
58
+ def scope_for(scope)
59
+ PactBroker.policy_scope!(scope)
60
+ end
60
61
  end
61
62
  end
62
63
  end
@@ -4,6 +4,8 @@ require "pact_broker/repositories/helpers"
4
4
  module PactBroker
5
5
  module Deployments
6
6
  class ReleasedVersion < Sequel::Model
7
+ set_primary_key :id
8
+
7
9
  many_to_one :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id
8
10
  many_to_one :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id
9
11
  many_to_one :environment, :class => "PactBroker::Deployments::Environment", :key => :environment_id, :primary_key => :id
@@ -1,10 +1,12 @@
1
1
  require "pact_broker/deployments/released_version"
2
2
  require "pact_broker/repositories/scopes"
3
+ require "pact_broker/events/publisher"
3
4
 
4
5
  module PactBroker
5
6
  module Deployments
6
7
  class ReleasedVersionService
7
8
  extend PactBroker::Repositories::Scopes
9
+ extend PactBroker::Events::Publisher
8
10
 
9
11
  def self.next_uuid
10
12
  SecureRandom.uuid
@@ -25,6 +27,8 @@ module PactBroker
25
27
  # error when marking an existing row as supported again IRL.
26
28
  ReleasedVersion.where(id: released_version.id).set_currently_supported
27
29
  released_version.refresh
30
+ broadcast(:released_version_created, { released_version: released_version })
31
+ released_version
28
32
  end
29
33
 
30
34
  def self.find_currently_supported_versions_for_environment(environment, pacticipant_name: nil, pacticipant_version_number: nil)
@@ -13,7 +13,7 @@ module PactBroker
13
13
  end
14
14
 
15
15
  def forbidden?
16
- return false if PactBroker.configuration.authorize.nil?
16
+ return false unless PactBroker.configuration.authorization_configured?
17
17
  !PactBroker.configuration.authorize.call(self, {})
18
18
  end
19
19
 
@@ -33,7 +33,7 @@ This endpoint is designed to be used by a command line tool, and hence, the resp
33
33
  ### Success
34
34
 
35
35
  * `notices`
36
- * `level`: one of `debug`, `info`, `warning`,`prompt`,`success`, `error`, `danger`
36
+ * `type`: one of `debug`, `info`, `warning`,`prompt`,`success`, `error`, `danger`
37
37
  * `text`: the text of the notice. This is designed to be displayed in the output of a CLI.
38
38
 
39
39
  The `_links` section will contain links to all the resources created by the publication. The relations are:
@@ -105,19 +105,19 @@ If there is a conflict with an existing published pact and `allow_dangerous_cont
105
105
  {
106
106
  "notices": [
107
107
  {
108
- "level": "debug",
108
+ "type": "debug",
109
109
  "text": "Created Foo version dc5eb529230038a4673b8c971395bd2922d8b240 with branch main and tags main"
110
110
  },
111
111
  {
112
- "level": "info",
112
+ "type": "info",
113
113
  "text": "Pact published for Foo version dc5eb529230038a4673b8c971395bd2922d8b240 and provider Bar."
114
114
  },
115
115
  {
116
- "level": "debug",
116
+ "type": "debug",
117
117
  "text": " Events detected: contract_published, contract_content_changed (first time any pact published for this consumer with consumer version tagged main)"
118
118
  },
119
119
  {
120
- "level": "debug",
120
+ "type": "debug",
121
121
  "text": " Webhook \"foo webhook\" triggered for event contract_content_changed.\n See logs at http://example.org/triggered-webhooks/1234/logs\""
122
122
  }
123
123
  ],
@@ -3,6 +3,7 @@ require "pact_broker/db"
3
3
  module PactBroker
4
4
  module Domain
5
5
  class Label < Sequel::Model
6
+ set_primary_key([:name, :pacticipant_id])
6
7
  unrestrict_primary_key
7
8
 
8
9
  associate(:many_to_one, :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id)
@@ -5,6 +5,8 @@ require "pact_broker/tags/eager_loaders"
5
5
  module PactBroker
6
6
  module Domain
7
7
  class Tag < Sequel::Model
8
+ set_primary_key([:name, :version_id])
9
+
8
10
  plugin :timestamps, update_on_create: true
9
11
  plugin :insert_ignore, identifying_columns: [:name, :version_id]
10
12
 
@@ -228,7 +228,7 @@ module PactBroker
228
228
  end
229
229
 
230
230
  # So consumer_version_tag_name can be accessed by method name
231
- def method_missing(m, *args, &block)
231
+ def method_missing(m, *args, **kwargs, &block)
232
232
  if values.key?(m) && args.size == 0
233
233
  values[m]
234
234
  else
@@ -24,10 +24,12 @@ module PactBroker
24
24
  VERSION_COLUMNS = [:id, :number, :repository_ref, :pacticipant_id, :order, :created_at, :updated_at, :build_url]
25
25
 
26
26
  class Version < Sequel::Model(Sequel::Model.db[:versions].select(*VERSION_COLUMNS.collect{ | column | Sequel.qualify(:versions, column) }))
27
+ set_primary_key :id
28
+
27
29
  plugin :timestamps, update_on_create: true
30
+ plugin :age
28
31
  plugin :upsert, { identifying_columns: [:pacticipant_id, :number], ignore_columns_on_update: [:id, :created_at, :order] }
29
32
 
30
- set_primary_key :id
31
33
  one_to_many :pact_publications, order: :revision_number, class: "PactBroker::Pacts::PactPublication", key: :consumer_version_id
32
34
  associate(:many_to_one, :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id)
33
35
  one_to_many :tags, :reciprocal => :version, order: :created_at
@@ -41,6 +43,7 @@ module PactBroker
41
43
  end
42
44
 
43
45
  one_to_many :deployed_versions, class: "PactBroker::Deployments::DeployedVersion", key: :version_id, primary_key: :id, order: [:created_at, :id]
46
+ one_to_many :released_versions, class: "PactBroker::Deployments::ReleasedVersion", key: :version_id, primary_key: :id, order: [:created_at, :id]
44
47
 
45
48
  many_to_one :latest_version_for_pacticipant, read_only: true, key: :id,
46
49
  class: Version,
@@ -50,7 +50,7 @@ module PactBroker
50
50
  def execute pact, verification, event_context, options
51
51
  logger.info "Executing #{self} event_context=#{event_context}"
52
52
  template_params = template_parameters(pact, verification, event_context, options)
53
- webhook_request = request.build(template_params, options.slice(:user_agent, :disable_ssl_verification, :cert_store))
53
+ webhook_request = request.build(template_params, **options.slice(:user_agent, :disable_ssl_verification, :cert_store))
54
54
  http_response, error = execute_request(webhook_request)
55
55
  success = success?(http_response, options)
56
56
  http_request = webhook_request.http_request
@@ -18,7 +18,7 @@ module PactBroker
18
18
  DEFAULT_PAGE_NUMBER = 1
19
19
 
20
20
  # This method provides data for both the OSS server side rendered index (with and without tags)
21
- # and the Pactflow UI. It really needs to be broken into to separate methods, as it's getting too messy
21
+ # and the PactFlow UI. It really needs to be broken into to separate methods, as it's getting too messy
22
22
  # supporting both
23
23
 
24
24
  def self.pact_publication_scope
@@ -109,7 +109,7 @@ module PactBroker
109
109
  # rubocop: disable Metrics/CyclomaticComplexity
110
110
  def self.latest_verification_for_pseudo_branch(pact_publication, is_overall_latest, latest_verifications_for_cv_tags, tags_option, options)
111
111
  if options[:view] == "branch" || (options[:view] == "all" && pact_publication.consumer_version.branch_heads.any?)
112
- pact_publication.latest_verification || pact_publication.latest_verification_for_consumer_branches
112
+ pact_publication.latest_verification || pact_publication.latest_verification_for_consumer_branches(forbid_lazy_load: false)
113
113
  elsif tags_option == true
114
114
  latest_verifications_for_cv_tags
115
115
  .select{ | v | v.consumer_id == pact_publication.consumer_id && v.provider_id == pact_publication.provider_id && pact_publication.head_pact_tags.collect(&:name).include?(v.consumer_version_tag_name) }
@@ -8,6 +8,7 @@ require "pact_broker/verifications/latest_verification_for_consumer_and_provider
8
8
  module PactBroker
9
9
  module Integrations
10
10
  class Integration < Sequel::Model(Sequel::Model.db[:integrations].select(:id, :consumer_id, :provider_id))
11
+ set_primary_key :id
11
12
  plugin :insert_ignore, identifying_columns: [:consumer_id, :provider_id]
12
13
  associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
13
14
  associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
@@ -2,17 +2,10 @@ en:
2
2
  errors:
3
3
  not_blank?: "can't be blank"
4
4
  filled?: "can't be blank"
5
- valid_method?: "is not a recognised HTTP method"
6
5
  valid_url?: "is not a valid URL eg. http://example.org"
7
6
  base64?: "is not valid Base64"
8
- valid_version_number?: "Version number '%{value}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
9
- name_in_path_matches_name_in_pact?: "does not match %{left} name in path ('%{right}')."
10
- valid_consumer_version_number?: "Consumer version number '%{value}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
11
- non_templated_host?: "cannot have a template parameter in the host"
12
- pacticipant_exists?: "does not match an existing pacticipant"
13
7
  single_line?: "cannot contain multiple lines"
14
8
  no_spaces?: "cannot contain spaces"
15
- environment_with_name_exists?: "with name '%{value}' does not exist"
16
9
  none?: "cannot be provided"
17
10
 
18
11
  pact_broker:
@@ -68,33 +61,61 @@ en:
68
61
  version_branch: Configure the version branch to be the value of your repository branch.
69
62
  errors:
70
63
  validation:
71
- blank: "cannot be blank."
64
+ no_spaces: "cannot contain spaces"
65
+ single_line: "cannot contain multiple lines"
66
+ environment_not_found: "with name '%{value}' does not exist"
67
+ blank: "cannot be blank"
68
+ cannot_be_provided_at_same_time: "%{name_1} and %{name_2} cannot be provided at the same time"
69
+ blank_with_name: "%{name} cannot be blank"
72
70
  attribute_missing: "Missing required attribute '%{attribute}'"
73
71
  invalid_http_method: "Invalid HTTP method '%{method}'"
74
72
  invalid_url: "Invalid URL '%{url}'. Expected format: http://example.org"
75
73
  pact_missing_pacticipant_name: "was not found at expected path $.%{pacticipant}.name in the submitted pact file."
76
- pact_content_modification_not_allowed: "Cannot change the content of the pact for %{consumer_name} version %{consumer_version_number} and provider %{provider_name}, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. For more information see https://docs.pact.io/go/versioning"
74
+ pact_content_modification_not_allowed: "Cannot change the content of the pact for %{consumer_name} version %{consumer_version_number} and provider %{provider_name}, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. Some Pact libraries generate random data when a concrete value for a type matcher is not specified, and this can cause the contract to mutate - ensure you have given example values for all type matchers. For more information see https://docs.pact.io/go/versioning"
77
75
  consumer_version_number_missing: "Please specify the consumer version number by setting the X-Pact-Consumer-Version header."
78
76
  consumer_version_number_header_invalid: "X-Pact-Consumer-Version '%{consumer_version_number}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
79
- consumer_version_number_invalid: "Consumer version number '%{consumer_version_number}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
80
77
  pacticipant_name_mismatch: "in pact ('%{name_in_pact}') does not match %{pacticipant} name in path ('%{name}')."
81
- consumer_name_in_content_mismatch_pacticipant_name: "consumer name in contract content ('%{consumer_name_in_content}') must match pacticipantName ('%{pacticipant_name}')"
82
78
  consumer_name_in_contract_mismatch_pacticipant_name: "consumerName ('%{consumer_name_in_contract}') must match pacticipantName ('%{pacticipant_name}')"
83
- provider_name_in_content_mismatch: "provider name in contract content ('%{provider_name_in_content}') must match providerName ('%{provider_name}') in contracts"
79
+ consumer_name_in_content_mismatch: "consumer name in contract content ('%{consumer_name_in_content}') must match consumerName in contract params ('%{consumer_name}')"
80
+ provider_name_in_content_mismatch: "provider name in contract content ('%{provider_name_in_content}') must match providerName in contract params ('%{provider_name}')"
84
81
  connection_encoding_not_utf8: "The Sequel connection encoding (%{encoding}) is strongly recommended to be \"utf8\". If you need to set it to %{encoding} for some particular reason, then disable this check by setting config.validate_database_connection_config = false"
85
82
  invalid_webhook_uuid: The UUID can only contain the characters A-Z, a-z, 0-9, _ and -, and must be 16 or more characters.
86
83
  pacticipant_not_found: No pacticipant with name '%{name}' found
87
- environment_name_must_be_unique: Another environment with name '%{name}' already exists.
84
+ environment_name_must_be_unique: "name '%{name}' is already used by an existing environment."
88
85
  must_specify_environment_or_tag: Must specify either an environment or a 'to' tag.
89
86
  cannot_specify_tag_and_environment: Cannot specify both a 'to' tag and an environment.
90
87
  cannot_specify_latest_and_environment: Cannot specify both latest=true and an environment.
91
88
  cannot_specify_more_than_one_destination_identifier: Cannot specify more than one of tag, environment and mainBranch.
92
89
  environment_with_name_not_found: "Environment with name '%{name}' does not exist"
93
90
  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."
94
- invalid_content_for_content_type: "The content could not be parsed as %{content_type}"
91
+ invalid_content_for_content_type: "content could not be parsed as %{content_type}"
95
92
  cannot_set_currently_deployed_true: The currentlyDeployed property cannot be set back to true. Please record a new deployment.
96
93
  cannot_set_currently_supported_true: The currentlySupported property cannot be set back to true. Please record a new deployment.
97
94
  invalid_limit: The limit must be 1 or greater.
95
+ pacts_for_verification_selector_fallback_tag: "fallbackTag can only be set if latest is true"
96
+ pacts_for_verification_selector_matching_branch_requires_provider_version_branch: the providerVersionBranch must be specified when the selector matchingBranch=true is used
97
+ pacts_for_verification_selector_tag_and_branch_disallowed: "cannot specify both a tag and a branch"
98
+ pacts_for_verification_selector_fallback_tag_requires_tag: "a tag must be specified when a fallbackTag is specified"
99
+ pacts_for_verification_selector_fallback_branch_requires_branch: "a branch must be specified when a fallbackBranch is specified"
100
+ pacts_for_verification_selector_branch_and_latest_false_disallowed: cannot specify a branch with latest=false
101
+ pacts_for_verification_selector_main_branch_and_latest_false_disallowed: cannot specify mainBranch=true with latest=false
102
+ pacts_for_verification_selector_fallback_branch_and_latest_false_disallowed: fallbackBranch can only be set if latest is true
103
+ pacts_for_verification_selector_required_params_missing: must specify a value for environment or tag or branch, or specify mainBranch=true, matchingBranch=true, latest=true, deployed=true, released=true or deployedOrReleased=true
104
+ pacts_for_verification_selector_main_branch_with_other_param_disallowed: cannot specify mainBranch=true with any other criteria apart from consumer
105
+ pacts_for_verification_selector_matching_branch_with_other_param_disallowed: cannot specify matchingBranch=true with any other criteria apart from consumer
106
+ pacts_for_verification_selector_deployed_and_released_disallowed: cannot specify both deployed=true and released=true
107
+ pacts_for_verification_selector_deployed_and_deployed_or_released_disallowed: cannot specify both deployed=true and deployedOrReleased=true
108
+ pacts_for_verification_selector_released_and_deployed_or_released_disallowed: cannot specify both released=true and deployedOrReleased=true
109
+ pacts_for_verification_selector_provider_version_branch_empty: when pending or WIP pacts are enabled and there are no tags provided, the provider version branch must not be an empty string, as it is used in the calculations for WIP/pending. A value must be provided (recommended), or it must not be set at all.
110
+ pact_name_in_path_mismatch_name_in_pact: "name in pact '%{name_in_pact}' does not match name in URL path '%{name_in_path}'."
111
+ base64: "is not valid Base64"
112
+ non_utf_8_char_in_contract: "contract content has a non UTF-8 character at char %{char_number} and cannot be parsed as JSON. Fragment preceding invalid character is: '%{fragment}'"
113
+ invalid_consumer_version_number: "Consumer version number '%{value}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
114
+ invalid_version_number: "Version number '%{value}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
115
+ invalid_url: "is not a valid URL eg. http://example.org"
116
+ pacticipant_with_name_not_found: "does not match an existing pacticipant"
117
+ invalid_http_method: "is not a recognised HTTP method"
118
+ webhook_templated_host_not_allowed: "cannot have a template parameter in the host"
98
119
  duplicate_pacticipant: |
99
120
  This is the first time a pact has been published for "%{new_name}".
100
121
  The name "%{new_name}" is very similar to the following existing pacticipants:
@@ -109,6 +130,7 @@ en:
109
130
  new_line_in_url_path: URL path cannot contain a new line character.
110
131
  tab_in_url_path: URL path cannot contain a tab character.
111
132
  non_utf_8_char_in_request_body: "Request body has a non UTF-8 character at char %{char_number} and cannot be parsed as JSON. Fragment preceding invalid character is: '%{fragment}'"
133
+ non_utf_8_char_in_contract: "Contract has a non UTF-8 character at char %{char_number} and cannot be parsed as JSON. Fragment preceding invalid character is: '%{fragment}'"
112
134
 
113
135
  "400":
114
136
  title: 400 Malformed Request
@@ -17,10 +17,10 @@ module PactBroker
17
17
 
18
18
  def self.from_selectors(selectors)
19
19
  most_specific_criteria = selectors.collect(&:most_specific_criterion)
20
- all_pacticipant_ids = selectors.collect(&:pacticipant_id)
21
- specified_pacticipant_ids = selectors.select(&:specified?).collect(&:pacticipant_id)
22
- pacticipant_version_ids = collect_ids(most_specific_criteria, :pacticipant_version_id)
23
- pacticipant_ids = collect_ids(most_specific_criteria, :pacticipant_id)
20
+ all_pacticipant_ids = selectors.collect(&:pacticipant_id).uniq
21
+ specified_pacticipant_ids = selectors.select(&:specified?).collect(&:pacticipant_id).uniq
22
+ pacticipant_version_ids = collect_ids(most_specific_criteria, :pacticipant_version_id).uniq
23
+ pacticipant_ids = collect_ids(most_specific_criteria, :pacticipant_id).uniq
24
24
  QueryIds.new(all_pacticipant_ids, specified_pacticipant_ids, pacticipant_ids, pacticipant_version_ids)
25
25
  end
26
26