pact_broker 2.75.0 → 2.78.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +12 -0
  3. data/.github/workflows/test-ruby-3.yml +19 -0
  4. data/.github/workflows/test.yml +19 -4
  5. data/.gitignore +3 -1
  6. data/CHANGELOG.md +48 -0
  7. data/DEVELOPER_SETUP.md +62 -3
  8. data/Dockerfile +1 -0
  9. data/ISSUES.md +13 -5
  10. data/README.md +1 -1
  11. data/config.ru +1 -0
  12. data/db/ddl_statements/head_pact_tags.rb +24 -1
  13. data/db/ddl_statements/latest_tagged_pact_consumer_version_orders.rb +11 -0
  14. data/db/ddl_statements/latest_tagged_pact_publications.rb +6 -0
  15. data/db/ddl_statements/latest_verification_ids_for_consumer_version_tags.rb +13 -0
  16. data/db/migrations/20210117_add_branch_to_version.rb +9 -0
  17. data/db/migrations/20210202_add_created_at_to_head_pact_tags.rb +14 -0
  18. data/db/migrations/20210205_add_pacticipant_id_to_tag.rb +17 -0
  19. data/db/migrations/20210206_add_index_to_tags_and_versions.rb +27 -0
  20. data/db/migrations/20210207_optimise_latest_verification_ids_for_consumer_version_tags.rb +13 -0
  21. data/db/migrations/20210208_optimise_latest_tagged_pact_cv_orders.rb +13 -0
  22. data/db/migrations/20210210_create_environments_table.rb +16 -0
  23. data/docker-compose-issue-repro-with-pact-broker-docker-image.yml +44 -0
  24. data/lib/pact_broker/api.rb +7 -2
  25. data/lib/pact_broker/api/contracts/dry_validation_predicates.rb +8 -0
  26. data/lib/pact_broker/api/contracts/environment_schema.rb +49 -0
  27. data/lib/pact_broker/api/decorators/base_decorator.rb +11 -0
  28. data/lib/pact_broker/api/decorators/dashboard_decorator.rb +5 -1
  29. data/lib/pact_broker/api/decorators/environment_decorator.rb +30 -0
  30. data/lib/pact_broker/api/decorators/environments_decorator.rb +21 -0
  31. data/lib/pact_broker/api/decorators/matrix_decorator.rb +8 -2
  32. data/lib/pact_broker/api/decorators/reason_decorator.rb +2 -2
  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/paths.rb +5 -0
  37. data/lib/pact_broker/api/resources/default_base_resource.rb +15 -2
  38. data/lib/pact_broker/api/resources/environment.rb +76 -0
  39. data/lib/pact_broker/api/resources/environments.rb +75 -0
  40. data/lib/pact_broker/api/resources/index.rb +20 -0
  41. data/lib/pact_broker/api/resources/latest_version.rb +27 -0
  42. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -0
  43. data/lib/pact_broker/api/resources/verifications.rb +5 -2
  44. data/lib/pact_broker/api/resources/version.rb +15 -9
  45. data/lib/pact_broker/api/resources/webhook_execution.rb +1 -1
  46. data/lib/pact_broker/app.rb +3 -0
  47. data/lib/pact_broker/certificates/certificate.rb +1 -1
  48. data/lib/pact_broker/config/setting.rb +1 -1
  49. data/lib/pact_broker/config/space_delimited_integer_list.rb +25 -0
  50. data/lib/pact_broker/configuration.rb +18 -1
  51. data/lib/pact_broker/db/data_migrations/helpers.rb +4 -0
  52. data/lib/pact_broker/db/data_migrations/set_extra_columns_for_tags.rb +29 -0
  53. data/lib/pact_broker/db/migrate_data.rb +1 -0
  54. data/lib/pact_broker/db/seed_example_data.rb +13 -13
  55. data/lib/pact_broker/deployments/environment.rb +15 -0
  56. data/lib/pact_broker/deployments/environment_service.rb +39 -0
  57. data/lib/pact_broker/doc/controllers/app.rb +1 -1
  58. data/lib/pact_broker/doc/views/index/environment.markdown +37 -0
  59. data/lib/pact_broker/doc/views/index/environments.markdown +53 -0
  60. data/lib/pact_broker/doc/views/index/latest-pact-versions.markdown +1 -1
  61. data/lib/pact_broker/doc/views/index/pacticipant-version-tag.markdown +1 -0
  62. data/lib/pact_broker/doc/views/index/pacticipant-version.markdown +13 -0
  63. data/lib/pact_broker/domain/index_item.rb +18 -4
  64. data/lib/pact_broker/domain/pacticipant.rb +9 -5
  65. data/lib/pact_broker/domain/tag.rb +131 -71
  66. data/lib/pact_broker/domain/verification.rb +3 -2
  67. data/lib/pact_broker/domain/version.rb +58 -23
  68. data/lib/pact_broker/domain/webhook.rb +12 -9
  69. data/lib/pact_broker/hash_refinements.rb +4 -0
  70. data/lib/pact_broker/index/service.rb +55 -49
  71. data/lib/pact_broker/locale/en.yml +3 -1
  72. data/lib/pact_broker/matrix/quick_row.rb +8 -0
  73. data/lib/pact_broker/metrics/service.rb +1 -1
  74. data/lib/pact_broker/pacts/eager_loaders.rb +52 -0
  75. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +18 -13
  76. data/lib/pact_broker/pacts/lazy_loaders.rb +14 -0
  77. data/lib/pact_broker/pacts/pact_publication.rb +38 -84
  78. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +297 -0
  79. data/lib/pact_broker/pacts/pact_version.rb +1 -2
  80. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +286 -0
  81. data/lib/pact_broker/pacts/repository.rb +5 -231
  82. data/lib/pact_broker/pacts/selected_pact.rb +4 -0
  83. data/lib/pact_broker/pacts/selector.rb +56 -1
  84. data/lib/pact_broker/pacts/selectors.rb +16 -0
  85. data/lib/pact_broker/pacts/service.rb +6 -8
  86. data/lib/pact_broker/pacts/squash_pacts_for_verification.rb +1 -4
  87. data/lib/pact_broker/pacts/verifiable_pact.rb +45 -2
  88. data/lib/pact_broker/pacts/verifiable_pact_messages.rb +56 -16
  89. data/lib/pact_broker/repositories/helpers.rb +4 -0
  90. data/lib/pact_broker/services.rb +9 -0
  91. data/lib/pact_broker/tags/eager_loaders.rb +47 -0
  92. data/lib/pact_broker/tags/repository.rb +3 -1
  93. data/lib/pact_broker/tags/service.rb +0 -3
  94. data/lib/pact_broker/tags/tag_with_latest_flag.rb +1 -0
  95. data/lib/pact_broker/test/http_test_data_builder.rb +101 -35
  96. data/lib/pact_broker/test/test_data_builder.rb +50 -3
  97. data/lib/pact_broker/ui/app.rb +6 -0
  98. data/lib/pact_broker/ui/controllers/base_controller.rb +5 -1
  99. data/lib/pact_broker/ui/controllers/pacts.rb +18 -0
  100. data/lib/pact_broker/ui/view_models/index_item.rb +9 -0
  101. data/lib/pact_broker/ui/view_models/matrix_line.rb +36 -0
  102. data/lib/pact_broker/ui/views/index/show-with-tags.haml +8 -0
  103. data/lib/pact_broker/ui/views/matrix/show.haml +10 -0
  104. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +7 -5
  105. data/lib/pact_broker/verifications/service.rb +2 -1
  106. data/lib/pact_broker/version.rb +1 -1
  107. data/lib/pact_broker/versions/eager_loaders.rb +71 -0
  108. data/lib/pact_broker/versions/lazy_loaders.rb +13 -0
  109. data/lib/pact_broker/versions/repository.rb +22 -2
  110. data/lib/pact_broker/versions/service.rb +5 -1
  111. data/lib/pact_broker/webhooks/execution.rb +3 -2
  112. data/lib/pact_broker/webhooks/latest_triggered_webhook.rb +2 -0
  113. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +10 -3
  114. data/lib/pact_broker/webhooks/service.rb +8 -7
  115. data/lib/pact_broker/webhooks/trigger_service.rb +56 -23
  116. data/lib/pact_broker/webhooks/triggered_webhook.rb +14 -5
  117. data/lib/pact_broker/webhooks/webhook.rb +1 -1
  118. data/lib/pact_broker/webhooks/webhook_event.rb +1 -1
  119. data/lib/pact_broker/webhooks/webhook_execution_result.rb +4 -1
  120. data/lib/pact_broker/webhooks/webhook_request_logger.rb +5 -1
  121. data/lib/rack/pact_broker/set_base_url.rb +22 -0
  122. data/lib/sequel/plugins/upsert.rb +18 -4
  123. data/public/stylesheets/index.css +22 -1
  124. data/public/stylesheets/matrix.css +0 -21
  125. data/regression/can_i_deploy_spec.rb +5 -4
  126. data/regression/index_spec.rb +26 -0
  127. data/regression/regression_helper.rb +29 -3
  128. data/regression/script/clear.sh +3 -0
  129. data/regression/script/run.sh +3 -0
  130. data/script/demonstrate-version-branches.rb +33 -0
  131. data/script/pry.rb +2 -2
  132. data/script/reproduce-issue-starting-up.rb +13 -23
  133. data/script/reproduce-issue.rb +18 -14
  134. data/script/trigger-release.sh +1 -1
  135. data/script/webhook-server.ru +5 -5
  136. data/spec/features/create_environment_spec.rb +47 -0
  137. data/spec/features/create_tag_spec.rb +32 -0
  138. data/spec/features/create_version_spec.rb +70 -0
  139. data/spec/features/delete_environment_spec.rb +16 -0
  140. data/spec/features/end_deployment_spec.rb +29 -0
  141. data/spec/features/get_environment_spec.rb +19 -0
  142. data/spec/features/get_environments_spec.rb +20 -0
  143. data/spec/features/record_deployment_spec.rb +28 -0
  144. data/spec/features/update_environment_spec.rb +44 -0
  145. data/spec/fixtures/approvals/modifiable_resources.approved.json +6 -0
  146. data/spec/fixtures/dashboard.json +4 -2
  147. data/spec/integration/ui/index_spec.rb +4 -4
  148. data/spec/lib/pact_broker/api/contracts/environment_schema_spec.rb +83 -0
  149. data/spec/lib/pact_broker/api/decorators/dashboard_decorator_spec.rb +4 -2
  150. data/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb +11 -6
  151. data/spec/lib/pact_broker/api/decorators/reason_decorator_spec.rb +6 -6
  152. data/spec/lib/pact_broker/api/decorators/verifiable_pacts_query_decorator_spec.rb +6 -0
  153. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +18 -0
  154. data/spec/lib/pact_broker/api/resources/default_base_resource_approval_spec.rb +1 -1
  155. data/spec/lib/pact_broker/api/resources/default_base_resource_spec.rb +1 -12
  156. data/spec/lib/pact_broker/api/resources/provider_pacts_for_verification_spec.rb +4 -0
  157. data/spec/lib/pact_broker/api/resources/verifications_spec.rb +2 -5
  158. data/spec/lib/pact_broker/api/resources/webhook_execution_result_spec.rb +56 -0
  159. data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +3 -2
  160. data/spec/lib/pact_broker/config/space_delimited_integer_list_spec.rb +47 -0
  161. data/spec/lib/pact_broker/configuration_spec.rb +12 -0
  162. data/spec/lib/pact_broker/doc/controllers/app_spec.rb +3 -5
  163. data/spec/lib/pact_broker/domain/tag_spec.rb +101 -27
  164. data/spec/lib/pact_broker/domain/version_spec.rb +103 -15
  165. data/spec/lib/pact_broker/domain/webhook_spec.rb +7 -7
  166. data/spec/lib/pact_broker/index/service_spec.rb +89 -15
  167. data/spec/lib/pact_broker/pacts/pact_publication_dataset_module_spec.rb +400 -0
  168. data/spec/lib/pact_broker/pacts/pact_publication_spec.rb +434 -14
  169. data/spec/lib/pact_broker/pacts/repository_find_for_verification_fallback_spec.rb +1 -1
  170. data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +1 -1
  171. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_branch_spec.rb +224 -0
  172. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +46 -7
  173. data/spec/lib/pact_broker/pacts/selector_spec.rb +3 -2
  174. data/spec/lib/pact_broker/pacts/service_find_for_verification_spec.rb +2 -3
  175. data/spec/lib/pact_broker/pacts/service_spec.rb +9 -7
  176. data/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb +57 -10
  177. data/spec/lib/pact_broker/tags/repository_spec.rb +2 -0
  178. data/spec/lib/pact_broker/verifications/service_spec.rb +4 -1
  179. data/spec/lib/pact_broker/versions/repository_spec.rb +54 -0
  180. data/spec/lib/pact_broker/webhooks/render_spec.rb +6 -5
  181. data/spec/lib/pact_broker/webhooks/service_spec.rb +13 -9
  182. data/spec/lib/pact_broker/webhooks/trigger_service_spec.rb +60 -18
  183. data/spec/lib/pact_broker/webhooks/webhook_request_logger_spec.rb +8 -0
  184. data/spec/lib/sequel/plugins/upsert_spec.rb +31 -3
  185. data/spec/service_consumers/hal_relation_proxy_app.rb +3 -1
  186. data/spec/service_consumers/provider_states_for_pact_broker_client.rb +16 -0
  187. data/spec/spec_helper.rb +17 -5
  188. data/spec/support/approvals.rb +24 -0
  189. data/spec/support/shared_examples_for_responses.rb +11 -0
  190. data/tasks/db.rake +1 -0
  191. data/tasks/rspec.rake +1 -1
  192. metadata +69 -4
  193. data/docker-compose-issue-repro.yml +0 -22
@@ -5,6 +5,10 @@ module PactBroker
5
5
  def column_exists?(connection, table, column)
6
6
  connection.table_exists?(table) && connection.schema(table).find{|col| col.first == column }
7
7
  end
8
+
9
+ def columns_exist?(connection, table, columns)
10
+ columns.all?{ | column | column_exists?(connection, table, column) }
11
+ end
8
12
  end
9
13
  end
10
14
  end
@@ -0,0 +1,29 @@
1
+ require 'pact_broker/db/data_migrations/helpers'
2
+
3
+ module PactBroker
4
+ module DB
5
+ module DataMigrations
6
+ class SetExtraColumnsForTags
7
+ extend Helpers
8
+
9
+ def self.call(connection)
10
+ if columns_exist?(connection, :tags, [:version_id, :pacticipant_id]) &&
11
+ columns_exist?(connection, :versions, [:id, :pacticipant_id])
12
+ connection[:tags].update(
13
+ pacticipant_id: connection[:versions].select(:pacticipant_id)
14
+ .where(Sequel[:versions][:id] => Sequel[:tags][:version_id])
15
+ )
16
+ end
17
+
18
+ if columns_exist?(connection, :tags, [:version_id, :version_order]) &&
19
+ columns_exist?(connection, :versions, [:id, :order])
20
+ connection[:tags].update(
21
+ version_order: connection[:versions].select(:order)
22
+ .where(Sequel[:versions][:id] => Sequel[:tags][:version_id])
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -21,6 +21,7 @@ module PactBroker
21
21
  DataMigrations::DeleteDeprecatedWebhookExecutions.call(database_connection)
22
22
  DataMigrations::SetCreatedAtForLatestPactPublications.call(database_connection)
23
23
  DataMigrations::SetCreatedAtForLatestVerifications.call(database_connection)
24
+ DataMigrations::SetExtraColumnsForTags.call(database_connection)
24
25
  end
25
26
  end
26
27
  end
@@ -16,25 +16,25 @@ module PactBroker
16
16
  PactBroker::Test::TestDataBuilder.new
17
17
  .create_consumer(consumer_name, created_at: days_ago(16))
18
18
  .create_provider(provider_name, created_at: days_ago(16))
19
- .create_consumer_version("e15da45d3943bf10793a6d04cfb9f5dabe430fe2", created_at: days_ago(16))
19
+ .create_consumer_version("e15da45d3943bf10793a6d04cfb9f5dabe430fe2", branch: "main", created_at: days_ago(16))
20
20
  .create_consumer_version_tag("prod", created_at: days_ago(16))
21
- .create_consumer_version_tag("master", created_at: days_ago(16))
21
+ .create_consumer_version_tag("main", created_at: days_ago(16))
22
22
  .create_pact(json_content: pact_1, created_at: days_ago(16))
23
- .create_verification(provider_version: "1315e0b1924cb6f42751f977789be3559373033a", execution_date: days_ago(15))
24
- .create_provider_version_tag("master", created_at: days_ago(14))
23
+ .create_verification(provider_version: "1315e0b1924cb6f42751f977789be3559373033a", branch: "main", execution_date: days_ago(15))
24
+ .create_provider_version_tag("main", created_at: days_ago(14))
25
25
  .create_provider_version_tag("prod", created_at: days_ago(14))
26
- .create_verification(provider_version: "480e5aeb30467856ca995d0024d2c1800b0719e5", success: false, number: 2, execution_date: days_ago(14))
27
- .create_provider_version_tag("master", created_at: days_ago(14))
28
- .create_consumer_version("725c6ccb7cf7efc51b4394f9828585eea9c379d9", created_at: days_ago(7))
26
+ .create_verification(provider_version: "480e5aeb30467856ca995d0024d2c1800b0719e5", branch: "main", success: false, number: 2, execution_date: days_ago(14))
27
+ .create_provider_version_tag("main", created_at: days_ago(14))
28
+ .create_consumer_version("725c6ccb7cf7efc51b4394f9828585eea9c379d9", branch: "feat/new-thing", created_at: days_ago(7))
29
29
  .create_consumer_version_tag("feat-new-thing", created_at: days_ago(7))
30
30
  .create_pact(json_content: pact_2, created_at: days_ago(7))
31
- .create_consumer_version("7bd4d9173522826dc3e8704fd62dde0424f4c827", created_at: days_ago(1))
32
- .create_consumer_version_tag("master", created_at: days_ago(1))
31
+ .create_consumer_version("7bd4d9173522826dc3e8704fd62dde0424f4c827", branch: "main", created_at: days_ago(1))
32
+ .create_consumer_version_tag("main", created_at: days_ago(1))
33
33
  .create_pact(json_content: pact_3, created_at: days_ago(1))
34
- .create_verification(provider_version: "4fdf20082263d4c5038355a3b734be1c0054d1e1", execution_date: days_ago(1))
35
- .create_provider_version_tag("master", created_at: days_ago(1))
36
- .create_consumer_version("5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", created_at: days_ago(0.5))
37
- .create_consumer_version_tag("master", created_at: days_ago(0.5))
34
+ .create_verification(provider_version: "4fdf20082263d4c5038355a3b734be1c0054d1e1", branch: "main", execution_date: days_ago(1))
35
+ .create_provider_version_tag("main", created_at: days_ago(1))
36
+ .create_consumer_version("5556b8149bf8bac76bc30f50a8a2dd4c22c85f30", branch: "main", created_at: days_ago(0.5))
37
+ .create_consumer_version_tag("main", created_at: days_ago(0.5))
38
38
  .republish_same_pact(created_at: days_ago(0.5))
39
39
  end
40
40
 
@@ -0,0 +1,15 @@
1
+ require 'sequel'
2
+ require 'sequel/plugins/serialization'
3
+
4
+
5
+ module PactBroker
6
+ module Deployments
7
+ class Environment < Sequel::Model
8
+ OPEN_STRUCT_TO_JSON = lambda { |thing| Sequel.object_to_json(thing.collect(&:to_h)) }
9
+ JSON_TO_OPEN_STRUCT = lambda { | json | Sequel.parse_json(json).collect{ | hash| OpenStruct.new(hash) } }
10
+ plugin :upsert, identifying_columns: [:uuid]
11
+ plugin :serialization
12
+ serialize_attributes [OPEN_STRUCT_TO_JSON, JSON_TO_OPEN_STRUCT], :contacts
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ require 'pact_broker/deployments/environment'
2
+ require 'securerandom'
3
+
4
+ module PactBroker
5
+ module Deployments
6
+ module EnvironmentService
7
+
8
+ def self.next_uuid
9
+ SecureRandom.uuid
10
+ end
11
+
12
+ def self.create(uuid, environment)
13
+ environment.uuid = uuid
14
+ environment.save
15
+ end
16
+
17
+ def self.update(uuid, environment)
18
+ environment.uuid = uuid
19
+ environment.upsert
20
+ end
21
+
22
+ def self.find_all
23
+ PactBroker::Deployments::Environment.order(Sequel.function(:lower, :display_name)).all
24
+ end
25
+
26
+ def self.find(uuid)
27
+ PactBroker::Deployments::Environment.where(uuid: uuid).single_record
28
+ end
29
+
30
+ def self.find_by_name(name)
31
+ PactBroker::Deployments::Environment.where(name: name).single_record
32
+ end
33
+
34
+ def self.delete(uuid)
35
+ PactBroker::Deployments::Environment.where(uuid: uuid).delete
36
+ end
37
+ end
38
+ end
39
+ end
@@ -51,7 +51,7 @@ module PactBroker
51
51
  # https://www.acunetix.com/blog/articles/automated-detection-of-host-header-attacks/
52
52
  # Either use the explicitly configured base url or an empty string,
53
53
  # rather than request.base_url, which uses the X-Forwarded headers.
54
- env["pactbroker.base_url"] || PactBroker.configuration.base_url || ''
54
+ env["pactbroker.base_url"] || ''
55
55
  end
56
56
  end
57
57
  end
@@ -0,0 +1,37 @@
1
+ # Environment
2
+
3
+ Allowed methods: `GET`, `PUT`, `DELETE`
4
+
5
+ Path: `/environment/{uuid}`
6
+
7
+ ## Viewing an environment
8
+
9
+ Example:
10
+
11
+ curl http://${PACT_BROKER_HOST}/environments/79060381-269c-4769-9894-9ec3cab44729 \
12
+ -H "Accept: application/hal+json"
13
+ {
14
+ "uuid": "79060381-269c-4769-9894-9ec3cab44729",
15
+ "name": "test",
16
+ "displayName": "Test",
17
+ "production": false
18
+ }
19
+
20
+ ## Updating an environment
21
+
22
+ Example:
23
+
24
+ curl -X PUT http://${PACT_BROKER_HOST}/environments/79060381-269c-4769-9894-9ec3cab44729 \
25
+ -H "Content-Type: application/json" \
26
+ -H "Accept: application/hal+json" \
27
+ -d '{
28
+ "name": "test",
29
+ "displayName": "Test",
30
+ "production": false
31
+ }'
32
+
33
+ ## Deleting an environment
34
+
35
+ Example:
36
+
37
+ curl -v -X DELETE http://${PACT_BROKER_HOST}/environments/79060381-269c-4769-9894-9ec3cab44729
@@ -0,0 +1,53 @@
1
+ # Environments
2
+
3
+ Allowed methods: `GET`, `POST`
4
+
5
+ Path: `/environments`
6
+
7
+ ## Creating an environment
8
+
9
+ Send a POST to `/environments` with the environment payload.
10
+
11
+ Example:
12
+
13
+ curl http://${PACT_BROKER_HOST}/environments \
14
+ -H "Content-Type: application/json" \
15
+ -H "Accept: application/hal+json" \
16
+ -d '{
17
+ "name": "test",
18
+ "displayName": "Test",
19
+ "production": false
20
+ }'
21
+
22
+ Alternatively, you can use the HAL Browser.
23
+
24
+ * Click on the `API Browser` link at the top of the Pact Broker index page.
25
+ * In the `Links` section on the left, locate the `pb:environments` relation, and click on the yellow `!` "Perform non-GET request" button.
26
+ * In the `Body:` text box, fill in the required JSON properties.
27
+ * Click `Make Request`.
28
+
29
+ Properties:
30
+
31
+ * `uuid`: System generated unique identifier.
32
+ * `name`: Must be unique. No spaces allowed. This will be the name used in the `can-i-deploy` and `record-deployment` CLI commands. eg. "payments-sit-1"
33
+ * `displayName`: A more verbose name for the environment. "Payments Team SIT 1"
34
+ * `production`: Whether or not this environment is a production environment.
35
+
36
+ If all the services in the Broker are deployed to the same "public" internet, then there only needs to be one Production environment. If there are multiple segregated production environments (eg. when maintaining on-premises software for multiple customers ) then you should create a separate production Environment for each logical deployment environment.
37
+
38
+ ## Listing environments
39
+
40
+ `GET /environments`
41
+
42
+ {
43
+ "_embedded": {
44
+ "environments": [
45
+ {
46
+ "uuid": "79060381-269c-4769-9894-9ec3cab44729",
47
+ "name": "production",
48
+ "displayName": "Production",
49
+ "production": true
50
+ }
51
+ ]
52
+ }
53
+ }
@@ -1,5 +1,5 @@
1
1
  # Latest pact versions
2
2
 
3
- Allowed methods: GET
3
+ Allowed methods: `GET`
4
4
 
5
5
  A list of the latest pact versions for each consumer/provider pair. The "latest" is determined by inspecting the consumer version used when each pact was published.
@@ -1,6 +1,7 @@
1
1
  # Pacticipant version tag
2
2
 
3
3
  Allowed methods: `GET`, `PUT`, `DELETE`
4
+
4
5
  Path: `/pacticipants/{pacticipant}/versions/{version}/tags/{tag}`
5
6
 
6
7
  To create a tag, send an empty request with the URL specified above and `Content-Type` of `application/json`.
@@ -0,0 +1,13 @@
1
+ # Pacticipant version
2
+
3
+ Allowed methods: `GET`, `PUT`, `DELETE`
4
+
5
+ Path: `/pacticipants/{pacticipant}/versions/{version}`
6
+
7
+ ## Example
8
+
9
+ PUT http://broker/pacticipants/Bar/versions/1e70030c6579915e5ff56b107a0fd25cf5df7464
10
+ {
11
+ "branch": "main",
12
+ "buildUrl": "http://ci.mydomain/my-job/1"
13
+ }
@@ -13,13 +13,13 @@ module PactBroker
13
13
  :latest_verification_latest_tags
14
14
 
15
15
  # rubocop:disable Metrics/ParameterLists
16
- def self.create(consumer, provider, latest_pact, latest, latest_verification, webhooks = [], triggered_webhooks = [], tags = [], latest_verification_latest_tags = [])
17
- new(consumer, provider, latest_pact, latest, latest_verification, webhooks, triggered_webhooks, tags, latest_verification_latest_tags)
16
+ def self.create(consumer, provider, latest_pact, latest, latest_verification, webhooks = [], triggered_webhooks = [], tags = [], latest_verification_latest_tags = [], latest_for_branch = nil)
17
+ new(consumer, provider, latest_pact, latest, latest_verification, webhooks, triggered_webhooks, tags, latest_verification_latest_tags, latest_for_branch)
18
18
  end
19
19
  # rubocop:enable Metrics/ParameterLists
20
20
 
21
21
  # rubocop:disable Metrics/ParameterLists
22
- def initialize(consumer, provider, latest_pact = nil, latest = true, latest_verification = nil, webhooks = [], triggered_webhooks = [], tags = [], latest_verification_latest_tags = [])
22
+ def initialize(consumer, provider, latest_pact = nil, latest = true, latest_verification = nil, webhooks = [], triggered_webhooks = [], tags = [], latest_verification_latest_tags = [], latest_for_branch = nil)
23
23
  @consumer = consumer
24
24
  @provider = provider
25
25
  @latest_pact = latest_pact
@@ -29,6 +29,7 @@ module PactBroker
29
29
  @triggered_webhooks = triggered_webhooks
30
30
  @tags = tags
31
31
  @latest_verification_latest_tags = latest_verification_latest_tags
32
+ @latest_for_branch = latest_for_branch
32
33
  end
33
34
  # rubocop:enable Metrics/ParameterLists
34
35
 
@@ -37,7 +38,8 @@ module PactBroker
37
38
  other.latest_pact == latest_pact &&
38
39
  other.latest? == latest? &&
39
40
  other.latest_verification == latest_verification &&
40
- other.webhooks == webhooks
41
+ other.webhooks == webhooks &&
42
+ other.latest_for_branch? == latest_for_branch?
41
43
  end
42
44
 
43
45
  def == other
@@ -68,6 +70,14 @@ module PactBroker
68
70
  @latest_pact.consumer_version
69
71
  end
70
72
 
73
+ def consumer_version_branch
74
+ consumer_version.branch
75
+ end
76
+
77
+ def latest_for_branch?
78
+ @latest_for_branch
79
+ end
80
+
71
81
  def provider_version
72
82
  @latest_verification ? @latest_verification.provider_version : nil
73
83
  end
@@ -76,6 +86,10 @@ module PactBroker
76
86
  @latest_verification ? @latest_verification.provider_version_number : nil
77
87
  end
78
88
 
89
+ def provider_version_branch
90
+ provider_version&.branch
91
+ end
92
+
79
93
  # these are the consumer tag names for which this pact publication
80
94
  # is the latest with that tag
81
95
  def tag_names
@@ -27,6 +27,10 @@ module PactBroker
27
27
  filter = name_like(Sequel[:labels][:name], label_name)
28
28
  join(:labels, {pacticipant_id: :id}).where(filter)
29
29
  end
30
+
31
+ def find_by_name(name)
32
+ where(name_like(:name, name))
33
+ end
30
34
  end
31
35
 
32
36
  def before_destroy
@@ -51,17 +55,17 @@ module PactBroker
51
55
  messages << message('errors.validation.attribute_missing', attribute: 'name') unless name
52
56
  messages
53
57
  end
58
+
59
+ def any_versions?
60
+ PactBroker::Domain::Version.where(pacticipant: self).any?
61
+ end
54
62
  end
55
63
  end
56
64
  end
57
65
 
58
-
59
-
60
-
61
-
62
66
  # Table: pacticipants
63
67
  # Columns:
64
- # id | integer | PRIMARY KEY DEFAULT nextval('pacticipants_id_seq'::regclass)
68
+ # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
65
69
  # name | text |
66
70
  # repository_url | text |
67
71
  # created_at | timestamp without time zone | NOT NULL
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/db'
2
2
  require 'pact_broker/repositories/helpers'
3
+ require 'pact_broker/tags/eager_loaders'
3
4
 
4
5
  module PactBroker
5
6
  module Domain
@@ -10,47 +11,43 @@ module PactBroker
10
11
  unrestrict_primary_key
11
12
  associate(:many_to_one, :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id)
12
13
 
14
+ # The tag for the latest version that has a tag with a matching name
15
+ many_to_one :head_tag, read_only: true, key: [:name, :pacticipant_id],
16
+ class: Tag,
17
+ dataset: lambda {
18
+ Tag.where(name: name, pacticipant_id: pacticipant_id)
19
+ .order(Sequel.desc(:version_order))
20
+ .limit(1)
21
+ },
22
+ eager_loader: PactBroker::Tags::EagerLoaders::HeadTag
23
+
13
24
  dataset_module do
14
25
  include PactBroker::Repositories::Helpers
15
26
 
16
- def latest_tags
17
- tags_versions_join = {
18
- Sequel[:tags][:version_id] => Sequel[:versions][:id],
19
- }
27
+ def join_pact_publications
28
+ join(:pact_publications, { Sequel[:tags][:version_id] => Sequel[:pact_publications][:consumer_version_id] } )
29
+ end
20
30
 
21
- latest_tags_versions_join = {
22
- Sequel[:latest_tags][:name] => Sequel[:tags][:name],
23
- Sequel[:latest_tags][:latest_order] => Sequel[:versions][:order],
24
- Sequel[:latest_tags][:pacticipant_id] => Sequel[:versions][:pacticipant_id],
25
- }
31
+ def for(pacticipant_name, version_number, tag_name)
32
+ where(
33
+ version_id: db[:versions].select(:id).where(
34
+ number: version_number,
35
+ pacticipant_id: db[:pacticipants].select(:id).where(name_like(:name, pacticipant_name))
36
+ ),
37
+ name: tag_name
38
+ ).single_record
39
+ end
26
40
 
27
- latest_tags = PactBroker::Domain::Tag
28
- .select_group(Sequel[:tags][:name], Sequel[:versions][:pacticipant_id])
29
- .select_append{ max(order).as(latest_order) }
30
- .join(:versions, tags_versions_join)
41
+ def latest_tags
42
+ self_join = {
43
+ Sequel[:tags][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id],
44
+ Sequel[:tags][:name] => Sequel[:tags_2][:name]
45
+ }
31
46
 
32
47
  PactBroker::Domain::Tag
33
48
  .select_all_qualified
34
- .join(:versions,
35
- { Sequel[:tags][:version_id] => Sequel[:versions][:id] }
36
- )
37
- .join(latest_tags, latest_tags_versions_join, { table_alias: :latest_tags })
38
- end
39
-
40
- # Ron's fancy join
41
- # performs every so slightly better
42
- def latest_tags_2
43
- tag_versions = PactBroker::Domain::Tag
44
- .select_all_qualified
45
- .select_append(Sequel[:versions][:pacticipant_id])
46
- .select_append(Sequel[:versions][:order])
47
- .join(:versions,
48
- { Sequel[:tags][:version_id] => Sequel[:versions][:id] }
49
- )
50
-
51
- tag_versions
52
- .left_join(tag_versions, { Sequel[:tags][:name] => Sequel[:tags_2][:name], Sequel[:versions][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id] }, { table_alias: :tags_2 }) do | table, joined_table, js |
53
- Sequel.qualify(table, :order) > Sequel.qualify(joined_table, :order)
49
+ .left_join(:tags, self_join, { table_alias: :tags_2 }) do
50
+ Sequel[:tags_2][:version_order] > Sequel[:tags][:version_order]
54
51
  end
55
52
  .where(Sequel[:tags_2][:name] => nil)
56
53
  end
@@ -58,32 +55,40 @@ module PactBroker
58
55
  # Does NOT care about whether or not there is a pact publication
59
56
  # for the version
60
57
  def latest_tags_for_pacticipant_ids(pacticipant_ids)
61
- tags_versions_join = {
62
- Sequel[:tags][:version_id] => Sequel[:versions][:id],
63
- Sequel[:versions][:pacticipant_id] => pacticipant_ids
58
+ self_join = {
59
+ Sequel[:tags][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id],
60
+ Sequel[:tags][:name] => Sequel[:tags_2][:name],
61
+ Sequel[:tags_2][:pacticipant_id] => pacticipant_ids,
64
62
  }
65
63
 
66
- latest_tags_versions_join = {
67
- Sequel[:latest_tags][:name] => Sequel[:tags][:name],
68
- Sequel[:latest_tags][:latest_order] => Sequel[:versions][:order],
69
- Sequel[:latest_tags][:pacticipant_id] => Sequel[:versions][:pacticipant_id],
70
- Sequel[:versions][:pacticipant_id] => pacticipant_ids
71
- }
64
+ Tag
65
+ .select_all_qualified
66
+ .left_join(:tags, self_join, { table_alias: :tags_2 }) do
67
+ Sequel[:tags_2][:version_order] > Sequel[:tags][:version_order]
68
+ end
69
+ .where(Sequel[:tags_2][:name] => nil)
70
+ .where(Sequel[:tags][:pacticipant_id] => pacticipant_ids)
71
+ end
72
72
 
73
- latest_tags = PactBroker::Domain::Tag
74
- .select_group(Sequel[:tags][:name], Sequel[:versions][:pacticipant_id])
75
- .select_append{ max(order).as(latest_order) }
76
- .join(:versions, tags_versions_join)
73
+ def latest_tags_for_pacticipant_ids_and_tag_names(pacticipant_ids, tag_names)
74
+ self_join = {
75
+ Sequel[:tags][:pacticipant_id] => Sequel[:tags_2][:pacticipant_id],
76
+ Sequel[:tags][:name] => Sequel[:tags_2][:name],
77
+ Sequel[:tags_2][:pacticipant_id] => pacticipant_ids,
78
+ Sequel[:tags_2][:name] => tag_names
79
+ }
77
80
 
78
- PactBroker::Domain::Tag
81
+ Tag
79
82
  .select_all_qualified
80
- .join(:versions,
81
- { Sequel[:tags][:version_id] => Sequel[:versions][:id],
82
- Sequel[:versions][:pacticipant_id] => pacticipant_ids
83
- })
84
- .join(latest_tags, latest_tags_versions_join, { table_alias: :latest_tags })
83
+ .left_join(:tags, self_join, { table_alias: :tags_2 }) do
84
+ Sequel[:tags_2][:version_order] > Sequel[:tags][:version_order]
85
+ end
86
+ .where(Sequel[:tags_2][:name] => nil)
87
+ .where(Sequel[:tags][:pacticipant_id] => pacticipant_ids)
88
+ .where(Sequel[:tags][:name] => tag_names)
85
89
  end
86
90
 
91
+ # ignores tags that don't have a pact publication
87
92
  def head_tags_for_consumer_id(consumer_id)
88
93
  lp = :latest_pact_publication_ids_for_consumer_versions
89
94
  tags_versions_join = {
@@ -98,7 +103,7 @@ module PactBroker
98
103
  # head tags for this consumer
99
104
  # the latest tag, pacticipant_id, version order
100
105
  # for versions that have a pact publication
101
- PactBroker::Domain::Tag
106
+ Tag
102
107
  .select_group(Sequel[:tags][:name], Sequel[:versions][:pacticipant_id])
103
108
  .select_append{ max(order).as(latest_consumer_version_order) }
104
109
  .join(:versions, tags_versions_join)
@@ -106,25 +111,74 @@ module PactBroker
106
111
  end
107
112
 
108
113
  def head_tags_for_pact_publication(pact_publication)
109
- head_tags_versions_join = {
110
- Sequel[:head_tags][:latest_consumer_version_order] => Sequel[:versions][:order],
111
- Sequel[:head_tags][:pacticipant_id] => Sequel[:versions][:pacticipant_id],
112
- Sequel[:versions][:pacticipant_id] => pact_publication.consumer_id
113
- }
114
+ Tag.where(version_id: pact_publication.consumer_version_id).all.select do | tag |
115
+ tag_pp_join = {
116
+ Sequel[:pact_publications][:consumer_version_id] => Sequel[:tags][:version_id],
117
+ Sequel[:pact_publications][:consumer_id] => pact_publication.consumer_id,
118
+ Sequel[:pact_publications][:provider_id] => pact_publication.provider_id,
119
+ Sequel[:tags][:name] => tag.name
120
+ }
121
+ Tag.join(:pact_publications, tag_pp_join) do
122
+ Sequel[:tags][:version_order] > tag.version_order
123
+ end
124
+ .where(pacticipant_id: pact_publication.consumer_id)
125
+ .limit(1)
126
+ .empty?
127
+ end
128
+ end
129
+ end
114
130
 
115
- # Find the head tags that belong to this pact publication
116
- # Note: The tag model has the name and version_id,
117
- # but does not have the created_at value set - but don't need it for now
118
- head_tags_for_consumer_id(pact_publication.consumer_id).from_self(alias: :head_tags)
119
- .select(Sequel[:head_tags][:name], Sequel[:versions][:id].as(:version_id))
120
- .join(:versions, head_tags_versions_join)
121
- .where(Sequel[:versions][:id] => pact_publication.consumer_version_id)
131
+ def before_save
132
+ if version
133
+ if version.order && self.version_order.nil?
134
+ self.version_order = version.order
135
+ end
136
+
137
+ if self.pacticipant_id.nil?
138
+ if version.pacticipant_id
139
+ self.pacticipant_id = version.pacticipant_id
140
+ elsif version&.pacticipant&.id
141
+ self.pacticipant_id = version.pacticipant.id
142
+ end
143
+ end
144
+ end
145
+
146
+ if version_order.nil? || pacticipant_id.nil?
147
+ raise PactBroker::Error.new("Need to set version_order and pacticipant_id for tags now")
148
+ else
149
+ super
150
+ end
151
+ end
152
+
153
+ def latest_for_pacticipant?
154
+ head_tag == self
155
+ end
156
+
157
+ alias_method :latest?, :latest_for_pacticipant?
158
+
159
+ def latest_for_pact_publication?(pact_publication)
160
+ tag_pp_join = {
161
+ Sequel[:pact_publications][:consumer_version_id] => Sequel[:tags][:version_id],
162
+ Sequel[:pact_publications][:consumer_id] => pact_publication.consumer_id,
163
+ Sequel[:pact_publications][:provider_id] => pact_publication.provider_id,
164
+ Sequel[:tags][:name] => name
165
+ }
166
+ own_version_order = self.version_order
167
+ Tag.join(:pact_publications, tag_pp_join) do
168
+ Sequel[:tags][:version_order] > own_version_order
122
169
  end
170
+ .where(pacticipant_id: pact_publication.consumer_id)
171
+ .limit(1)
172
+ .empty?
123
173
  end
124
174
 
125
175
  def <=> other
126
176
  name <=> other.name
127
177
  end
178
+
179
+ def pacticipant
180
+ version.pacticipant
181
+ end
128
182
  end
129
183
  end
130
184
  end
@@ -132,12 +186,18 @@ end
132
186
  # Table: tags
133
187
  # Primary Key: (name, version_id)
134
188
  # Columns:
135
- # name | text |
136
- # version_id | integer |
137
- # created_at | timestamp without time zone | NOT NULL
138
- # updated_at | timestamp without time zone | NOT NULL
189
+ # name | text |
190
+ # version_id | integer |
191
+ # created_at | timestamp without time zone | NOT NULL
192
+ # updated_at | timestamp without time zone | NOT NULL
193
+ # pacticipant_id | integer |
194
+ # version_order | integer |
139
195
  # Indexes:
140
- # tags_pk | PRIMARY KEY btree (version_id, name)
141
- # ndx_tag_name | btree (name)
196
+ # tags_pk | PRIMARY KEY btree (version_id, name)
197
+ # ndx_tag_name | btree (name)
198
+ # tags_pacticipant_id_index | btree (pacticipant_id)
199
+ # tags_pacticipant_id_name_version_order_desc_index | btree (pacticipant_id, name, version_order DESC)
200
+ # tags_version_id_index | btree (version_id)
201
+ # tags_version_order_index | btree (version_order)
142
202
  # Foreign key constraints:
143
203
  # tags_version_id_fkey | (version_id) REFERENCES versions(id)