pact_broker 2.76.2 → 2.79.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +12 -0
  3. data/.github/workflows/release_gem.yml +1 -0
  4. data/.github/workflows/test-ruby-3.yml +19 -0
  5. data/.github/workflows/test.yml +16 -7
  6. data/.gitignore +3 -1
  7. data/CHANGELOG.md +53 -0
  8. data/DEVELOPER_DOCUMENTATION.md +13 -0
  9. data/DEVELOPER_SETUP.md +62 -3
  10. data/Dockerfile +1 -0
  11. data/ISSUES.md +7 -7
  12. data/config.ru +2 -1
  13. data/db/ddl_statements/head_pact_tags.rb +24 -1
  14. data/db/ddl_statements/latest_tagged_pact_consumer_version_orders.rb +11 -0
  15. data/db/ddl_statements/latest_tagged_pact_publications.rb +6 -0
  16. data/db/ddl_statements/latest_verification_ids_for_consumer_version_tags.rb +13 -0
  17. data/db/migrations/20210117_add_branch_to_version.rb +9 -0
  18. data/db/migrations/20210202_add_created_at_to_head_pact_tags.rb +14 -0
  19. data/db/migrations/20210205_add_pacticipant_id_to_tag.rb +17 -0
  20. data/db/migrations/20210206_add_index_to_tags_and_versions.rb +27 -0
  21. data/db/migrations/20210207_optimise_latest_verification_ids_for_consumer_version_tags.rb +13 -0
  22. data/db/migrations/20210208_optimise_latest_tagged_pact_cv_orders.rb +13 -0
  23. data/db/migrations/20210210_create_environments_table.rb +16 -0
  24. data/docker-compose-dev-postgres.yml +9 -1
  25. data/lib/pact_broker/api.rb +7 -2
  26. data/lib/pact_broker/api/contracts/dry_validation_predicates.rb +8 -0
  27. data/lib/pact_broker/api/contracts/environment_schema.rb +49 -0
  28. data/lib/pact_broker/api/decorators/base_decorator.rb +11 -0
  29. data/lib/pact_broker/api/decorators/dashboard_decorator.rb +5 -1
  30. data/lib/pact_broker/api/decorators/environment_decorator.rb +30 -0
  31. data/lib/pact_broker/api/decorators/environments_decorator.rb +21 -0
  32. data/lib/pact_broker/api/decorators/matrix_decorator.rb +3 -1
  33. data/lib/pact_broker/api/decorators/verifiable_pacts_query_decorator.rb +2 -0
  34. data/lib/pact_broker/api/decorators/version_decorator.rb +15 -2
  35. data/lib/pact_broker/api/pact_broker_urls.rb +8 -0
  36. data/lib/pact_broker/api/resources/default_base_resource.rb +18 -0
  37. data/lib/pact_broker/api/resources/environment.rb +76 -0
  38. data/lib/pact_broker/api/resources/environments.rb +75 -0
  39. data/lib/pact_broker/api/resources/index.rb +20 -0
  40. data/lib/pact_broker/api/resources/latest_version.rb +27 -0
  41. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -0
  42. data/lib/pact_broker/api/resources/version.rb +15 -9
  43. data/lib/pact_broker/app.rb +1 -1
  44. data/lib/pact_broker/certificates/certificate.rb +1 -1
  45. data/lib/pact_broker/config/setting.rb +1 -1
  46. data/lib/pact_broker/config/space_delimited_integer_list.rb +25 -0
  47. data/lib/pact_broker/configuration.rb +22 -1
  48. data/lib/pact_broker/db/data_migrations/helpers.rb +4 -0
  49. data/lib/pact_broker/db/data_migrations/set_extra_columns_for_tags.rb +29 -0
  50. data/lib/pact_broker/db/migrate_data.rb +1 -0
  51. data/lib/pact_broker/db/seed_example_data.rb +13 -13
  52. data/lib/pact_broker/deployments/environment.rb +15 -0
  53. data/lib/pact_broker/deployments/environment_service.rb +39 -0
  54. data/lib/pact_broker/doc/views/index/environment.markdown +37 -0
  55. data/lib/pact_broker/doc/views/index/environments.markdown +53 -0
  56. data/lib/pact_broker/doc/views/index/latest-pact-versions.markdown +1 -1
  57. data/lib/pact_broker/doc/views/index/pacticipant-version-tag.markdown +1 -0
  58. data/lib/pact_broker/doc/views/index/pacticipant-version.markdown +13 -0
  59. data/lib/pact_broker/domain/index_item.rb +18 -4
  60. data/lib/pact_broker/domain/pacticipant.rb +9 -5
  61. data/lib/pact_broker/domain/tag.rb +131 -71
  62. data/lib/pact_broker/domain/verification.rb +3 -2
  63. data/lib/pact_broker/domain/version.rb +58 -23
  64. data/lib/pact_broker/domain/webhook.rb +6 -3
  65. data/lib/pact_broker/index/service.rb +55 -49
  66. data/lib/pact_broker/locale/en.yml +3 -1
  67. data/lib/pact_broker/matrix/quick_row.rb +8 -0
  68. data/lib/pact_broker/metrics/service.rb +1 -1
  69. data/lib/pact_broker/pacts/eager_loaders.rb +52 -0
  70. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +18 -13
  71. data/lib/pact_broker/pacts/lazy_loaders.rb +14 -0
  72. data/lib/pact_broker/pacts/pact_publication.rb +38 -84
  73. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +297 -0
  74. data/lib/pact_broker/pacts/pact_version.rb +1 -2
  75. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +286 -0
  76. data/lib/pact_broker/pacts/repository.rb +5 -240
  77. data/lib/pact_broker/pacts/selected_pact.rb +4 -0
  78. data/lib/pact_broker/pacts/selector.rb +56 -1
  79. data/lib/pact_broker/pacts/selectors.rb +16 -0
  80. data/lib/pact_broker/pacts/service.rb +2 -6
  81. data/lib/pact_broker/pacts/squash_pacts_for_verification.rb +1 -4
  82. data/lib/pact_broker/pacts/verifiable_pact.rb +23 -2
  83. data/lib/pact_broker/pacts/verifiable_pact_messages.rb +56 -16
  84. data/lib/pact_broker/repositories/helpers.rb +4 -0
  85. data/lib/pact_broker/services.rb +9 -0
  86. data/lib/pact_broker/tags/eager_loaders.rb +47 -0
  87. data/lib/pact_broker/tags/repository.rb +3 -1
  88. data/lib/pact_broker/tags/service.rb +0 -3
  89. data/lib/pact_broker/tags/tag_with_latest_flag.rb +1 -0
  90. data/lib/pact_broker/test/http_test_data_builder.rb +23 -7
  91. data/lib/pact_broker/test/test_data_builder.rb +50 -3
  92. data/lib/pact_broker/ui/view_models/index_item.rb +19 -2
  93. data/lib/pact_broker/ui/view_models/matrix_line.rb +38 -2
  94. data/lib/pact_broker/ui/views/index/show-with-tags.haml +14 -6
  95. data/lib/pact_broker/ui/views/matrix/show.haml +12 -2
  96. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +7 -5
  97. data/lib/pact_broker/version.rb +1 -1
  98. data/lib/pact_broker/versions/eager_loaders.rb +71 -0
  99. data/lib/pact_broker/versions/lazy_loaders.rb +13 -0
  100. data/lib/pact_broker/versions/repository.rb +22 -2
  101. data/lib/pact_broker/versions/service.rb +5 -1
  102. data/lib/pact_broker/webhooks/execution.rb +3 -2
  103. data/lib/pact_broker/webhooks/latest_triggered_webhook.rb +2 -0
  104. data/lib/pact_broker/webhooks/triggered_webhook.rb +11 -4
  105. data/lib/pact_broker/webhooks/webhook.rb +1 -1
  106. data/lib/pact_broker/webhooks/webhook_event.rb +1 -1
  107. data/lib/pact_broker/webhooks/webhook_execution_result.rb +6 -1
  108. data/lib/pact_broker/webhooks/webhook_request_logger.rb +7 -1
  109. data/lib/rack/pact_broker/set_base_url.rb +35 -5
  110. data/lib/sequel/plugins/upsert.rb +18 -4
  111. data/pact_broker.gemspec +1 -1
  112. data/public/javascripts/clipboard.js +8 -2
  113. data/public/stylesheets/index.css +26 -2
  114. data/public/stylesheets/matrix.css +0 -21
  115. data/regression/can_i_deploy_spec.rb +5 -4
  116. data/regression/index_spec.rb +26 -0
  117. data/regression/regression_helper.rb +29 -3
  118. data/regression/script/clear.sh +3 -0
  119. data/regression/script/run.sh +3 -0
  120. data/script/demonstrate-version-branches.rb +33 -0
  121. data/script/pry.rb +2 -2
  122. data/spec/features/create_environment_spec.rb +47 -0
  123. data/spec/features/create_tag_spec.rb +32 -0
  124. data/spec/features/create_version_spec.rb +70 -0
  125. data/spec/features/delete_environment_spec.rb +16 -0
  126. data/spec/features/end_deployment_spec.rb +29 -0
  127. data/spec/features/get_environment_spec.rb +19 -0
  128. data/spec/features/get_environments_spec.rb +20 -0
  129. data/spec/features/record_deployment_spec.rb +28 -0
  130. data/spec/features/update_environment_spec.rb +44 -0
  131. data/spec/fixtures/approvals/modifiable_resources.approved.json +6 -0
  132. data/spec/fixtures/dashboard.json +4 -2
  133. data/spec/lib/pact_broker/api/contracts/environment_schema_spec.rb +83 -0
  134. data/spec/lib/pact_broker/api/decorators/dashboard_decorator_spec.rb +4 -2
  135. data/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb +11 -6
  136. data/spec/lib/pact_broker/api/decorators/verifiable_pacts_query_decorator_spec.rb +6 -0
  137. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +18 -0
  138. data/spec/lib/pact_broker/api/resources/default_base_resource_approval_spec.rb +1 -1
  139. data/spec/lib/pact_broker/api/resources/provider_pacts_for_verification_spec.rb +4 -0
  140. data/spec/lib/pact_broker/api/resources/webhook_execution_result_spec.rb +56 -0
  141. data/spec/lib/pact_broker/config/space_delimited_integer_list_spec.rb +47 -0
  142. data/spec/lib/pact_broker/configuration_spec.rb +12 -0
  143. data/spec/lib/pact_broker/domain/tag_spec.rb +101 -27
  144. data/spec/lib/pact_broker/domain/version_spec.rb +103 -15
  145. data/spec/lib/pact_broker/domain/webhook_spec.rb +1 -1
  146. data/spec/lib/pact_broker/index/service_spec.rb +89 -15
  147. data/spec/lib/pact_broker/pacts/pact_publication_dataset_module_spec.rb +400 -0
  148. data/spec/lib/pact_broker/pacts/pact_publication_spec.rb +434 -14
  149. data/spec/lib/pact_broker/pacts/repository_find_for_verification_fallback_spec.rb +1 -1
  150. data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +1 -1
  151. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_branch_spec.rb +224 -0
  152. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +20 -7
  153. data/spec/lib/pact_broker/pacts/selector_spec.rb +3 -2
  154. data/spec/lib/pact_broker/pacts/service_find_for_verification_spec.rb +2 -3
  155. data/spec/lib/pact_broker/pacts/service_spec.rb +2 -2
  156. data/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb +57 -10
  157. data/spec/lib/pact_broker/tags/repository_spec.rb +2 -0
  158. data/spec/lib/pact_broker/versions/repository_spec.rb +54 -0
  159. data/spec/lib/pact_broker/webhooks/webhook_request_logger_spec.rb +8 -0
  160. data/spec/lib/rack/pact_broker/set_base_url_spec.rb +86 -0
  161. data/spec/lib/sequel/plugins/upsert_spec.rb +31 -3
  162. data/spec/migrations/change_migration_strategy_spec.rb +3 -3
  163. data/spec/service_consumers/hal_relation_proxy_app.rb +3 -1
  164. data/spec/service_consumers/provider_states_for_pact_broker_client.rb +16 -0
  165. data/spec/spec_helper.rb +17 -5
  166. data/spec/support/approvals.rb +24 -0
  167. data/spec/support/shared_examples_for_responses.rb +11 -0
  168. data/tasks/database.rb +1 -1
  169. data/tasks/db.rake +1 -0
  170. data/tasks/rspec.rake +1 -1
  171. data/vendor/hal-browser/styles.css +6 -0
  172. metadata +70 -11
@@ -16,7 +16,7 @@ module PactBroker
16
16
  associate(:many_to_one, :provider_version, class: "PactBroker::Domain::Version", key: :provider_version_id, primary_key: :id)
17
17
  associate(:many_to_one, :provider, class: "PactBroker::Domain::Pacticipant", key: :provider_id, primary_key: :id)
18
18
  associate(:many_to_one, :consumer, class: "PactBroker::Domain::Pacticipant", key: :consumer_id, primary_key: :id)
19
- associate(:one_to_many, :provider_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :provider_version_id, key: :version_id)
19
+ associate(:one_to_many, :provider_version_tags, :class => "PactBroker::Domain::Tag", primary_key: :provider_version_id, key: :version_id)
20
20
  plugin :serialization, :json, :test_results
21
21
 
22
22
  def before_create
@@ -197,7 +197,7 @@ end
197
197
 
198
198
  # Table: verifications
199
199
  # Columns:
200
- # id | integer | PRIMARY KEY DEFAULT nextval('verifications_id_seq'::regclass)
200
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
201
201
  # number | integer |
202
202
  # success | boolean | NOT NULL
203
203
  # provider_version | text |
@@ -209,6 +209,7 @@ end
209
209
  # test_results | text |
210
210
  # consumer_id | integer |
211
211
  # provider_id | integer |
212
+ # wip | boolean | NOT NULL DEFAULT false
212
213
  # Indexes:
213
214
  # verifications_pkey | PRIMARY KEY btree (id)
214
215
  # verifications_pact_version_id_number_index | UNIQUE btree (pact_version_id, number)
@@ -2,6 +2,8 @@ require 'pact_broker/db'
2
2
  require 'pact_broker/domain/order_versions'
3
3
  require 'pact_broker/repositories/helpers'
4
4
  require 'pact_broker/tags/tag_with_latest_flag'
5
+ require 'pact_broker/versions/eager_loaders'
6
+ require 'pact_broker/versions/lazy_loaders'
5
7
 
6
8
  module PactBroker
7
9
  module Domain
@@ -22,37 +24,51 @@ module PactBroker
22
24
 
23
25
  class Version < Sequel::Model
24
26
  plugin :timestamps, update_on_create: true
25
- plugin :insert_ignore, identifying_columns: [:pacticipant_id, :number]
27
+ plugin :upsert, { identifying_columns: [:pacticipant_id, :number], ignore_columns_on_update: [:id, :created_at, :order] }
26
28
 
27
29
  set_primary_key :id
28
30
  one_to_many :pact_publications, order: :revision_number, class: "PactBroker::Pacts::PactPublication", key: :consumer_version_id
29
31
  associate(:many_to_one, :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id)
30
32
  one_to_many :tags, :reciprocal => :version, order: :created_at
31
33
 
32
- one_to_many :tags_with_latest_flag, :class => "PactBroker::Tags::TagWithLatestFlag", primary_keys: [:id], key: [:version_id], :eager_loader=>(proc do |eo_opts|
33
- tags_for_versions = PactBroker::Domain::Tag.where(version_id: eo_opts[:key_hash][:id].keys)
34
- latest_tag_for_pacticipants = PactBroker::Domain::Tag.latest_tags_for_pacticipant_ids(eo_opts[:rows].collect(&:pacticipant_id)).all
35
-
36
- eo_opts[:rows].each{|row| row.associations[:tags_with_latest_flag] = [] }
37
-
38
- tags_for_versions.each do | tag |
39
- latest = latest_tag_for_pacticipants.any? { |latest_tag| latest_tag.name == tag.name && latest_tag.version_id == tag.version_id }
40
- eo_opts[:id_map][tag.version_id].each do | version |
41
- version.associations[:tags_with_latest_flag] << EagerTagWithLatestFlag.new(tag, latest)
42
- end
43
- end
44
- end)
34
+ many_to_one :latest_version_for_pacticipant, read_only: true, key: :id,
35
+ class: Version,
36
+ dataset: lambda { Version.latest_version_for_pacticipant(pacticipant) },
37
+ eager_loader: PactBroker::Versions::EagerLoaders::LatestVersionForPacticipant
45
38
 
39
+ many_to_one :latest_version_for_branch, read_only: true, key: :id,
40
+ class: Version,
41
+ dataset: PactBroker::Versions::LazyLoaders::LATEST_VERSION_FOR_BRANCH,
42
+ eager_loader: PactBroker::Versions::EagerLoaders::LatestVersionForBranch
46
43
 
47
44
  dataset_module do
48
45
  include PactBroker::Repositories::Helpers
49
46
 
47
+ def latest_version_for_pacticipant(pacticipant)
48
+ where(pacticipant: pacticipant)
49
+ .order(Sequel.desc(:order))
50
+ .limit(1)
51
+ end
52
+
50
53
  def for(pacticipant_name, version_number)
51
54
  where_pacticipant_name(pacticipant_name).where_number(version_number).single_record
52
55
  end
53
56
 
57
+ def latest_versions_for_pacticipant_branches(pacticipant_id, branches)
58
+ query = Version.where(Sequel[:versions][:pacticipant_id] => pacticipant_id, Sequel[:versions][:branch] => branches)
59
+
60
+ self_join = {
61
+ Sequel[:versions][:pacticipant_id] => Sequel[:versions_2][:pacticipant_id],
62
+ Sequel[:versions][:branch] => Sequel[:versions_2][:branch]
63
+ }
64
+ query.select_all_qualified.left_join(query, self_join, table_alias: :versions_2) do
65
+ Sequel[:versions_2][:order] > Sequel[:versions][:order]
66
+ end
67
+ .where(Sequel[:versions_2][:order] => nil)
68
+ end
69
+
54
70
  def where_pacticipant_name(pacticipant_name)
55
- where(pacticipant_id: db[:pacticipants].select(:id).where(name_like(:name, pacticipant_name)))
71
+ where(Sequel[:versions][:pacticipant_id] => db[:pacticipants].select(:id).where(name_like(:name, pacticipant_name)))
56
72
  # If we do a join, we get the extra columns from the pacticipant table that then
57
73
  # make == not work
58
74
  # join(:pacticipants) do | p |
@@ -118,7 +134,7 @@ module PactBroker
118
134
  Sequel[:versions][:order] => Sequel[:latest][:latest_version_order]
119
135
  }
120
136
 
121
- group_by_cols = selector.tag == true ? [:pacticipant_id, Sequel[:tags][:name]] : [:pacticipant_id]
137
+ group_by_cols = selector.tag == true ? [Sequel[:versions][:pacticipant_id], Sequel[:tags][:name]] : [Sequel[:versions][:pacticipant_id]]
122
138
 
123
139
  max_order_for_each_pacticipant = query
124
140
  .select_group(*group_by_cols)
@@ -128,9 +144,12 @@ module PactBroker
128
144
  end
129
145
  end
130
146
 
147
+ # Isn't called on upsert when the record is updated with Sqlite
148
+ # Is called with Postgres/MySQL
149
+ # Haven't had time to dig into why
131
150
  def after_create
132
151
  super
133
- OrderVersions.(self)
152
+ OrderVersions.(self) unless self.order
134
153
  refresh
135
154
  end
136
155
 
@@ -147,29 +166,45 @@ module PactBroker
147
166
  "Version #{number} - #{updated_at.to_time.localtime.strftime("%d/%m/%Y")}"
148
167
  end
149
168
 
169
+ def head_tags
170
+ tags.select(&:latest_for_pacticipant?)
171
+ end
172
+
150
173
  # What about provider??? This makes no sense
151
174
  def latest_pact_publication
152
175
  pact_publications.last
153
176
  end
177
+
178
+ def latest_for_branch?
179
+ branch ? latest_version_for_branch.order == order : nil
180
+ end
181
+
182
+ def latest_for_pacticipant?
183
+ latest_version_for_pacticipant == self
184
+ end
154
185
  end
155
186
  end
156
187
  end
157
188
 
158
189
  # Table: versions
159
190
  # Columns:
160
- # id | integer | PRIMARY KEY DEFAULT nextval('versions_id_seq'::regclass)
191
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
161
192
  # number | text |
162
193
  # repository_ref | text |
163
194
  # pacticipant_id | integer | NOT NULL
164
195
  # order | integer |
165
196
  # created_at | timestamp without time zone | NOT NULL
166
197
  # updated_at | timestamp without time zone | NOT NULL
198
+ # branch | text |
199
+ # build_url | text |
167
200
  # Indexes:
168
- # versions_pkey | PRIMARY KEY btree (id)
169
- # uq_ver_ppt_ord | UNIQUE btree (pacticipant_id, "order")
170
- # versions_pacticipant_id_number_index | UNIQUE btree (pacticipant_id, number)
171
- # ndx_ver_num | btree (number)
172
- # ndx_ver_ord | btree ("order")
201
+ # versions_pkey | PRIMARY KEY btree (id)
202
+ # uq_ver_ppt_ord | UNIQUE btree (pacticipant_id, "order")
203
+ # versions_pacticipant_id_number_index | UNIQUE btree (pacticipant_id, number)
204
+ # ndx_ver_num | btree (number)
205
+ # ndx_ver_ord | btree ("order")
206
+ # versions_pacticipant_id_branch_order_index | btree (pacticipant_id, branch, "order")
207
+ # versions_pacticipant_id_order_desc_index | btree (pacticipant_id, "order" DESC)
173
208
  # Foreign key constraints:
174
209
  # versions_pacticipant_id_fkey | (pacticipant_id) REFERENCES pacticipants(id)
175
210
  # Referenced By:
@@ -54,13 +54,16 @@ module PactBroker
54
54
 
55
55
  def execute pact, verification, event_context, options
56
56
  logger.info "Executing #{self} event_context=#{event_context}"
57
- webhook_request = request.build(template_parameters(pact, verification, event_context, options))
57
+ template_params = template_parameters(pact, verification, event_context, options)
58
+ webhook_request = request.build(template_params)
58
59
  http_response, error = execute_request(webhook_request)
59
60
 
61
+ logs = generate_logs(webhook_request, http_response, error, event_context, options.fetch(:logging_options))
62
+ http_request = webhook_request.http_request
60
63
  PactBroker::Webhooks::WebhookExecutionResult.new(
61
- webhook_request.http_request,
64
+ http_request,
62
65
  http_response,
63
- generate_logs(webhook_request, http_response, error, event_context, options.fetch(:logging_options)),
66
+ logs,
64
67
  error
65
68
  )
66
69
  end
@@ -13,14 +13,6 @@ module PactBroker
13
13
  extend PactBroker::Services
14
14
  extend PactBroker::Logging
15
15
 
16
- COLS = [:id, :consumer_name, :provider_name, :consumer_version_order]
17
- LATEST_PPS = Sequel::Model.db[:latest_pact_publications].select(*COLS)
18
- LATEST_TAGGED_PPS = Sequel::Model.db[:latest_tagged_pact_publications].select(*COLS)
19
- HEAD_PP_ORDER_COLUMNS = [
20
- Sequel.asc(Sequel.function(:lower, :consumer_name)),
21
- Sequel.desc(:consumer_version_order),
22
- Sequel.asc(Sequel.function(:lower, :provider_name))
23
- ].freeze
24
16
  DEFAULT_PAGE_SIZE = 30
25
17
  DEFAULT_PAGE_NUMBER = 1
26
18
 
@@ -40,27 +32,26 @@ module PactBroker
40
32
 
41
33
  def self.find_index_items options = {}
42
34
  latest_verifications_for_cv_tags = latest_verifications_for_consumer_version_tags(options)
43
- latest_pact_publication_ids = latest_pact_publications.select(:id).all.collect{ |h| h[:id] }
35
+ latest_pp_ids = latest_pact_publication_ids
44
36
 
45
37
  # We only need to know if a webhook exists for an integration, not what its properties are
46
38
  webhooks = PactBroker::Webhooks::Webhook.select(:consumer_id, :provider_id).distinct.all
47
39
 
48
- pact_publication_ids = head_pact_publication_ids(options)
49
- pagination_record_count = pact_publication_ids.pagination_record_count
40
+ pact_publication_query = head_pact_publications(options)
41
+ pagination_record_count = pact_publication_query.pagination_record_count
50
42
 
51
- pact_publications = pact_publication_scope
52
- .where(id: pact_publication_ids)
53
- .select_all_qualified
43
+ pact_publications = pact_publication_query
54
44
  .eager(:consumer)
55
45
  .eager(:provider)
56
46
  .eager(:pact_version)
57
47
  .eager(integration: [{latest_verification: :provider_version}, :latest_triggered_webhooks])
58
- .eager(:consumer_version)
59
- .eager(latest_verification: { provider_version: :tags_with_latest_flag })
60
- .eager(:head_pact_tags)
48
+ .eager(consumer_version: [:latest_version_for_branch, { tags: :head_tag }])
49
+ .eager(latest_verification: { provider_version: [:latest_version_for_branch, { tags: :head_tag } ] })
50
+ .eager(:head_pact_publications_for_tags)
61
51
 
62
52
  index_items = pact_publications.all.collect do | pact_publication |
63
- is_overall_latest_for_integration = latest_pact_publication_ids.include?(pact_publication.id)
53
+ is_overall_latest_for_integration = latest_pp_ids.include?(pact_publication.id)
54
+
64
55
  latest_verification = latest_verification_for_pseudo_branch(pact_publication, is_overall_latest_for_integration, latest_verifications_for_cv_tags, options[:tags])
65
56
  webhook = webhooks.find{ |webhook| webhook.is_for?(pact_publication.integration) }
66
57
 
@@ -72,8 +63,9 @@ module PactBroker
72
63
  latest_verification,
73
64
  webhook ? [webhook]: [],
74
65
  pact_publication.integration.latest_triggered_webhooks,
75
- consumer_version_tags(pact_publication, options[:tags]),
76
- options[:tags] && latest_verification ? latest_verification.provider_version.tags_with_latest_flag.select(&:latest?) : []
66
+ consumer_version_tags(pact_publication, options[:tags]).sort_by(&:created_at).collect(&:name),
67
+ options[:tags] && latest_verification ? latest_verification.provider_version.tags.select(&:latest_for_pacticipant?).sort_by(&:created_at) : [],
68
+ pact_publication.latest_for_branch?
77
69
  )
78
70
  end.sort
79
71
 
@@ -98,32 +90,26 @@ module PactBroker
98
90
 
99
91
  def self.consumer_version_tags(pact_publication, tags_option)
100
92
  if tags_option == true
101
- pact_publication.head_pact_tags.collect(&:name)
93
+ pact_publication.head_pact_tags
102
94
  elsif tags_option.is_a?(Array)
103
- pact_publication.head_pact_tags.collect(&:name) & tags_option
95
+ pact_publication.head_pact_tags.select{ |tag| tags_option.include?(tag.name)}
104
96
  else
105
97
  []
106
98
  end
107
99
  end
108
100
 
109
101
  def self.find_index_items_for_api(consumer_name: nil, provider_name: nil, **ignored)
110
- latest_pact_publication_ids = latest_pact_publications.select(:id).all.collect{ |h| h[:id] }
111
- pact_publication_ids = head_pact_publication_ids(consumer_name: consumer_name, provider_name: provider_name, tags: true)
112
-
113
- pact_publications = pact_publication_scope
114
- .where(id: pact_publication_ids)
115
- .select_all_qualified
102
+ latest_pp_ids = latest_pact_publication_ids
103
+ pact_publications = head_pact_publications(consumer_name: consumer_name, provider_name: provider_name, tags: true)
116
104
  .eager(:consumer)
117
105
  .eager(:provider)
118
106
  .eager(:pact_version)
119
- .eager(:consumer_version)
120
- .eager(latest_verification: { provider_version: :tags_with_latest_flag })
121
- .eager(:head_pact_tags)
122
-
107
+ .eager(consumer_version: [:latest_version_for_branch, { tags: :head_tag }])
108
+ .eager(latest_verification: { provider_version: [:latest_version_for_branch, { tags: :head_tag }]})
109
+ .eager(:head_pact_publications_for_tags)
123
110
 
124
111
  pact_publications.all.collect do | pact_publication |
125
-
126
- is_overall_latest_for_integration = latest_pact_publication_ids.include?(pact_publication.id)
112
+ is_overall_latest_for_integration = latest_pp_ids.include?(pact_publication.id)
127
113
 
128
114
  PactBroker::Domain::IndexItem.create(
129
115
  pact_publication.consumer,
@@ -133,42 +119,62 @@ module PactBroker
133
119
  pact_publication.latest_verification,
134
120
  [],
135
121
  [],
136
- pact_publication.head_pact_tags.collect(&:name),
137
- pact_publication.latest_verification ? pact_publication.latest_verification.provider_version.tags_with_latest_flag.select(&:latest?) : []
122
+ pact_publication.head_pact_tags.sort_by(&:created_at).collect(&:name),
123
+ pact_publication.latest_verification ? pact_publication.latest_verification.provider_version.tags.select(&:latest_for_pacticipant?).sort_by(&:created_at) : []
138
124
  )
139
125
  end.sort
140
126
  end
141
127
 
142
128
  def self.latest_pact_publications
143
- db[:latest_pact_publications]
129
+ PactBroker::Pacts::PactPublication.overall_latest
130
+ end
131
+
132
+ def self.latest_pact_publication_ids
133
+ PactBroker::Pacts::PactPublication.select(Sequel[:pact_publications][:id]).overall_latest.collect(&:id)
144
134
  end
145
135
 
146
136
  def self.db
147
137
  PactBroker::Pacts::PactPublication.db
148
138
  end
149
139
 
150
- def self.head_pact_publication_ids(options = {})
151
- query = if options[:tags].is_a?(Array)
152
- LATEST_PPS.union(LATEST_TAGGED_PPS.where(tag_name: options[:tags]))
153
- elsif options[:tags]
154
- LATEST_PPS.union(LATEST_TAGGED_PPS)
155
- else
156
- LATEST_PPS
157
- end
140
+ def self.head_pact_publications(options = {})
141
+ base = PactBroker::Pacts::PactPublication.select(Sequel[:pact_publications][:id])
158
142
 
159
143
  if options[:consumer_name]
160
- query = query.where(PactBroker::Repositories::Helpers.name_like(:consumer_name, options[:consumer_name]))
144
+ consumer = pacticipant_repository.find_by_name!(options[:consumer_name])
145
+ base = base.for_consumer(consumer)
161
146
  end
162
147
 
163
148
  if options[:provider_name]
164
- query = query.where(PactBroker::Repositories::Helpers.name_like(:provider_name, options[:provider_name]))
149
+ provider = pacticipant_repository.find_by_name!(options[:provider_name])
150
+ base = base.for_provider(provider)
165
151
  end
166
152
 
167
- query.order(*HEAD_PP_ORDER_COLUMNS)
153
+ latest = base.overall_latest
154
+ ids_query = if options[:tags].is_a?(Array)
155
+ latest.union(base.latest_for_consumer_tag(options[:tags]))
156
+ elsif options[:tags]
157
+ latest.union(base.latest_by_consumer_tag)
158
+ else
159
+ latest
160
+ end
161
+
162
+ query = PactBroker::Pacts::PactPublication.select_all_qualified.where(Sequel[:pact_publications][:id] => ids_query)
163
+ .join_consumers(:consumers)
164
+ .join_providers(:providers)
165
+ .join(:versions, { Sequel[:pact_publications][:consumer_version_id] => Sequel[:cv][:id] }, { table_alias: :cv } )
166
+
167
+ order_columns = [
168
+ Sequel.asc(Sequel.function(:lower, Sequel[:consumers][:name])),
169
+ Sequel.desc(Sequel[:cv][:order]),
170
+ Sequel.asc(Sequel.function(:lower, Sequel[:providers][:name]))
171
+ ]
172
+
173
+ query.order(*order_columns)
168
174
  .paginate(options[:page_number] || DEFAULT_PAGE_NUMBER, options[:page_size] || DEFAULT_PAGE_SIZE)
169
- .select(:id)
170
175
  end
171
176
 
177
+ # eager loading the tag stuff doesn't seem to make it quicker
172
178
  def self.latest_verifications_for_consumer_version_tags(options)
173
179
  # server side rendered index page with tags[]=a&tags=[]b
174
180
  if options[:tags].is_a?(Array)
@@ -9,7 +9,8 @@ en:
9
9
  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"
10
10
  non_templated_host?: "cannot have a template parameter in the host"
11
11
  pacticipant_exists?: "does not match an existing pacticipant"
12
-
12
+ single_line?: "cannot contain multiple lines"
13
+ no_spaces?: "cannot contain spaces"
13
14
 
14
15
  pact_broker:
15
16
  messages:
@@ -46,6 +47,7 @@ en:
46
47
  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"
47
48
  invalid_webhook_uuid: The UUID can only contain the characters A-Z, a-z, 0-9, _ and -, and must be 16 or more characters.
48
49
  pacticipant_not_found: No pacticipant with name '%{name}' found
50
+ environment_name_must_be_unique: Another environment with name '%{name}' already exists.
49
51
  duplicate_pacticipant: |
50
52
  This is the first time a pact has been published for "%{new_name}".
51
53
  The name "%{new_name}" is very similar to the following existing consumers/providers:
@@ -351,6 +351,10 @@ module PactBroker
351
351
  consumer_version.number
352
352
  end
353
353
 
354
+ def consumer_version_branch
355
+ consumer_version.branch
356
+ end
357
+
354
358
  def consumer_version_order
355
359
  consumer_version.order
356
360
  end
@@ -363,6 +367,10 @@ module PactBroker
363
367
  provider_version&.number
364
368
  end
365
369
 
370
+ def provider_version_branch
371
+ provider_version&.branch
372
+ end
373
+
366
374
  def provider_version_order
367
375
  provider_version&.order
368
376
  end
@@ -58,7 +58,7 @@ module PactBroker
58
58
  tags: {
59
59
  count: PactBroker::Domain::Tag.count,
60
60
  distinctCount: PactBroker::Domain::Tag.select(:name).distinct.count,
61
- distinctWithPacticipantCount: PactBroker::Domain::Tag.join(:versions, { id: :version_id }).select_group(:name, :pacticipant_id).count
61
+ distinctWithPacticipantCount: PactBroker::Domain::Tag.join(:versions, { id: :version_id }).select_group(:name, Sequel[:versions][:pacticipant_id]).count
62
62
  },
63
63
  triggeredWebhooks: {
64
64
  count: PactBroker::Webhooks::TriggeredWebhook.count
@@ -0,0 +1,52 @@
1
+ module PactBroker
2
+ module Pacts
3
+ module EagerLoaders
4
+ class HeadPactPublicationsForTags
5
+ def self.call(eo)
6
+ pact_publications = eo[:rows]
7
+ initialize_association(pact_publications)
8
+ populate_associations(group_by_consumer_and_provider_ids(pact_publications))
9
+ end
10
+
11
+ def self.initialize_association(pact_publications)
12
+ pact_publications.each { |pp| pp.associations[:head_pact_publications_for_tags] = [] }
13
+ end
14
+
15
+ def self.group_by_consumer_and_provider_ids(pact_publications)
16
+ pact_publications.group_by{ |pact_publication| [pact_publication.consumer_id, pact_publication.provider_id] }
17
+ end
18
+
19
+ def self.populate_associations(grouped_pact_publications)
20
+ grouped_pact_publications.each do | key, pact_publications |
21
+ populate_associations_for_consumer_and_provider(key, pact_publications)
22
+ end
23
+ end
24
+
25
+ def self.populate_associations_for_consumer_and_provider(key, pact_publications)
26
+ head_pact_publications_by_tag = hash_of_head_pact_publications(
27
+ pact_publications.first.class,
28
+ pact_publications.first.consumer,
29
+ pact_publications.first.provider,
30
+ pact_publications.flat_map{ |pp| pp.consumer_version_tags.collect(&:name) }
31
+ )
32
+
33
+ pact_publications.each do | pact_publication |
34
+ pact_publication.consumer_version_tags.collect(&:name).sort.each do | tag_name |
35
+ pact_publication.associations[:head_pact_publications_for_tags] << head_pact_publications_by_tag[tag_name]
36
+ end
37
+ end
38
+ end
39
+
40
+ def self.hash_of_head_pact_publications pact_publication_class, consumer, provider, tag_names
41
+ pact_publication_class
42
+ .for_consumer(consumer)
43
+ .for_provider(provider)
44
+ .latest_for_consumer_tag(tag_names)
45
+ .each_with_object({}) do | head_pact_publication, hash |
46
+ hash[head_pact_publication.values.fetch(:tag_name)] = head_pact_publication
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end