pact_broker 2.85.0 → 2.88.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/trigger_pact_docs_update.yml +22 -0
  3. data/CHANGELOG.md +67 -0
  4. data/DEVELOPER_DOCUMENTATION.md +0 -2
  5. data/db/migrations/20210914_add_labels_to_webhooks.rb +14 -0
  6. data/db/migrations/20210915_add_verified_by_to_verification.rb +6 -0
  7. data/db/migrations/20210929_increase_event_context_column_size.rb +14 -0
  8. data/docker-compose-ci-mysql.yml +1 -0
  9. data/docker-compose-test.yml +2 -0
  10. data/docs/CONFIGURATION.md +255 -66
  11. data/docs/api/WEBHOOKS.md +789 -0
  12. data/docs/configuration.yml +166 -101
  13. data/lib/db.rb +0 -1
  14. data/lib/pact/doc/interaction_view_model.rb +2 -2
  15. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +1 -1
  16. data/lib/pact_broker/api/contracts/configuration.rb +33 -0
  17. data/lib/pact_broker/api/contracts/publish_contracts_schema.rb +35 -16
  18. data/lib/pact_broker/api/contracts/webhook_contract.rb +24 -2
  19. data/lib/pact_broker/api/decorators/matrix_decorator.rb +3 -1
  20. data/lib/pact_broker/api/decorators/pact_decorator.rb +12 -0
  21. data/lib/pact_broker/api/decorators/verification_decorator.rb +9 -3
  22. data/lib/pact_broker/api/decorators/webhook_decorator.rb +27 -4
  23. data/lib/pact_broker/api/pact_broker_urls.rb +30 -6
  24. data/lib/pact_broker/api/resources/all_webhooks.rb +2 -2
  25. data/lib/pact_broker/api/resources/default_base_resource.rb +5 -1
  26. data/lib/pact_broker/api/resources/environment.rb +4 -4
  27. data/lib/pact_broker/api/resources/environments.rb +1 -1
  28. data/lib/pact_broker/api/resources/index.rb +7 -1
  29. data/lib/pact_broker/api/resources/metadata_resource_methods.rb +33 -3
  30. data/lib/pact_broker/api/resources/pact_version.rb +4 -0
  31. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +1 -1
  32. data/lib/pact_broker/api/resources/publish_contracts.rb +1 -1
  33. data/lib/pact_broker/api/resources/verification.rb +5 -2
  34. data/lib/pact_broker/api/resources/webhook_execution.rb +7 -3
  35. data/lib/pact_broker/api/resources/webhook_execution_methods.rb +23 -17
  36. data/lib/pact_broker/api.rb +6 -0
  37. data/lib/pact_broker/application_context.rb +5 -0
  38. data/lib/pact_broker/config/runtime_configuration.rb +4 -0
  39. data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
  40. data/lib/pact_broker/db/clean.rb +1 -2
  41. data/lib/pact_broker/db/delete_overwritten_data.rb +41 -23
  42. data/lib/pact_broker/deployments/deployed_version.rb +1 -0
  43. data/lib/pact_broker/deployments/deployed_version_service.rb +1 -0
  44. data/lib/pact_broker/deployments/environment_service.rb +7 -2
  45. data/lib/pact_broker/deployments/released_version_service.rb +1 -0
  46. data/lib/pact_broker/doc/controllers/app.rb +1 -0
  47. data/lib/pact_broker/doc/views/can-i-deploy.markdown +2 -1
  48. data/lib/pact_broker/doc/views/index/publish-contracts.markdown +38 -9
  49. data/lib/pact_broker/doc/views/pacticipant/label.markdown +12 -0
  50. data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +7 -7
  51. data/lib/pact_broker/doc/views/webhooks.markdown +17 -0
  52. data/lib/pact_broker/domain/index_item.rb +9 -0
  53. data/lib/pact_broker/domain/pacticipant.rb +4 -0
  54. data/lib/pact_broker/domain/verification.rb +19 -4
  55. data/lib/pact_broker/domain/webhook.rb +5 -5
  56. data/lib/pact_broker/domain/webhook_pacticipant.rb +6 -0
  57. data/lib/pact_broker/index/service.rb +4 -4
  58. data/lib/pact_broker/locale/en.yml +4 -1
  59. data/lib/pact_broker/matrix/head_row.rb +1 -1
  60. data/lib/pact_broker/matrix/parse_can_i_deploy_query.rb +5 -3
  61. data/lib/pact_broker/matrix/quick_row.rb +0 -1
  62. data/lib/pact_broker/matrix/repository.rb +0 -1
  63. data/lib/pact_broker/matrix/row.rb +2 -2
  64. data/lib/pact_broker/pacticipants/repository.rb +1 -1
  65. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +0 -1
  66. data/lib/pact_broker/pacts/metadata.rb +7 -1
  67. data/lib/pact_broker/pacts/pact_publication.rb +7 -0
  68. data/lib/pact_broker/pacts/pact_publication_clean_selector_dataset_module.rb +19 -0
  69. data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +30 -2
  70. data/lib/pact_broker/pacts/pact_version.rb +24 -1
  71. data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +5 -4
  72. data/lib/pact_broker/pacts/repository.rb +50 -47
  73. data/lib/pact_broker/pacts/service.rb +5 -5
  74. data/lib/pact_broker/string_refinements.rb +1 -1
  75. data/lib/pact_broker/test/http_test_data_builder.rb +40 -10
  76. data/lib/pact_broker/test/test_data_builder.rb +28 -5
  77. data/lib/pact_broker/ui/helpers/url_helper.rb +12 -0
  78. data/lib/pact_broker/ui/view_models/index_item.rb +15 -1
  79. data/lib/pact_broker/ui/view_models/index_item_branch_head.rb +39 -0
  80. data/lib/pact_broker/ui/view_models/index_item_provider_branch_head.rb +39 -0
  81. data/lib/pact_broker/ui/views/dashboard/show.haml +14 -7
  82. data/lib/pact_broker/verifications/repository.rb +5 -2
  83. data/lib/pact_broker/verifications/service.rb +7 -4
  84. data/lib/pact_broker/version.rb +1 -1
  85. data/lib/pact_broker/versions/abbreviate_number.rb +8 -4
  86. data/lib/pact_broker/versions/branch_head.rb +0 -2
  87. data/lib/pact_broker/versions/branch_version.rb +1 -0
  88. data/lib/pact_broker/versions/repository.rb +0 -1
  89. data/lib/pact_broker/webhooks/event_listener.rb +4 -2
  90. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +18 -3
  91. data/lib/pact_broker/webhooks/repository.rb +10 -4
  92. data/lib/pact_broker/webhooks/trigger_service.rb +1 -1
  93. data/lib/pact_broker/webhooks/webhook.rb +71 -8
  94. data/lib/webmachine/describe_routes.rb +62 -0
  95. data/public/stylesheets/index.css +5 -0
  96. data/script/data/auto-create-things-for-tags.rb +1 -0
  97. data/script/data/branches.rb +1 -1
  98. data/script/data/contract-published-requiring-verification.rb +0 -1
  99. data/script/data/verify-pact-for-multiple-selectors.rb +30 -0
  100. data/script/docs/generate-api-docs.rb +117 -0
  101. data/script/docs/generate-configuration-docs.rb +24 -3
  102. data/script/docs/regenerate-api-docs.sh +11 -0
  103. data/script/generate-erd +55 -0
  104. data/spec/features/create_webhook_spec.rb +55 -10
  105. data/spec/features/get_pact_spec.rb +2 -3
  106. data/spec/fixtures/approvals/docs_webhooks_executing_a_saved_webhook_options.approved.json +20 -0
  107. data/spec/fixtures/approvals/docs_webhooks_executing_a_saved_webhook_post.approved.json +43 -0
  108. data/spec/fixtures/approvals/docs_webhooks_executing_an_unsaved_webhook_options.approved.json +20 -0
  109. data/spec/fixtures/approvals/docs_webhooks_executing_an_unsaved_webhook_post.approved.json +63 -0
  110. data/spec/fixtures/approvals/docs_webhooks_logs_of_triggered_webhook_get.approved.json +20 -0
  111. data/spec/fixtures/approvals/docs_webhooks_logs_of_triggered_webhook_options.approved.json +20 -0
  112. data/spec/fixtures/approvals/docs_webhooks_pact_webhooks_get.approved.json +45 -0
  113. data/spec/fixtures/approvals/docs_webhooks_pact_webhooks_options.approved.json +20 -0
  114. data/spec/fixtures/approvals/docs_webhooks_triggered_webhooks_for_pact_publication_get.approved.json +52 -0
  115. data/spec/fixtures/approvals/docs_webhooks_triggered_webhooks_for_pact_publication_options.approved.json +20 -0
  116. data/spec/fixtures/approvals/docs_webhooks_triggered_webhooks_for_verification_publication_get.approved.json +32 -0
  117. data/spec/fixtures/approvals/docs_webhooks_triggered_webhooks_for_verification_publication_options.approved.json +20 -0
  118. data/spec/fixtures/approvals/docs_webhooks_webhook_get.approved.json +74 -0
  119. data/spec/fixtures/approvals/docs_webhooks_webhook_options.approved.json +20 -0
  120. data/spec/fixtures/approvals/docs_webhooks_webhook_put.approved.json +77 -0
  121. data/spec/fixtures/approvals/docs_webhooks_webhooks_for_a_provider_get.approved.json +41 -0
  122. data/spec/fixtures/approvals/docs_webhooks_webhooks_for_a_provider_options.approved.json +20 -0
  123. data/spec/fixtures/approvals/docs_webhooks_webhooks_for_consumer_and_provider_get.approved.json +45 -0
  124. data/spec/fixtures/approvals/docs_webhooks_webhooks_for_consumer_and_provider_options.approved.json +20 -0
  125. data/spec/fixtures/approvals/docs_webhooks_webhooks_for_consumer_get.approved.json +41 -0
  126. data/spec/fixtures/approvals/docs_webhooks_webhooks_for_consumer_options.approved.json +20 -0
  127. data/spec/fixtures/approvals/docs_webhooks_webhooks_get.approved.json +45 -0
  128. data/spec/fixtures/approvals/docs_webhooks_webhooks_options.approved.json +20 -0
  129. data/spec/fixtures/approvals/docs_webhooks_webhooks_post.approved.json +78 -0
  130. data/spec/fixtures/approvals/docs_webhooks_webhooks_status_get.approved.json +79 -0
  131. data/spec/fixtures/approvals/docs_webhooks_webhooks_status_options.approved.json +20 -0
  132. data/spec/fixtures/approvals/get_provider_pacts_for_verification.approved.json +1 -2
  133. data/spec/fixtures/approvals/publish_contract_no_branch.approved.json +1 -2
  134. data/spec/fixtures/approvals/publish_contract_nothing_exists.approved.json +1 -2
  135. data/spec/fixtures/approvals/publish_contract_nothing_exists_with_webhook.approved.json +1 -2
  136. data/spec/fixtures/approvals/publish_contract_verification_already_exists.approved.json +1 -2
  137. data/spec/fixtures/approvals/publish_contract_with_validation_error.approved.json +1 -2
  138. data/spec/fixtures/invalid-publish-contract-body.json +38 -0
  139. data/spec/fixtures/verification.json +4 -0
  140. data/spec/integration/pact_metdata_spec.rb +105 -0
  141. data/spec/integration/webhooks/contract_publication_spec.rb +68 -0
  142. data/spec/integration/webhooks/contract_requiring_verification_published_spec.rb +67 -0
  143. data/spec/integration/webhooks/pact_publication_spec.rb +51 -0
  144. data/spec/integration/webhooks_documentation_spec.rb +348 -0
  145. data/spec/lib/pact/doc/markdown/consumer_contract_renderer_spec.rb +2 -2
  146. data/spec/lib/pact_broker/api/contracts/publish_contracts_schema_spec.rb +13 -0
  147. data/spec/lib/pact_broker/api/contracts/webhook_contract_spec.rb +50 -0
  148. data/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb +1 -1
  149. data/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +15 -7
  150. data/spec/lib/pact_broker/api/decorators/verification_summary_decorator_spec.rb +4 -2
  151. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +4 -4
  152. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +18 -0
  153. data/spec/lib/pact_broker/api/resources/triggered_webhook_logs_spec.rb +6 -5
  154. data/spec/lib/pact_broker/config/runtime_configuration_documentation_spec.rb +30 -0
  155. data/spec/lib/pact_broker/deployments/environment_service_spec.rb +22 -1
  156. data/spec/lib/pact_broker/domain/webhook_spec.rb +35 -0
  157. data/spec/lib/pact_broker/matrix/head_row_spec.rb +9 -5
  158. data/spec/lib/pact_broker/matrix/parse_can_i_deploy_query_spec.rb +13 -0
  159. data/spec/lib/pact_broker/pacts/{latest_tagged_pact_publications_spec.rb → pact_publication_clean_selector_dataset_module_spec.rb} +7 -9
  160. data/spec/lib/pact_broker/pacts/pact_version_spec.rb +32 -0
  161. data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +4 -5
  162. data/spec/lib/pact_broker/pacts/repository_spec.rb +33 -0
  163. data/spec/lib/pact_broker/ui/view_models/index_item_spec.rb +1 -1
  164. data/spec/lib/pact_broker/verifications/service_spec.rb +22 -8
  165. data/spec/lib/pact_broker/versions/abbreviate_number_spec.rb +2 -1
  166. data/spec/lib/pact_broker/webhooks/render_spec.rb +3 -2
  167. data/spec/lib/pact_broker/webhooks/repository_spec.rb +158 -15
  168. data/spec/lib/pact_broker/webhooks/webhook_spec.rb +8 -5
  169. data/spec/support/documentation.rb +64 -0
  170. data/spec/support/rack_helpers.rb +1 -1
  171. data/tasks/db.rake +4 -1
  172. data/tasks/development.rake +14 -13
  173. metadata +89 -12
  174. data/lib/pact_broker/pacts/all_pact_publications.rb +0 -158
  175. data/lib/pact_broker/pacts/latest_pact_publications.rb +0 -48
  176. data/lib/pact_broker/pacts/latest_pact_publications_by_consumer_version.rb +0 -26
  177. data/lib/pact_broker/pacts/latest_tagged_pact_publications.rb +0 -45
  178. data/lib/pact_broker/verifications/latest_verification_for_pact_version.rb +0 -39
  179. data/spec/lib/pact_broker/verifications/latest_verification_for_pact_version_spec.rb +0 -18
@@ -1,6 +1,5 @@
1
1
  require "sequel"
2
2
  require "pact_broker/domain/verification"
3
- require "pact_broker/verifications/latest_verification_for_pact_version"
4
3
  require "pact_broker/verifications/sequence"
5
4
  require "pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version"
6
5
 
@@ -61,6 +60,10 @@ module PactBroker
61
60
  PactBroker::Pacts::PactPublication.where(id: pact.id).single_record.latest_verification
62
61
  end
63
62
 
63
+ def find_latest_from_main_branch_for_pact(pact)
64
+ PactBroker::Pacts::PactPublication.where(id: pact.id).single_record.latest_main_branch_verification
65
+ end
66
+
64
67
  def any_verifications?(consumer, provider)
65
68
  PactBroker::Domain::Verification.where(consumer_id: consumer.id, provider_id: provider.id).any?
66
69
  end
@@ -73,7 +76,7 @@ module PactBroker
73
76
  end
74
77
 
75
78
  def find_latest_verifications_for_consumer_version consumer_name, consumer_version_number
76
- # Use LatestPactPublicationsByConsumerVersion not AllPactPublcations because we don't
79
+ # Use remove_verifications_for_overridden_consumer_versions because we don't
77
80
  # want verifications for shadowed revisions as it would be misleading.
78
81
  PactBroker::Domain::Verification
79
82
  .select_all_qualified
@@ -21,12 +21,13 @@ module PactBroker
21
21
  using PactBroker::HashRefinements
22
22
  extend PactBroker::Events::Publisher
23
23
 
24
- delegate [:any_verifications?] => :verification_repository
24
+ delegate [:any_verifications?, :find_latest_from_main_branch_for_pact] => :verification_repository
25
25
 
26
26
  def next_number
27
27
  verification_repository.next_number
28
28
  end
29
29
 
30
+ # TODO use a decorator instead of passing in params, srsly, Beth
30
31
  # verified_pacts is an array of SelectedPact objects
31
32
  def create next_verification_number, params, verified_pacts, event_context
32
33
  first_verified_pact = verified_pacts.first
@@ -37,6 +38,8 @@ module PactBroker
37
38
  verification.wip = params.fetch("wip")
38
39
  verification.pact_pending = params.fetch("pending")
39
40
  verification.number = next_verification_number
41
+ verification.verified_by_implementation = params.dig("verifiedBy", "implementation")
42
+ verification.verified_by_version = params.dig("verifiedBy", "version")
40
43
  verification.consumer_version_selector_hashes = event_context[:consumer_version_selectors]
41
44
  pact_version = pact_repository.find_pact_version(first_verified_pact.consumer, first_verified_pact.provider, first_verified_pact.pact_version_sha)
42
45
  verification = verification_repository.create(verification, provider_version_number, pact_version)
@@ -135,7 +138,7 @@ module PactBroker
135
138
  latest_version_from_main_branch = [version_service.find_latest_version_from_main_branch(pact_version.provider)].compact
136
139
 
137
140
  latest_version_from_main_branch.collect do | main_branch_version |
138
- identify_required_verification(pact_version, main_branch_version, "latest version from main branch")
141
+ identify_required_verification(pact_version, main_branch_version, "latest from main branch")
139
142
  end.compact
140
143
  end
141
144
  private :required_verifications_for_main_branch
@@ -145,7 +148,7 @@ module PactBroker
145
148
  unscoped_service.find_currently_deployed_versions_for_pacticipant(pact_version.provider)
146
149
  end
147
150
  deployed_versions.collect do | deployed_version |
148
- identify_required_verification(pact_version, deployed_version.version, "currently deployed version (#{deployed_version.environment_name})")
151
+ identify_required_verification(pact_version, deployed_version.version, "deployed in #{deployed_version.environment_name}")
149
152
  end.compact
150
153
  end
151
154
  private :required_verifications_for_deployed_versions
@@ -155,7 +158,7 @@ module PactBroker
155
158
  unscoped_service.find_currently_supported_versions_for_pacticipant(pact_version.provider)
156
159
  end
157
160
  released_versions.collect do | released_version |
158
- identify_required_verification(pact_version, released_version.version, "currently released version (#{released_version.environment_name})")
161
+ identify_required_verification(pact_version, released_version.version, "released in #{released_version.environment_name}")
159
162
  end.compact
160
163
  end
161
164
  private :required_verifications_for_released_versions
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = "2.85.0"
2
+ VERSION = "2.88.0"
3
3
  end
@@ -3,10 +3,14 @@ module PactBroker
3
3
  class AbbreviateNumber
4
4
 
5
5
  def self.call version_number
6
- if version_number
7
- version_number.gsub(/[A-Za-z0-9]{40}/) do | val |
8
- val[0..6]
9
- end
6
+ return version_number unless version_number
7
+
8
+ # hard limit of max 50 characters
9
+ version_length = version_number.length
10
+ return version_number[0...39] + "…" + version_number[version_length - 10...version_length] if version_length > 50
11
+
12
+ version_number.gsub(/[A-Za-z0-9]{40}/) do | val |
13
+ val[0..6]
10
14
  end
11
15
  end
12
16
  end
@@ -28,14 +28,12 @@ end
28
28
 
29
29
  # Table: branch_heads
30
30
  # Columns:
31
- # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
32
31
  # branch_id | integer | NOT NULL
33
32
  # branch_version_id | integer | NOT NULL
34
33
  # version_id | integer | NOT NULL
35
34
  # pacticipant_id | integer | NOT NULL
36
35
  # branch_name | text | NOT NULL
37
36
  # Indexes:
38
- # branch_heads_pkey | PRIMARY KEY btree (id)
39
37
  # branch_heads_branch_id_index | UNIQUE btree (branch_id)
40
38
  # branch_heads_branch_name_index | btree (branch_name)
41
39
  # branch_heads_pacticipant_id_index | btree (pacticipant_id)
@@ -51,6 +51,7 @@ end
51
51
  # branch_name | text | NOT NULL
52
52
  # created_at | timestamp without time zone | NOT NULL
53
53
  # updated_at | timestamp without time zone | NOT NULL
54
+ # auto_created | boolean | DEFAULT false
54
55
  # Indexes:
55
56
  # branch_versions_pkey | PRIMARY KEY btree (id)
56
57
  # branch_versions_branch_id_version_id_index | UNIQUE btree (branch_id, version_id)
@@ -53,7 +53,6 @@ module PactBroker
53
53
 
54
54
  # There may be a race condition if two simultaneous requests come in to create the same version
55
55
  def create(args)
56
- logger.info "Upserting version #{args[:number]} for pacticipant_id=#{args[:pacticipant_id]}"
57
56
  version_params = {
58
57
  number: args[:number],
59
58
  pacticipant_id: args[:pacticipant_id],
@@ -18,14 +18,16 @@ module PactBroker
18
18
  end
19
19
 
20
20
  def contract_published(params)
21
- handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED, params)
21
+ main_branch_verification = verification_service.find_latest_from_main_branch_for_pact(params.fetch(:pact))
22
+ handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_PUBLISHED, { verification: main_branch_verification }.compact.merge(params))
22
23
  if verification_service.calculate_required_verifications_for_pact(params.fetch(:pact)).any?
23
24
  handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_REQUIRING_VERIFICATION_PUBLISHED, params)
24
25
  end
25
26
  end
26
27
 
27
28
  def contract_content_changed(params)
28
- handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED, params)
29
+ main_branch_verification = verification_service.find_latest_from_main_branch_for_pact(params.fetch(:pact))
30
+ handle_event_for_webhook(PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED, { verification: main_branch_verification }.compact.merge(params))
29
31
  end
30
32
 
31
33
  def contract_content_unchanged(params)
@@ -7,6 +7,7 @@ module PactBroker
7
7
  PROVIDER_VERSION_NUMBER = "pactbroker.providerVersionNumber"
8
8
  PROVIDER_VERSION_TAGS = "pactbroker.providerVersionTags"
9
9
  PROVIDER_VERSION_BRANCH = "pactbroker.providerVersionBranch"
10
+ PROVIDER_VERSION_DESCRIPTIONS = "pactbroker.providerVersionDescriptions"
10
11
  CONSUMER_VERSION_TAGS = "pactbroker.consumerVersionTags"
11
12
  CONSUMER_VERSION_BRANCH = "pactbroker.consumerVersionBranch"
12
13
  CONSUMER_NAME = "pactbroker.consumerName"
@@ -17,6 +18,7 @@ module PactBroker
17
18
  GITLAB_VERIFICATION_STATUS = "pactbroker.gitlabVerificationStatus"
18
19
  CONSUMER_LABELS = "pactbroker.consumerLabels"
19
20
  PROVIDER_LABELS = "pactbroker.providerLabels"
21
+ BUILD_URL = "pactbroker.buildUrl"
20
22
  EVENT_NAME = "pactbroker.eventName"
21
23
  CURRENTLY_DEPLOYED_PROVIDER_VERSION_NUMBER = "pactbroker.currentlyDeployedProviderVersionNumber"
22
24
 
@@ -26,6 +28,7 @@ module PactBroker
26
28
  CONSUMER_VERSION_NUMBER,
27
29
  PROVIDER_VERSION_NUMBER,
28
30
  PROVIDER_VERSION_TAGS,
31
+ PROVIDER_VERSION_DESCRIPTIONS,
29
32
  PROVIDER_VERSION_BRANCH,
30
33
  CONSUMER_VERSION_TAGS,
31
34
  CONSUMER_VERSION_BRANCH,
@@ -38,9 +41,11 @@ module PactBroker
38
41
  CONSUMER_LABELS,
39
42
  PROVIDER_LABELS,
40
43
  EVENT_NAME,
44
+ BUILD_URL,
41
45
  CURRENTLY_DEPLOYED_PROVIDER_VERSION_NUMBER
42
46
  ]
43
47
 
48
+ # TODO change this verification to the latest main branch
44
49
  def initialize(pact, trigger_verification, webhook_context)
45
50
  @pact = pact
46
51
  @verification = trigger_verification || (pact && pact.latest_verification)
@@ -56,6 +61,7 @@ module PactBroker
56
61
  PROVIDER_VERSION_NUMBER => provider_version_number,
57
62
  PROVIDER_VERSION_TAGS => provider_version_tags,
58
63
  PROVIDER_VERSION_BRANCH => provider_version_branch,
64
+ PROVIDER_VERSION_DESCRIPTIONS => provider_version_descriptions,
59
65
  CONSUMER_VERSION_TAGS => consumer_version_tags,
60
66
  CONSUMER_VERSION_BRANCH => consumer_version_branch,
61
67
  CONSUMER_NAME => pact ? pact.consumer_name : "",
@@ -67,6 +73,7 @@ module PactBroker
67
73
  CONSUMER_LABELS => pacticipant_labels(pact && pact.consumer),
68
74
  PROVIDER_LABELS => pacticipant_labels(pact && pact.provider),
69
75
  EVENT_NAME => event_name,
76
+ BUILD_URL => build_url,
70
77
  CURRENTLY_DEPLOYED_PROVIDER_VERSION_NUMBER => currently_deployed_provider_version_number
71
78
  }
72
79
  end
@@ -136,10 +143,10 @@ module PactBroker
136
143
  end
137
144
 
138
145
  def consumer_version_branch
139
- if webhook_context[:consumer_version_branch]
140
- webhook_context[:consumer_version_branch]
146
+ if webhook_context.key?(:consumer_version_branch)
147
+ webhook_context[:consumer_version_branch] || ""
141
148
  else
142
- pact&.consumer_version&.branch || ""
149
+ pact&.consumer_version&.branch_names&.last || ""
143
150
  end
144
151
  end
145
152
 
@@ -171,6 +178,10 @@ module PactBroker
171
178
  end
172
179
  end
173
180
 
181
+ def provider_version_descriptions
182
+ webhook_context[:provider_version_descriptions]&.join(", ") || ""
183
+ end
184
+
174
185
  def pacticipant_labels pacticipant
175
186
  pacticipant && pacticipant.labels ? pacticipant.labels.collect(&:name).join(", ") : ""
176
187
  end
@@ -179,6 +190,10 @@ module PactBroker
179
190
  webhook_context.fetch(:event_name)
180
191
  end
181
192
 
193
+ def build_url
194
+ webhook_context[:build_url] || ""
195
+ end
196
+
182
197
  def currently_deployed_provider_version_number
183
198
  webhook_context[:currently_deployed_provider_version_number] || ""
184
199
  end
@@ -17,8 +17,8 @@ module PactBroker
17
17
  include Repositories
18
18
 
19
19
  def create uuid, webhook, consumer, provider
20
- consumer = pacticipant_repository.find_by_name(webhook.consumer.name) if webhook.consumer
21
- provider = pacticipant_repository.find_by_name(webhook.provider.name) if webhook.provider
20
+ consumer = find_pacticipant_by_name(webhook.consumer) || consumer
21
+ provider = find_pacticipant_by_name(webhook.provider) || provider
22
22
  db_webhook = Webhook.from_domain webhook, consumer, provider
23
23
  db_webhook.uuid = uuid
24
24
  db_webhook.save
@@ -36,8 +36,8 @@ module PactBroker
36
36
  # policy applied at resource level
37
37
  def update_by_uuid uuid, webhook
38
38
  existing_webhook = deliberately_unscoped(Webhook).find(uuid: uuid)
39
- existing_webhook.consumer_id = webhook.consumer ? pacticipant_repository.find_by_name(webhook.consumer.name).id : nil
40
- existing_webhook.provider_id = webhook.provider ? pacticipant_repository.find_by_name(webhook.provider.name).id : nil
39
+ existing_webhook.consumer_id = find_pacticipant_by_name(webhook.consumer)&.id
40
+ existing_webhook.provider_id = find_pacticipant_by_name(webhook.provider)&.id
41
41
  existing_webhook.update_from_domain(webhook).save
42
42
  existing_webhook.events.collect(&:delete)
43
43
  (webhook.events || []).each do | webhook_event |
@@ -187,6 +187,12 @@ module PactBroker
187
187
 
188
188
  private
189
189
 
190
+ def find_pacticipant_by_name(pacticipant)
191
+ return unless pacticipant&.name
192
+
193
+ pacticipant_repository.find_by_name(pacticipant.name)
194
+ end
195
+
190
196
  def deliberately_unscoped(scope)
191
197
  scope
192
198
  end
@@ -135,7 +135,7 @@ module PactBroker
135
135
  required_verifications = verification_service.calculate_required_verifications_for_pact(pact)
136
136
  event_contexts.flat_map do | event_context |
137
137
  required_verifications.collect do | required_verification |
138
- event_context.merge(provider_version_number: required_verification.provider_version.number, provider_version_descriptions: required_verification.provider_version_descriptions)
138
+ event_context.merge(provider_version_number: required_verification.provider_version.number, provider_version_descriptions: required_verification.provider_version_descriptions.uniq)
139
139
  end
140
140
  end
141
141
  else
@@ -30,12 +30,14 @@ module PactBroker
30
30
  end
31
31
 
32
32
  def find_by_consumer_and_or_provider consumer, provider
33
+
33
34
  where(
34
35
  Sequel.|(
35
36
  { consumer_id: consumer.id, provider_id: provider.id },
36
- { consumer_id: nil, provider_id: provider.id },
37
- { consumer_id: consumer.id, provider_id: nil },
38
- { consumer_id: nil, provider_id: nil}
37
+ { consumer_id: nil, provider_id: provider.id, consumer_label: nil },
38
+ { consumer_id: consumer.id, provider_id: nil, provider_label: nil },
39
+ { consumer_id: nil, provider_id: nil, consumer_label: nil, provider_label: nil },
40
+ *labels_criteria_for_consumer_or_provider(consumer, provider)
39
41
  )
40
42
  )
41
43
  end
@@ -51,6 +53,32 @@ module PactBroker
51
53
  def enabled
52
54
  where(enabled: true)
53
55
  end
56
+
57
+ private
58
+
59
+ def labels_criteria_for_consumer_or_provider(consumer, provider)
60
+ consumer_labels = consumer.labels.map(&:name)
61
+ provider_labels = provider.labels.map(&:name)
62
+
63
+ [].then do |criteria|
64
+ next criteria if consumer_labels.empty?
65
+ criteria + [
66
+ { consumer_label: consumer_labels, provider_label: nil, provider_id: nil },
67
+ { consumer_label: consumer_labels, provider_label: nil, provider_id: provider.id }
68
+ ]
69
+ end.then do |criteria|
70
+ next criteria if provider_labels.empty?
71
+ criteria + [
72
+ { provider_label: provider_labels, consumer_label: nil, consumer_id: nil },
73
+ { provider_label: provider_labels, consumer_label: nil, consumer_id: consumer.id }
74
+ ]
75
+ end.then do |criteria|
76
+ next criteria if consumer_labels.empty? || provider_labels.empty?
77
+ criteria + [
78
+ { consumer_label: consumer_labels, provider_label: provider_labels }
79
+ ]
80
+ end
81
+ end
54
82
  end
55
83
 
56
84
  def update_from_domain webhook
@@ -74,8 +102,8 @@ module PactBroker
74
102
  Domain::Webhook.new(
75
103
  uuid: uuid,
76
104
  description: description,
77
- consumer: consumer,
78
- provider: provider,
105
+ consumer: webhook_consumer,
106
+ provider: webhook_provider,
79
107
  events: events,
80
108
  request: Webhooks::WebhookRequestTemplate.new(request_attributes),
81
109
  enabled: enabled,
@@ -100,7 +128,15 @@ module PactBroker
100
128
  end
101
129
 
102
130
  def is_for? integration
103
- (consumer_id == integration.consumer_id || !consumer_id) && (provider_id == integration.provider_id || !provider_id)
131
+ (
132
+ consumer_id == integration.consumer_id ||
133
+ match_label?(:consumer, integration) ||
134
+ match_all?(:consumer)
135
+ ) && (
136
+ provider_id == integration.provider_id ||
137
+ match_label?(:provider, integration) ||
138
+ match_all?(:provider)
139
+ )
104
140
  end
105
141
 
106
142
  # Keep the triggered webhooks after the webhook has been deleted
@@ -110,7 +146,6 @@ module PactBroker
110
146
  super
111
147
  end
112
148
 
113
-
114
149
  def self.properties_hash_from_domain webhook
115
150
  is_json_request_body = !(String === webhook.request.body || webhook.request.body.nil?) # Can't rely on people to set content type
116
151
  {
@@ -122,9 +157,32 @@ module PactBroker
122
157
  enabled: webhook.enabled.nil? ? true : webhook.enabled,
123
158
  body: (is_json_request_body ? webhook.request.body.to_json : webhook.request.body),
124
159
  is_json_request_body: is_json_request_body,
125
- headers: webhook.request.headers
160
+ headers: webhook.request.headers,
161
+ consumer_label: webhook.consumer&.label,
162
+ provider_label: webhook.provider&.label
126
163
  }
127
164
  end
165
+
166
+ def webhook_consumer
167
+ return if consumer.nil? && consumer_label.nil?
168
+
169
+ Domain::WebhookPacticipant.new(name: consumer&.name, label: consumer_label)
170
+ end
171
+
172
+ def webhook_provider
173
+ return if provider.nil? && provider_label.nil?
174
+
175
+ Domain::WebhookPacticipant.new(name: provider&.name, label: provider_label)
176
+ end
177
+
178
+ def match_all?(name)
179
+ public_send(:"#{name}_id").nil? && public_send(:"#{name}_label").nil?
180
+ end
181
+
182
+ def match_label?(name, integration)
183
+ label = public_send(:"#{name}_label")
184
+ public_send(:"#{name}_id").nil? && integration.public_send(name).label?(label)
185
+ end
128
186
  end
129
187
  end
130
188
  end
@@ -146,9 +204,14 @@ end
146
204
  # enabled | boolean | DEFAULT true
147
205
  # description | text |
148
206
  # headers | text |
207
+ # consumer_label | text |
208
+ # provider_label | text |
149
209
  # Indexes:
150
210
  # webhooks_pkey | PRIMARY KEY btree (id)
151
211
  # uq_webhook_uuid | UNIQUE btree (uuid)
212
+ # Check constraints:
213
+ # consumer_label_exclusion | (consumer_id IS NULL OR consumer_id IS NOT NULL AND consumer_label IS NULL)
214
+ # provider_label_exclusion | (provider_id IS NULL OR provider_id IS NOT NULL AND provider_label IS NULL)
152
215
  # Foreign key constraints:
153
216
  # fk_webhooks_consumer | (consumer_id) REFERENCES pacticipants(id)
154
217
  # fk_webhooks_provider | (provider_id) REFERENCES pacticipants(id)
@@ -0,0 +1,62 @@
1
+ require "webmachine/adapters/rack_mapped"
2
+
3
+ module Webmachine
4
+ class DescribeRoutes
5
+
6
+ def self.call(webmachine_applications, search_term: nil)
7
+ path_mappings = webmachine_applications.flat_map { | webmachine_application | paths_to_resource_class_mappings(webmachine_application) }
8
+
9
+ if search_term
10
+ path_mappings = path_mappings.select{ |(route, _)| route[:path].include?(search_term) }
11
+ end
12
+
13
+ path_mappings.sort_by{ | mapping | mapping[:path] }
14
+ end
15
+
16
+ def self.paths_to_resource_class_mappings(webmachine_application)
17
+ webmachine_application.routes.collect do | route |
18
+ resource_path_absolute = Pathname.new(source_location_for(route.resource))
19
+ {
20
+ path: "/" + route.path_spec.collect{ |part| part.is_a?(Symbol) ? ":#{part}" : part }.join("/"),
21
+ resource_class: route.resource,
22
+ resource_name: route.instance_variable_get(:@bindings)[:resource_name],
23
+ resource_class_location: resource_path_absolute.relative_path_from(Pathname.pwd).to_s
24
+ }.merge(info_from_resource_instance(route))
25
+ end
26
+ end
27
+
28
+ def self.info_from_resource_instance(route)
29
+ with_no_logging do
30
+ path_info = { application_context: OpenStruct.new, pacticipant_name: "foo", pacticipant_version_number: "1", resource_name: "foo" }
31
+ path_info.default = "1"
32
+ dummy_request = Webmachine::Adapters::Rack::RackRequest.new("GET", "/", Webmachine::Headers["host" => "example.org"], nil, {}, {}, { "REQUEST_METHOD" => "GET" })
33
+ dummy_request.path_info = path_info
34
+ dummy_resource = route.resource.new(dummy_request, Webmachine::Response.new)
35
+ if dummy_resource
36
+ {
37
+ allowed_methods: dummy_resource.allowed_methods,
38
+ }
39
+ else
40
+ {}
41
+ end
42
+ end
43
+ rescue StandardError => e
44
+ puts "Could not determine instance info for #{route.resource}. #{e.class} - #{e.message}"
45
+ {}
46
+ end
47
+
48
+ def self.source_location_for(clazz)
49
+ first_instance_method_name = (clazz.instance_methods(false) + clazz.private_instance_methods(false)).first
50
+ clazz.instance_method(first_instance_method_name).source_location.first
51
+ end
52
+
53
+ # If we don't turn off the logging, we get metrics logging due to the instantiation of the Webmachine::RackRequest class
54
+ def self.with_no_logging
55
+ original_default_level = SemanticLogger.default_level
56
+ SemanticLogger.default_level = :fatal
57
+ yield
58
+ ensure
59
+ SemanticLogger.default_level = original_default_level
60
+ end
61
+ end
62
+ end
@@ -241,3 +241,8 @@ span.copy-success-icon {
241
241
  div.top-of-group {
242
242
  margin-top: 20px;
243
243
  }
244
+
245
+
246
+ div[data-toggle="tooltip"] {
247
+ cursor: pointer;
248
+ }
@@ -13,6 +13,7 @@ begin
13
13
  .deploy_to_prod(pacticipant: "AutoDetectTestProvider", version: "1")
14
14
  .publish_pact(consumer: "AutoDetectTestConsumer", provider: "AutoDetectTestProvider", consumer_version: "1", tag: "feat/x", content_id: "2111")
15
15
  .publish_pact(consumer: "AutoDetectTestConsumer", provider: "AutoDetectTestProvider", consumer_version: "2", tag: "feat/y", content_id: "21asdfd")
16
+ .deploy_to_prod(pacticipant: "AutoDetectTestConsumer", version: "1")
16
17
 
17
18
  rescue StandardError => e
18
19
  puts "#{e.class} #{e.message}"
@@ -11,7 +11,7 @@ begin
11
11
  .publish_contract(consumer: "branch-consumer", provider: "branch-provider", consumer_version: "1", content_id: "1111", branch: "main")
12
12
  .publish_contract(consumer: "branch-consumer", provider: "branch-provider", consumer_version: "1", content_id: "1111", branch: "feat/x")
13
13
  .publish_contract(consumer: "branch-consumer", provider: "branch-provider", consumer_version: "2", content_id: "1111", branch: "feat/x")
14
- .get_pacts_for_verification(provider: "branch-provider", enable_pending: false)
14
+ .get_pacts_for_verification(provider: "branch-provider", enable_pending: false, consumer_version_selectors: [ { branch: "main" }, { branch: "feat/x" }])
15
15
  .verify_pact(
16
16
  provider_version_branch: "main",
17
17
  provider_version: "1",
@@ -14,7 +14,6 @@ begin
14
14
  .create_pacticipant("NewWebhookTestConsumer")
15
15
  .create_pacticipant("NewWebhookTestProvider")
16
16
  .create_tagged_pacticipant_version(pacticipant: "NewWebhookTestProvider", version: "1", tag: "main")
17
- .deploy_to_prod(pacticipant: "NewWebhookTestProvider", version: "1")
18
17
  .record_deployment(pacticipant: "NewWebhookTestProvider", version: "1", environment_name: "test")
19
18
  .record_deployment(pacticipant: "NewWebhookTestProvider", version: "1", environment_name: "prod")
20
19
  .create_version(pacticipant: "NewWebhookTestProvider", version: "2", branch: "main")
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+
4
+ $LOAD_PATH << "#{Dir.pwd}/lib"
5
+ require "pact_broker/test/http_test_data_builder"
6
+ base_url = ENV["PACT_BROKER_BASE_URL"] || "http://localhost:9292"
7
+
8
+ td = PactBroker::Test::HttpTestDataBuilder.new(base_url)
9
+ td.delete_pacticipant("some-consumer")
10
+ .delete_pacticipant("some-provider")
11
+ .create_pacticipant("some-consumer")
12
+ .create_pacticipant("some-provider")
13
+ .publish_pact(consumer: "some-consumer", consumer_version: "1", provider: "some-provider", content_id: "111", branch: "main")
14
+ .publish_pact(consumer: "some-consumer", consumer_version: "2", provider: "some-provider", content_id: "111", branch: "feat/x")
15
+ .get_pacts_for_verification(
16
+ provider_version_tag: "main",
17
+ consumer_version_selectors: [{ branch: "main" }, { branch: "feat/x" }]
18
+ )
19
+ .verify_pact(
20
+ index: 0,
21
+ provider_version_tag: "main",
22
+ provider_version: "1",
23
+ success: true
24
+ )
25
+
26
+ rescue StandardError => e
27
+ puts "#{e.class} #{e.message}"
28
+ puts e.backtrace
29
+ exit 1
30
+ end