pact_broker 2.99.0 → 2.102.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/Gemfile +1 -0
- data/README.md +14 -4
- data/db/migrations/20220622_default_allow_dangerous_contract_modification_to_false_for_new_installations.rb +22 -0
- data/db/migrations/20220625_delete_pacticipants_with_no_name.rb +62 -0
- data/db/migrations/migration_helper.rb +6 -0
- data/docs/CONFIGURATION.md +3 -1
- data/docs/api/PACTICIPANTS.md +290 -0
- data/docs/api/WEBHOOKS.md +40 -40
- data/lib/pact_broker/api/contracts/pacticipant_create_schema.rb +28 -0
- data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/reason_decorator.rb +1 -15
- data/lib/pact_broker/api/decorators/triggered_webhook_decorator.rb +1 -2
- data/lib/pact_broker/api/resources/all_webhooks.rb +1 -4
- data/lib/pact_broker/api/resources/base_resource.rb +58 -5
- data/lib/pact_broker/api/resources/branch_version.rb +10 -1
- data/lib/pact_broker/api/resources/clean.rb +11 -9
- data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +0 -4
- data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +0 -4
- data/lib/pact_broker/api/resources/deployed_version.rb +20 -16
- data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -1
- data/lib/pact_broker/api/resources/environment.rb +5 -5
- data/lib/pact_broker/api/resources/environments.rb +1 -5
- data/lib/pact_broker/api/resources/label.rb +4 -0
- data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +10 -0
- data/lib/pact_broker/api/resources/pact.rb +14 -5
- data/lib/pact_broker/api/resources/pact_webhooks.rb +1 -4
- data/lib/pact_broker/api/resources/pacticipant.rb +11 -5
- data/lib/pact_broker/api/resources/pacticipant_resource_methods.rb +0 -1
- data/lib/pact_broker/api/resources/pacticipant_webhooks.rb +1 -4
- data/lib/pact_broker/api/resources/pacticipants.rb +7 -6
- data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +19 -11
- data/lib/pact_broker/api/resources/publish_contracts.rb +12 -16
- data/lib/pact_broker/api/resources/released_version.rb +20 -8
- data/lib/pact_broker/api/resources/released_versions_for_version_and_environment.rb +11 -7
- data/lib/pact_broker/api/resources/tag.rb +7 -3
- data/lib/pact_broker/api/resources/verifications.rb +7 -9
- data/lib/pact_broker/api/resources/version.rb +8 -8
- data/lib/pact_broker/api/resources/webhook.rb +5 -4
- data/lib/pact_broker/api/resources/webhook_execution.rb +4 -6
- data/lib/pact_broker/config/runtime_configuration.rb +1 -1
- data/lib/pact_broker/contracts/contract_to_publish.rb +4 -0
- data/lib/pact_broker/contracts/contracts_to_publish.rb +4 -0
- data/lib/pact_broker/contracts/service.rb +23 -5
- data/lib/pact_broker/doc/views/index/pacticipant-branch-version.markdown +13 -2
- data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +1 -1
- data/lib/pact_broker/domain/pacticipant.rb +1 -0
- data/lib/pact_broker/domain/tag.rb +8 -32
- data/lib/pact_broker/domain/verification.rb +3 -2
- data/lib/pact_broker/domain/version.rb +26 -10
- data/lib/pact_broker/locale/en.yml +8 -4
- data/lib/pact_broker/matrix/deployment_status_summary.rb +28 -19
- data/lib/pact_broker/matrix/parse_query.rb +5 -0
- data/lib/pact_broker/matrix/quick_row.rb +5 -2
- data/lib/pact_broker/matrix/repository.rb +3 -3
- data/lib/pact_broker/matrix/resolved_selector.rb +47 -10
- data/lib/pact_broker/matrix/service.rb +17 -7
- data/lib/pact_broker/matrix/unresolved_selector.rb +14 -2
- data/lib/pact_broker/messages.rb +0 -15
- data/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names.rb +3 -3
- data/lib/pact_broker/pacticipants/repository.rb +5 -4
- data/lib/pact_broker/pacticipants/service.rb +11 -1
- data/lib/pact_broker/pacts/generate_sha.rb +1 -0
- data/lib/pact_broker/pacts/pact_version.rb +1 -0
- data/lib/pact_broker/pacts/verifiable_pact_messages.rb +4 -2
- data/lib/pact_broker/repositories/helpers.rb +13 -0
- data/lib/pact_broker/string_refinements.rb +8 -0
- data/lib/pact_broker/test/http_test_data_builder.rb +15 -0
- data/lib/pact_broker/test/test_data_builder.rb +20 -0
- data/lib/pact_broker/ui/controllers/index.rb +3 -1
- data/lib/pact_broker/ui/views/index/show.haml +3 -4
- data/lib/pact_broker/ui/views/matrix/show.haml +5 -2
- data/lib/pact_broker/verifications/pact_version_provider_tag_successful_verification.rb +15 -0
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/branch_service.rb +7 -0
- data/lib/pact_broker/versions/branch_version_repository.rb +17 -0
- data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +1 -1
- data/lib/rack/pact_broker/cascade.rb +87 -0
- data/lib/webmachine/describe_routes.rb +43 -9
- metadata +8 -5
- data/lib/db.rb +0 -79
- data/lib/pact_broker/api/resources/default_base_resource.rb +0 -0
@@ -10,13 +10,14 @@ module PactBroker
|
|
10
10
|
extend PactBroker::Repositories
|
11
11
|
extend PactBroker::Services
|
12
12
|
include PactBroker::Logging
|
13
|
+
extend PactBroker::Messages
|
13
14
|
|
14
15
|
def self.messages_for_potential_duplicate_pacticipants(pacticipant_names, base_url)
|
15
16
|
messages = []
|
16
17
|
pacticipant_names.each do | name |
|
17
18
|
potential_duplicate_pacticipants = find_potential_duplicate_pacticipants(name)
|
18
19
|
if potential_duplicate_pacticipants.any?
|
19
|
-
messages <<
|
20
|
+
messages << potential_duplicate_pacticipant_message(name, potential_duplicate_pacticipants, base_url)
|
20
21
|
end
|
21
22
|
end
|
22
23
|
messages
|
@@ -98,6 +99,15 @@ module PactBroker
|
|
98
99
|
pacticipant
|
99
100
|
end
|
100
101
|
end
|
102
|
+
|
103
|
+
private_class_method def self.potential_duplicate_pacticipant_message(new_name, potential_duplicate_pacticipants, base_url)
|
104
|
+
existing_names = potential_duplicate_pacticipants.
|
105
|
+
collect{ | p | "* #{p.name}" }.join("\n")
|
106
|
+
message("errors.duplicate_pacticipant",
|
107
|
+
new_name: new_name,
|
108
|
+
existing_names: existing_names,
|
109
|
+
base_url: base_url)
|
110
|
+
end
|
101
111
|
end
|
102
112
|
end
|
103
113
|
end
|
@@ -7,6 +7,7 @@ require "pact_broker/pacts/content"
|
|
7
7
|
module PactBroker
|
8
8
|
module Pacts
|
9
9
|
class GenerateSha
|
10
|
+
# @param [String] json_content
|
10
11
|
def self.call json_content, _options = {}
|
11
12
|
content_for_sha = if PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results
|
12
13
|
extract_verifiable_content_for_sha(json_content)
|
@@ -179,4 +179,5 @@ end
|
|
179
179
|
# latest_pact_publication_ids_for_consumer_versions | latest_pact_publication_ids_for_consumer_v_pact_version_id_fkey | (pact_version_id) REFERENCES pact_versions(id) ON DELETE CASCADE
|
180
180
|
# latest_verification_id_for_pact_version_and_provider_version | latest_v_id_for_pv_and_pv_pact_version_id_fk | (pact_version_id) REFERENCES pact_versions(id) ON DELETE CASCADE
|
181
181
|
# pact_publications | pact_publications_pact_version_id_fkey | (pact_version_id) REFERENCES pact_versions(id)
|
182
|
+
# pact_version_provider_tag_successful_verifications | pact_version_provider_tag_successful_verifications_pv_id_fk | (pact_version_id) REFERENCES pact_versions(id) ON DELETE CASCADE
|
182
183
|
# verifications | verifications_pact_version_id_fkey | (pact_version_id) REFERENCES pact_versions(id)
|
@@ -31,7 +31,7 @@ module PactBroker
|
|
31
31
|
version_text = head_consumer_tags.size == 1 || branches.size == 1 ? "version" : "versions"
|
32
32
|
if wip?
|
33
33
|
# WIP pacts will always have tags, because it is part of the definition of being a WIP pact
|
34
|
-
"The pact at #{pact_version_url} is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest #{version_text} of #{consumer_name} #{joined_head_consumer_tags_and_branches} and
|
34
|
+
"The pact at #{pact_version_url} is being verified because it is a 'work in progress' pact (ie. it is the pact for the latest #{version_text} of #{consumer_name} #{joined_head_consumer_tags_and_branches} and it has not yet been successfully verified by #{pending_provider_branch_or_tags_description("a")} when the pact's application version was explicitly specified in the consumer version selectors). #{READ_MORE_WIP}"
|
35
35
|
else
|
36
36
|
criteria_or_criterion = selectors.size > 1 ? "criteria" : "criterion"
|
37
37
|
version_or_versions = pluralize("the consumer version", selectors.size)
|
@@ -40,7 +40,9 @@ module PactBroker
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def pending_reason
|
43
|
-
if pending?
|
43
|
+
if pending? && wip?
|
44
|
+
"This pact is in pending state for this version of #{provider_name} because it was included as a 'work in progress' pact. If this verification fails, it will not cause the overall build to fail. #{READ_MORE_PENDING}"
|
45
|
+
elsif pending?
|
44
46
|
"This pact is in pending state for this version of #{provider_name} because a successful verification result for #{pending_provider_branch_or_tags_description("a")} has not yet been published. If this verification fails, it will not cause the overall build to fail. #{READ_MORE_PENDING}"
|
45
47
|
else
|
46
48
|
"This pact has previously been successfully verified by #{non_pending_provider_branch_or_tags_description}. If this verification fails, it will fail the build. #{READ_MORE_PENDING}"
|
@@ -55,6 +55,19 @@ module PactBroker
|
|
55
55
|
select_append(Sequel[model.table_name].*)
|
56
56
|
end
|
57
57
|
|
58
|
+
# @param [Symbol] max_column the name of the column of which to calculate the maxiumum
|
59
|
+
# @param [Array<Symbol>] group_by_columns the names of the columns by which to group
|
60
|
+
def max_group_by(max_column, group_by_columns, &extra_criteria_block)
|
61
|
+
maximums_base_query = extra_criteria_block ? extra_criteria_block.call(self) : self
|
62
|
+
maximums = maximums_base_query.select_group(*group_by_columns).select_append(Sequel.function(:max, max_column).as(:max_value))
|
63
|
+
|
64
|
+
max_join = group_by_columns.each_with_object({ Sequel[:maximums][:max_value] => max_column }) do | column_name, joins |
|
65
|
+
joins[Sequel[:maximums][column_name]] = column_name
|
66
|
+
end
|
67
|
+
|
68
|
+
join(maximums, max_join, table_alias: :maximums)
|
69
|
+
end
|
70
|
+
|
58
71
|
def select_for_subquery column
|
59
72
|
if mysql? #stoopid mysql doesn't allow you to modify datasets with subqueries
|
60
73
|
column_name = column.respond_to?(:alias) ? column.alias : column
|
@@ -4,6 +4,10 @@ module PactBroker
|
|
4
4
|
def blank?
|
5
5
|
true
|
6
6
|
end
|
7
|
+
|
8
|
+
def present?
|
9
|
+
false
|
10
|
+
end
|
7
11
|
end
|
8
12
|
|
9
13
|
refine Numeric do
|
@@ -13,6 +17,10 @@ module PactBroker
|
|
13
17
|
end
|
14
18
|
|
15
19
|
refine String do
|
20
|
+
def present?
|
21
|
+
!blank?
|
22
|
+
end
|
23
|
+
|
16
24
|
def not_blank?
|
17
25
|
!blank?
|
18
26
|
end
|
@@ -286,6 +286,7 @@ module PactBroker
|
|
286
286
|
"githubVerificationStatus" => "${pactbroker.githubVerificationStatus}",
|
287
287
|
"providerVersionNumber" => "${pactbroker.providerVersionNumber}",
|
288
288
|
"providerVersionTags" => "${pactbroker.providerVersionTags}",
|
289
|
+
"providerVersionBranch" => "${pactbroker.providerVersionBranch}",
|
289
290
|
"canIMerge" => "${pactbroker.providerMainBranchGithubVerificationStatus}"
|
290
291
|
}
|
291
292
|
}
|
@@ -323,6 +324,20 @@ module PactBroker
|
|
323
324
|
self
|
324
325
|
end
|
325
326
|
|
327
|
+
def can_i_merge(pacticipant:, version:)
|
328
|
+
can_i_merge_response = client.get("matrix", { q: [pacticipant: pacticipant, version: version], latestby: "cvp", mainBranch: true, latest: true }.compact ).tap { |response| check_for_error(response) }
|
329
|
+
can = !!(can_i_merge_response.body["summary"] || {})["deployable"]
|
330
|
+
puts "can-i-merge #{pacticipant} version #{version}: #{can ? 'yes' : 'no'}"
|
331
|
+
summary = can_i_merge_response.body["summary"]
|
332
|
+
verification_result_urls = (can_i_merge_response.body["matrix"] || []).collect do | row |
|
333
|
+
row.dig("verificationResult", "_links", "self", "href")
|
334
|
+
end.compact
|
335
|
+
summary.merge!("verification_result_urls" => verification_result_urls)
|
336
|
+
puts summary.to_yaml
|
337
|
+
separate
|
338
|
+
self
|
339
|
+
end
|
340
|
+
|
326
341
|
def delete_integration(consumer:, provider:)
|
327
342
|
puts "Deleting all data for the integration between #{consumer} and #{provider}"
|
328
343
|
client.delete("integrations/provider/#{encode(provider)}/consumer/#{encode(consumer)}").tap { |response| check_for_error(response) }
|
@@ -572,6 +572,26 @@ module PactBroker
|
|
572
572
|
}.to_json
|
573
573
|
end
|
574
574
|
|
575
|
+
def fixed_json_content(consumer_name, provider_name, differentiator)
|
576
|
+
{
|
577
|
+
"consumer" => {
|
578
|
+
"name" => consumer_name
|
579
|
+
},
|
580
|
+
"provider" => {
|
581
|
+
"name" => provider_name
|
582
|
+
},
|
583
|
+
"interactions" => [{
|
584
|
+
"request" => {
|
585
|
+
"method" => "GET",
|
586
|
+
"path" => "/things/#{differentiator}"
|
587
|
+
},
|
588
|
+
"response" => {
|
589
|
+
"status" => 200
|
590
|
+
}
|
591
|
+
}],
|
592
|
+
}.to_json
|
593
|
+
end
|
594
|
+
|
575
595
|
private
|
576
596
|
|
577
597
|
def create_pacticipant_version(version_number, pacticipant, params = {})
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require "pact_broker/ui/controllers/base_controller"
|
2
2
|
require "pact_broker/ui/view_models/index_items"
|
3
|
+
require "pact_broker/string_refinements"
|
3
4
|
require "haml"
|
4
5
|
|
5
6
|
module PactBroker
|
@@ -7,6 +8,7 @@ module PactBroker
|
|
7
8
|
module Controllers
|
8
9
|
class Index < Base
|
9
10
|
include PactBroker::Services
|
11
|
+
using PactBroker::StringRefinements
|
10
12
|
|
11
13
|
get "/" do
|
12
14
|
set_headers
|
@@ -27,7 +29,7 @@ module PactBroker
|
|
27
29
|
|
28
30
|
index_items = index_service.find_index_items(options)
|
29
31
|
|
30
|
-
if index_items.
|
32
|
+
if index_items.empty? && !search.blank?
|
31
33
|
error_messages << "No pacticipants found matching: \"#{search}\""
|
32
34
|
end
|
33
35
|
|
@@ -70,12 +70,14 @@
|
|
70
70
|
%input{name: 'q[]latest', value: 'true', hidden: true, class: 'latest-flag'}
|
71
71
|
|
72
72
|
|
73
|
-
|
73
|
+
%input{name: 'latest', value: options.latest.to_s, hidden: true }
|
74
|
+
%input{name: 'mainBranch', value: options.mainBranch.to_s, hidden: true}
|
75
|
+
|
76
|
+
- if options.tag
|
74
77
|
.selector
|
75
78
|
%label{for: 'to'}
|
76
79
|
= options.latest ? 'To (tag)' : 'With all (tagged)'
|
77
80
|
%input{name: 'tag', id: 'to', value: options.tag }
|
78
|
-
%input{name: 'latest', value: options.latest.to_s, hidden: true}
|
79
81
|
|
80
82
|
- if options.environment_name
|
81
83
|
.selector
|
@@ -83,6 +85,7 @@
|
|
83
85
|
To environment
|
84
86
|
%input{name: 'environment', id: 'environment', value: options.environment_name }
|
85
87
|
|
88
|
+
|
86
89
|
%div.top-of-group.form-check
|
87
90
|
%input{type: 'radio', name: "latestby", class: 'form-check-input', value: '', id: 'all_rows', checked: options.all_rows_checked}
|
88
91
|
%label{for: 'all_rows', class: "form-check-label"}
|
@@ -9,3 +9,18 @@ module PactBroker
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
# Table: pact_version_provider_tag_successful_verifications
|
14
|
+
# Columns:
|
15
|
+
# id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
|
16
|
+
# pact_version_id | integer | NOT NULL
|
17
|
+
# provider_version_tag_name | text | NOT NULL
|
18
|
+
# wip | boolean | NOT NULL
|
19
|
+
# verification_id | integer |
|
20
|
+
# execution_date | timestamp without time zone | NOT NULL
|
21
|
+
# Indexes:
|
22
|
+
# pact_version_provider_tag_successful_verifications_pkey | PRIMARY KEY btree (id)
|
23
|
+
# pact_version_provider_tag_verifications_pv_pvtn_wip_unique | UNIQUE btree (pact_version_id, provider_version_tag_name, wip)
|
24
|
+
# Foreign key constraints:
|
25
|
+
# pact_version_provider_tag_successful_verifications_pv_id_fk | (pact_version_id) REFERENCES pact_versions(id) ON DELETE CASCADE
|
26
|
+
# pact_version_provider_tag_successful_verifications_v_id_fk | (verification_id) REFERENCES verifications(id) ON DELETE SET NULL
|
data/lib/pact_broker/version.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
require "pact_broker/logging"
|
2
2
|
require "pact_broker/repositories"
|
3
3
|
require "pact_broker/messages"
|
4
|
+
require "forwardable"
|
4
5
|
|
5
6
|
module PactBroker
|
6
7
|
module Versions
|
7
8
|
class BranchService
|
8
9
|
extend PactBroker::Repositories
|
9
10
|
|
11
|
+
class << self
|
12
|
+
extend Forwardable
|
13
|
+
delegate [:delete_branch_version] => :branch_version_repository
|
14
|
+
end
|
15
|
+
|
16
|
+
|
10
17
|
def self.find_branch_version(pacticipant_name:, branch_name:, version_number:, **)
|
11
18
|
BranchVersion.where(
|
12
19
|
version: PactBroker::Domain::Version.where_pacticipant_name_and_version_number(pacticipant_name, version_number),
|
@@ -16,6 +16,23 @@ module PactBroker
|
|
16
16
|
branch_version
|
17
17
|
end
|
18
18
|
|
19
|
+
# Deletes a branch version - that is, removes a version from a branch.
|
20
|
+
# Updates the branch head if the deleted branch version was the latest for the branch.
|
21
|
+
#
|
22
|
+
# @param [PactBroker::Versions::BranchVersion] the branch version to delete
|
23
|
+
def delete_branch_version(branch_version)
|
24
|
+
latest = branch_version.latest?
|
25
|
+
branch = branch_version.latest? ? branch_version.branch : nil
|
26
|
+
deleted = branch_version.delete
|
27
|
+
if latest
|
28
|
+
new_head_branch_version = BranchVersion.find_latest_for_branch(branch)
|
29
|
+
if new_head_branch_version
|
30
|
+
PactBroker::Versions::BranchHead.new(branch: branch, branch_version: new_head_branch_version).upsert
|
31
|
+
end
|
32
|
+
end
|
33
|
+
deleted
|
34
|
+
end
|
35
|
+
|
19
36
|
private
|
20
37
|
|
21
38
|
def find_or_create_branch(pacticipant, branch_name)
|
@@ -173,7 +173,7 @@ module PactBroker
|
|
173
173
|
if webhook_context.key?(:provider_version_branch)
|
174
174
|
webhook_context[:provider_version_branch] || ""
|
175
175
|
else
|
176
|
-
verification&.provider_version&.
|
176
|
+
verification&.provider_version&.branch_names&.last || ""
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is a modified version of the Rack::Cascade class from https://github.com/rack/rack/blob/2833813/lib/rack/cascade.rb
|
4
|
+
# that short circuts the cascade if a response body is provided for a 404/403 response.
|
5
|
+
# This is to allow the UI and API to be in a cascade, but allows the UI
|
6
|
+
# to return a 404 page when the route matches, but there is no domain object found.
|
7
|
+
# If we don't do this, then the 404 from the UI causes an API response to be returned.
|
8
|
+
|
9
|
+
|
10
|
+
# This does not work. Do not use it. Have not yet worked out why.
|
11
|
+
|
12
|
+
module Rack
|
13
|
+
# Rack::Cascade tries a request on several apps, and returns the
|
14
|
+
# first response that is not 404 or 405 (or in a list of configured
|
15
|
+
# status codes). If all applications tried return one of the configured
|
16
|
+
# status codes, return the last response.
|
17
|
+
|
18
|
+
module PactBroker
|
19
|
+
class Cascade
|
20
|
+
# deprecated, no longer used
|
21
|
+
NotFound = [404, { CONTENT_TYPE => "text/plain" }, []]
|
22
|
+
|
23
|
+
# An array of applications to try in order.
|
24
|
+
attr_reader :apps
|
25
|
+
|
26
|
+
# Set the apps to send requests to, and what statuses result in
|
27
|
+
# cascading. Arguments:
|
28
|
+
#
|
29
|
+
# apps: An enumerable of rack applications.
|
30
|
+
# cascade_for: The statuses to use cascading for. If a response is received
|
31
|
+
# from an app, the next app is tried.
|
32
|
+
def initialize(apps, cascade_for = [404, 405])
|
33
|
+
@apps = []
|
34
|
+
apps.each { |app| add app }
|
35
|
+
|
36
|
+
@cascade_for = {}
|
37
|
+
[*cascade_for].each { |status| @cascade_for[status] = true }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Call each app in order. If the responses uses a status that requires
|
41
|
+
# cascading, try the next app. If all responses require cascading,
|
42
|
+
# return the response from the last app.
|
43
|
+
def call(env)
|
44
|
+
return [404, { CONTENT_TYPE => "text/plain" }, []] if @apps.empty?
|
45
|
+
result = nil
|
46
|
+
last_body = nil
|
47
|
+
|
48
|
+
@apps.each_with_index do |app, i|
|
49
|
+
# The SPEC says that the body must be closed after it has been iterated
|
50
|
+
# by the server, or if it is replaced by a middleware action. Cascade
|
51
|
+
# replaces the body each time a cascade happens. It is assumed that nil
|
52
|
+
# does not respond to close, otherwise the previous application body
|
53
|
+
# will be closed. The final application body will not be closed, as it
|
54
|
+
# will be passed to the server as a result.
|
55
|
+
last_body.close if last_body.respond_to? :close
|
56
|
+
result = app.call(env)
|
57
|
+
|
58
|
+
puts result.last
|
59
|
+
|
60
|
+
# If it is a 404/403 AND the response body is empty, then try the next app
|
61
|
+
if @cascade_for.include?(result[0].to_i) && result[2].respond_to?(:empty?) && result[2].empty?
|
62
|
+
last_body = result[2]
|
63
|
+
else
|
64
|
+
puts "returned from #{i} of #{@apps.size}"
|
65
|
+
# otherwise, return the result
|
66
|
+
return result
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
result
|
71
|
+
end
|
72
|
+
|
73
|
+
# Append an app to the list of apps to cascade. This app will
|
74
|
+
# be tried last.
|
75
|
+
def add(app)
|
76
|
+
@apps << app
|
77
|
+
end
|
78
|
+
|
79
|
+
# Whether the given app is one of the apps to cascade to.
|
80
|
+
def include?(app)
|
81
|
+
@apps.include?(app)
|
82
|
+
end
|
83
|
+
|
84
|
+
alias_method :<<, :add
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -5,6 +5,7 @@ module Webmachine
|
|
5
5
|
|
6
6
|
Route = Struct.new(
|
7
7
|
:path,
|
8
|
+
:path_spec,
|
8
9
|
:resource_class,
|
9
10
|
:resource_name,
|
10
11
|
:resource_class_location,
|
@@ -23,6 +24,38 @@ module Webmachine
|
|
23
24
|
def path_include?(component)
|
24
25
|
path.include?(component)
|
25
26
|
end
|
27
|
+
|
28
|
+
def route_param_names
|
29
|
+
path_spec.select { | component | component.is_a?(Symbol) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Creates a Webmachine Resource for the given route for use in tests.
|
33
|
+
# @param [Hash] env the rack env from which to build the request
|
34
|
+
# @param [PactBroker::ApplicationContext] application_context the application context
|
35
|
+
# @param [Hash] path_param_values concrete parameter values from which to construct the path
|
36
|
+
# @return [Webmachine::Resource] the webmachine resource for the request
|
37
|
+
def build_resource(env, application_context, path_param_values)
|
38
|
+
path = "/" + path_spec.collect{ | part | part.is_a?(Symbol) ? (path_param_values[part] || "missing-param") : part }.join("/")
|
39
|
+
|
40
|
+
path_params = route_param_names.each_with_object({}){ | name, new_params | new_params[name] = path_param_values[name] }
|
41
|
+
path_info = {
|
42
|
+
application_context: application_context,
|
43
|
+
resource_name: resource_name
|
44
|
+
}.merge(path_params)
|
45
|
+
|
46
|
+
rack_req = ::Rack::Request.new({ "REQUEST_METHOD" => "GET", "rack.input" => StringIO.new("") }.merge(env) )
|
47
|
+
dummy_request = Webmachine::Adapters::Rack::RackRequest.new(
|
48
|
+
rack_req.env["REQUEST_METHOD"],
|
49
|
+
path,
|
50
|
+
Webmachine::Headers.from_cgi({"HTTP_HOST" => "example.org"}.merge(env)),
|
51
|
+
Webmachine::Adapters::Rack::RequestBody.new(rack_req),
|
52
|
+
{},
|
53
|
+
{},
|
54
|
+
rack_req.env
|
55
|
+
)
|
56
|
+
dummy_request.path_info = path_info
|
57
|
+
resource_class.new(dummy_request, Webmachine::Response.new)
|
58
|
+
end
|
26
59
|
end
|
27
60
|
|
28
61
|
def self.call(webmachine_applications, search_term: nil)
|
@@ -36,24 +69,25 @@ module Webmachine
|
|
36
69
|
end
|
37
70
|
|
38
71
|
def self.paths_to_resource_class_mappings(webmachine_application)
|
39
|
-
webmachine_application.routes.collect do |
|
40
|
-
resource_path_absolute = Pathname.new(source_location_for(
|
72
|
+
webmachine_application.routes.collect do | webmachine_route |
|
73
|
+
resource_path_absolute = Pathname.new(source_location_for(webmachine_route.resource))
|
41
74
|
Route.new({
|
42
|
-
path: "/" +
|
43
|
-
|
44
|
-
|
75
|
+
path: "/" + webmachine_route.path_spec.collect{ |part| part.is_a?(Symbol) ? ":#{part}" : part }.join("/"),
|
76
|
+
path_spec: webmachine_route.path_spec,
|
77
|
+
resource_class: webmachine_route.resource,
|
78
|
+
resource_name: webmachine_route.instance_variable_get(:@bindings)[:resource_name],
|
45
79
|
resource_class_location: resource_path_absolute.relative_path_from(Pathname.pwd).to_s
|
46
|
-
}.merge(info_from_resource_instance(
|
47
|
-
end
|
80
|
+
}.merge(info_from_resource_instance(webmachine_route)))
|
81
|
+
end.reject{ | route | route.resource_class == Webmachine::Trace::TraceResource }
|
48
82
|
end
|
49
83
|
|
50
|
-
def self.info_from_resource_instance(
|
84
|
+
def self.info_from_resource_instance(webmachine_route)
|
51
85
|
with_no_logging do
|
52
86
|
path_info = { application_context: OpenStruct.new, pacticipant_name: "foo", pacticipant_version_number: "1", resource_name: "foo" }
|
53
87
|
path_info.default = "1"
|
54
88
|
dummy_request = Webmachine::Adapters::Rack::RackRequest.new("GET", "/", Webmachine::Headers["host" => "example.org"], nil, {}, {}, { "REQUEST_METHOD" => "GET" })
|
55
89
|
dummy_request.path_info = path_info
|
56
|
-
dummy_resource =
|
90
|
+
dummy_resource = webmachine_route.resource.new(dummy_request, Webmachine::Response.new)
|
57
91
|
if dummy_resource
|
58
92
|
{
|
59
93
|
allowed_methods: dummy_resource.allowed_methods,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pact_broker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.102.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bethany Skurrie
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-
|
13
|
+
date: 2022-07-07 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httparty
|
@@ -584,10 +584,12 @@ files:
|
|
584
584
|
- db/migrations/20211120_create_pact_version_provider_tag_successful_verifications.rb
|
585
585
|
- db/migrations/20211121_migrate_pact_version_provider_tag_successful_verifications_data.rb
|
586
586
|
- db/migrations/20220303_increase_consumer_version_selector_hashes_column_size.rb
|
587
|
+
- db/migrations/20220622_default_allow_dangerous_contract_modification_to_false_for_new_installations.rb
|
588
|
+
- db/migrations/20220625_delete_pacticipants_with_no_name.rb
|
587
589
|
- db/migrations/migration_helper.rb
|
588
590
|
- docs/CONFIGURATION.md
|
591
|
+
- docs/api/PACTICIPANTS.md
|
589
592
|
- docs/api/WEBHOOKS.md
|
590
|
-
- lib/db.rb
|
591
593
|
- lib/pact/doc/README.md
|
592
594
|
- lib/pact/doc/doc_file.rb
|
593
595
|
- lib/pact/doc/generate.rb
|
@@ -608,6 +610,7 @@ files:
|
|
608
610
|
- lib/pact_broker/api/contracts/dry_validation_predicates.rb
|
609
611
|
- lib/pact_broker/api/contracts/dry_validation_workarounds.rb
|
610
612
|
- lib/pact_broker/api/contracts/environment_schema.rb
|
613
|
+
- lib/pact_broker/api/contracts/pacticipant_create_schema.rb
|
611
614
|
- lib/pact_broker/api/contracts/pacticipant_name_contract.rb
|
612
615
|
- lib/pact_broker/api/contracts/pacticipant_name_validation.rb
|
613
616
|
- lib/pact_broker/api/contracts/pacticipant_schema.rb
|
@@ -705,7 +708,6 @@ files:
|
|
705
708
|
- lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb
|
706
709
|
- lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb
|
707
710
|
- lib/pact_broker/api/resources/dashboard.rb
|
708
|
-
- lib/pact_broker/api/resources/default_base_resource.rb
|
709
711
|
- lib/pact_broker/api/resources/deployed_version.rb
|
710
712
|
- lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb
|
711
713
|
- lib/pact_broker/api/resources/environment.rb
|
@@ -1076,6 +1078,7 @@ files:
|
|
1076
1078
|
- lib/rack/hal_browser/redirect.rb
|
1077
1079
|
- lib/rack/pact_broker/add_pact_broker_version_header.rb
|
1078
1080
|
- lib/rack/pact_broker/add_vary_header.rb
|
1081
|
+
- lib/rack/pact_broker/cascade.rb
|
1079
1082
|
- lib/rack/pact_broker/configurable_make_it_later.rb
|
1080
1083
|
- lib/rack/pact_broker/convert_404_to_hal.rb
|
1081
1084
|
- lib/rack/pact_broker/convert_file_extension_to_accept_header.rb
|
@@ -1222,7 +1225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1222
1225
|
- !ruby/object:Gem::Version
|
1223
1226
|
version: '0'
|
1224
1227
|
requirements: []
|
1225
|
-
rubygems_version: 3.3.
|
1228
|
+
rubygems_version: 3.3.17
|
1226
1229
|
signing_key:
|
1227
1230
|
specification_version: 4
|
1228
1231
|
summary: See description
|
data/lib/db.rb
DELETED
@@ -1,79 +0,0 @@
|
|
1
|
-
require "sequel"
|
2
|
-
require "sequel/connection_pool/threaded"
|
3
|
-
require "yaml"
|
4
|
-
require "pact_broker/logging"
|
5
|
-
require "erb"
|
6
|
-
require "pact_broker/project_root"
|
7
|
-
require "fileutils"
|
8
|
-
|
9
|
-
module DB
|
10
|
-
include PactBroker::Logging
|
11
|
-
##
|
12
|
-
# Sequel by default does not test connections in its connection pool before
|
13
|
-
# handing them to a client. To enable connection testing you need to load the
|
14
|
-
# "connection_validator" extension like below. The connection validator
|
15
|
-
# extension is configurable, by default it only checks connections once per
|
16
|
-
# hour:
|
17
|
-
#
|
18
|
-
# http://sequel.rubyforge.org/rdoc-plugins/files/lib/sequel/extensions/connection_validator_rb.html
|
19
|
-
#
|
20
|
-
# Because most of our applications so far are accessed infrequently, there is
|
21
|
-
# very little overhead in checking each connection when it is requested. This
|
22
|
-
# takes care of stale connections.
|
23
|
-
#
|
24
|
-
# A gotcha here is that it is not enough to enable the "connection_validator"
|
25
|
-
# extension, we also need to specify that we want to use the threaded connection
|
26
|
-
# pool, as noted in the documentation for the extension.
|
27
|
-
#
|
28
|
-
def self.connect db_credentials
|
29
|
-
# Keep this conifiguration in sync with lib/pact_broker/app.rb#configure_database_connection
|
30
|
-
Sequel.datetime_class = DateTime
|
31
|
-
if ENV["DEBUG"] == "true" && ENV["PACT_BROKER_SQL_LOG_LEVEL"] != "none"
|
32
|
-
logger = Logger.new($stdout)
|
33
|
-
end
|
34
|
-
if db_credentials.fetch("adapter") == "sqlite"
|
35
|
-
FileUtils.mkdir_p(File.dirname(db_credentials.fetch("database")))
|
36
|
-
end
|
37
|
-
con = Sequel.connect(db_credentials.merge(:logger => logger, :pool_class => Sequel::ThreadedConnectionPool, :encoding => "utf8"))
|
38
|
-
con.extension(:connection_validator)
|
39
|
-
con.extension(:pagination)
|
40
|
-
con.extension(:statement_timeout)
|
41
|
-
con.extension(:any_not_empty)
|
42
|
-
#con.extension(:caller_logging)
|
43
|
-
con.timezone = :utc
|
44
|
-
con.run("SET sql_mode='STRICT_TRANS_TABLES';") if db_credentials[:adapter].to_s =~ /mysql/
|
45
|
-
con
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.connection_for_env env
|
49
|
-
config = configuration_for_env(env)
|
50
|
-
logger.info "Connecting to #{env} #{config['adapter']} database #{config['database']}."
|
51
|
-
connect config
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.configuration_for_env env
|
55
|
-
database_yml = PactBroker.project_root.join("config","database.yml")
|
56
|
-
config = YAML.load(ERB.new(File.read(database_yml)).result)
|
57
|
-
config.fetch(env).fetch(ENV.fetch("DATABASE_ADAPTER","default"))
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.sqlite?
|
61
|
-
!!(PACT_BROKER_DB.adapter_scheme.to_s =~ /sqlite/)
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.mysql?
|
65
|
-
!!(PACT_BROKER_DB.adapter_scheme.to_s =~ /mysql/)
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.postgres?
|
69
|
-
!!(PACT_BROKER_DB.adapter_scheme.to_s =~ /postgres/)
|
70
|
-
end
|
71
|
-
|
72
|
-
PACT_BROKER_DB ||= connection_for_env ENV.fetch("RACK_ENV")
|
73
|
-
|
74
|
-
def self.health_check
|
75
|
-
PACT_BROKER_DB.synchronize do |c|
|
76
|
-
PACT_BROKER_DB.valid_connection? c
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
File without changes
|