pact_broker 2.106.0 → 2.107.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/Gemfile +3 -0
- data/db/migrations/20230131_add_cons_ver_id_ndx_to_latest_pp_id_for_cons_ver.rb +8 -4
- data/db/migrations/20230216_add_branch_heads_branch_version_id_index.rb +21 -0
- data/db/migrations/20230428_add_index_for_webhook_executions_pact_publication_id.rb +17 -0
- data/lib/pact_broker/api/contracts/base_contract.rb +22 -7
- data/lib/pact_broker/api/contracts/can_i_deploy_query_schema.rb +34 -0
- data/lib/pact_broker/api/contracts/configuration.rb +2 -0
- data/lib/pact_broker/api/contracts/consumer_version_selector_contract.rb +140 -0
- data/lib/pact_broker/api/contracts/dry_validation_errors_formatter.rb +50 -0
- data/lib/pact_broker/api/contracts/dry_validation_macros.rb +79 -0
- data/lib/pact_broker/api/contracts/dry_validation_methods.rb +71 -0
- data/lib/pact_broker/api/contracts/environment_schema.rb +19 -33
- data/lib/pact_broker/api/contracts/pacticipant_create_schema.rb +4 -17
- data/lib/pact_broker/api/contracts/pacticipant_schema.rb +15 -24
- data/lib/pact_broker/api/contracts/pacts_for_verification_json_query_schema.rb +37 -146
- data/lib/pact_broker/api/contracts/pacts_for_verification_query_string_schema.rb +7 -20
- data/lib/pact_broker/api/contracts/publish_contracts_contract_contract.rb +62 -0
- data/lib/pact_broker/api/contracts/publish_contracts_schema.rb +25 -124
- data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +21 -26
- data/lib/pact_broker/api/contracts/utf_8_validation.rb +2 -0
- data/lib/pact_broker/api/contracts/validation_helpers.rb +71 -0
- data/lib/pact_broker/api/contracts/verification_contract.rb +10 -29
- data/lib/pact_broker/api/contracts/webhook_contract.rb +20 -172
- data/lib/pact_broker/api/contracts/webhook_pacticipant_contract.rb +33 -0
- data/lib/pact_broker/api/contracts/webhook_request_contract.rb +125 -0
- data/lib/pact_broker/api/contracts.rb +3 -0
- data/lib/pact_broker/api/decorators/custom_error_problem_json_decorator.rb +4 -4
- data/lib/pact_broker/api/decorators/dashboard_text_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/extended_pact_decorator.rb +1 -1
- data/lib/pact_broker/api/decorators/matrix_decorator.rb +4 -4
- data/lib/pact_broker/api/decorators/matrix_text_decorator.rb +1 -1
- data/lib/pact_broker/api/decorators/pact_decorator.rb +1 -1
- data/lib/pact_broker/api/decorators/pacticipant_collection_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +2 -1
- data/lib/pact_broker/api/decorators/pacts_for_verification_query_decorator.rb +4 -1
- data/lib/pact_broker/api/decorators/pagination_links.rb +6 -6
- data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +4 -4
- data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +6 -6
- data/lib/pact_broker/api/decorators/verifiable_pact_decorator.rb +4 -4
- data/lib/pact_broker/api/decorators/webhook_decorator.rb +2 -3
- data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +5 -12
- data/lib/pact_broker/api/resources/all_webhooks.rb +5 -11
- data/lib/pact_broker/api/resources/base_resource.rb +3 -20
- data/lib/pact_broker/api/resources/branch_version.rb +3 -3
- data/lib/pact_broker/api/resources/can_i_deploy.rb +4 -19
- data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment.rb +1 -4
- data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag.rb +0 -2
- data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_tag_to_tag_badge.rb +1 -2
- data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +2 -2
- data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +2 -2
- data/lib/pact_broker/api/resources/dashboard.rb +3 -3
- data/lib/pact_broker/api/resources/deployed_version.rb +1 -1
- data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +2 -2
- data/lib/pact_broker/api/resources/environment.rb +1 -1
- data/lib/pact_broker/api/resources/environments.rb +2 -2
- data/lib/pact_broker/api/resources/error_handling_methods.rb +2 -2
- data/lib/pact_broker/api/resources/integrations.rb +1 -1
- data/lib/pact_broker/api/resources/label.rb +1 -1
- data/lib/pact_broker/api/resources/latest_pact.rb +2 -2
- data/lib/pact_broker/api/resources/latest_pacts.rb +1 -1
- data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +1 -1
- data/lib/pact_broker/api/resources/matrix.rb +2 -2
- data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +1 -1
- data/lib/pact_broker/api/resources/pact.rb +7 -4
- data/lib/pact_broker/api/resources/pact_triggered_webhooks.rb +1 -1
- data/lib/pact_broker/api/resources/pact_versions.rb +1 -1
- data/lib/pact_broker/api/resources/pact_webhooks.rb +7 -14
- data/lib/pact_broker/api/resources/pact_webhooks_status.rb +6 -2
- data/lib/pact_broker/api/resources/pacticipant.rb +1 -1
- data/lib/pact_broker/api/resources/pacticipant_webhooks.rb +7 -5
- data/lib/pact_broker/api/resources/pacticipants.rb +2 -2
- data/lib/pact_broker/api/resources/pacticipants_for_label.rb +1 -1
- data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +1 -1
- data/lib/pact_broker/api/resources/provider_pacts.rb +1 -1
- data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +4 -13
- data/lib/pact_broker/api/resources/publish_contracts.rb +8 -3
- data/lib/pact_broker/api/resources/released_version.rb +1 -1
- data/lib/pact_broker/api/resources/released_versions_for_version_and_environment.rb +2 -2
- data/lib/pact_broker/api/resources/tag.rb +1 -1
- data/lib/pact_broker/api/resources/tagged_pact_versions.rb +1 -1
- data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +2 -2
- data/lib/pact_broker/api/resources/verification.rb +2 -2
- data/lib/pact_broker/api/resources/verification_triggered_webhooks.rb +1 -1
- data/lib/pact_broker/api/resources/verifications.rb +4 -6
- data/lib/pact_broker/api/resources/version.rb +1 -1
- data/lib/pact_broker/api/resources/versions.rb +1 -1
- data/lib/pact_broker/api/resources/webhook.rb +7 -6
- data/lib/pact_broker/api/resources/webhook_execution.rb +6 -4
- data/lib/pact_broker/api.rb +3 -12
- data/lib/pact_broker/certificates/certificate.rb +1 -0
- data/lib/pact_broker/config/setting.rb +1 -0
- data/lib/pact_broker/contracts/service.rb +1 -0
- data/lib/pact_broker/date_helper.rb +1 -1
- data/lib/pact_broker/db/clean_incremental.rb +1 -1
- data/lib/pact_broker/db/delete_overwritten_data.rb +6 -2
- data/lib/pact_broker/deployments/currently_deployed_version_id.rb +2 -0
- data/lib/pact_broker/deployments/deployed_version.rb +2 -0
- data/lib/pact_broker/deployments/deployed_version_service.rb +5 -1
- data/lib/pact_broker/deployments/environment.rb +2 -0
- data/lib/pact_broker/deployments/environment_service.rb +4 -3
- data/lib/pact_broker/deployments/released_version.rb +2 -0
- data/lib/pact_broker/deployments/released_version_service.rb +4 -0
- data/lib/pact_broker/diagnostic/resources/base_resource.rb +1 -1
- data/lib/pact_broker/doc/views/index/publish-contracts.markdown +5 -5
- data/lib/pact_broker/domain/label.rb +1 -0
- data/lib/pact_broker/domain/tag.rb +2 -0
- data/lib/pact_broker/domain/verification.rb +1 -1
- data/lib/pact_broker/domain/version.rb +4 -1
- data/lib/pact_broker/domain/webhook.rb +1 -1
- data/lib/pact_broker/index/service.rb +1 -1
- data/lib/pact_broker/integrations/integration.rb +1 -0
- data/lib/pact_broker/locale/en.yml +35 -14
- data/lib/pact_broker/matrix/query_ids.rb +4 -4
- data/lib/pact_broker/matrix/resolved_selector.rb +6 -1
- data/lib/pact_broker/matrix/service.rb +1 -0
- data/lib/pact_broker/messages.rb +5 -1
- data/lib/pact_broker/pacticipants/repository.rb +12 -3
- data/lib/pact_broker/pacticipants/service.rb +7 -0
- data/lib/pact_broker/pacts/pact_params.rb +6 -17
- data/lib/pact_broker/pacts/pact_version.rb +1 -0
- data/lib/pact_broker/policies.rb +4 -4
- data/lib/pact_broker/test/http_test_data_builder.rb +46 -2
- data/lib/pact_broker/ui/app.rb +2 -2
- data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +1 -1
- data/lib/pact_broker/verifications/pact_version_provider_tag_successful_verification.rb +1 -0
- data/lib/pact_broker/verifications/service.rb +0 -6
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/branch.rb +1 -0
- data/lib/pact_broker/versions/branch_head.rb +2 -1
- data/lib/pact_broker/versions/branch_version.rb +11 -0
- data/lib/pact_broker/webhooks/execution.rb +1 -1
- data/lib/pact_broker/webhooks/repository.rb +1 -1
- data/lib/pact_broker/webhooks/service.rb +3 -25
- data/lib/pact_broker/webhooks/triggered_webhook.rb +1 -0
- data/lib/pact_broker/webhooks/webhook_event.rb +1 -0
- data/lib/pact_broker/webmachine.rb +22 -0
- data/lib/rack/pact_broker/invalid_uri_protection.rb +1 -1
- data/lib/rack/pact_broker/use_when.rb +6 -5
- data/lib/sequel/plugins/age.rb +13 -0
- data/lib/webmachine/application_monkey_patch.rb +5 -0
- data/lib/webmachine/describe_routes.rb +35 -8
- data/lib/webmachine/render_error_monkey_patch.rb +1 -1
- data/pact_broker.gemspec +7 -17
- metadata +65 -67
- data/lib/pact/doc/README.md +0 -5
- data/lib/pact_broker/api/contracts/dry_validation_predicates.rb +0 -36
- data/lib/pact_broker/api/contracts/dry_validation_workarounds.rb +0 -39
- data/lib/pact_broker/api/contracts/pacticipant_name_contract.rb +0 -24
- data/lib/pact_broker/api/contracts/pacticipant_name_validation.rb +0 -30
- data/lib/pact_broker/api/contracts/request_validations.rb +0 -33
- data/lib/pact_broker/api/resources/webhook_resource_methods.rb +0 -17
- data/lib/pact_broker/matrix/can_i_deploy_query_schema.rb +0 -46
|
@@ -1,33 +1,24 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "pact_broker/api/contracts/dry_validation_workarounds"
|
|
3
|
-
require "pact_broker/api/contracts/dry_validation_predicates"
|
|
4
|
-
require "pact_broker/messages"
|
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
|
5
2
|
|
|
6
3
|
module PactBroker
|
|
7
4
|
module Api
|
|
8
5
|
module Contracts
|
|
9
|
-
class PacticipantSchema
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
|
18
|
-
end
|
|
19
|
-
optional(:name).filled(:str?, :single_line?)
|
|
20
|
-
optional(:displayName).maybe(:str?, :single_line?, :not_blank?)
|
|
21
|
-
optional(:mainBranch).maybe(:str?, :single_line?, :no_spaces?)
|
|
22
|
-
optional(:repositoryUrl).maybe(:str?, :single_line?)
|
|
23
|
-
optional(:repositoryName).maybe(:str?, :single_line?)
|
|
24
|
-
optional(:repositoryNamespace).maybe(:str?, :single_line?)
|
|
6
|
+
class PacticipantSchema < BaseContract
|
|
7
|
+
json do
|
|
8
|
+
optional(:name).filled(:string)
|
|
9
|
+
optional(:displayName).maybe(:string)
|
|
10
|
+
optional(:mainBranch).maybe(:string)
|
|
11
|
+
optional(:repositoryUrl).maybe(:string)
|
|
12
|
+
optional(:repositoryName).maybe(:string)
|
|
13
|
+
optional(:repositoryNamespace).maybe(:string)
|
|
25
14
|
end
|
|
26
15
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
16
|
+
rule(:name).validate(:not_multiple_lines, :not_blank_if_present)
|
|
17
|
+
rule(:displayName).validate(:not_multiple_lines, :not_blank_if_present)
|
|
18
|
+
rule(:mainBranch).validate(:not_multiple_lines, :no_spaces_if_present, :not_blank_if_present)
|
|
19
|
+
rule(:repositoryUrl).validate(:not_multiple_lines)
|
|
20
|
+
rule(:repositoryName).validate(:not_multiple_lines)
|
|
21
|
+
rule(:repositoryNamespace).validate(:not_multiple_lines)
|
|
31
22
|
end
|
|
32
23
|
end
|
|
33
24
|
end
|
|
@@ -1,161 +1,52 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "pact_broker/
|
|
3
|
-
require "pact_broker/
|
|
4
|
-
require "pact_broker/api/contracts/dry_validation_workarounds"
|
|
5
|
-
require "pact_broker/api/contracts/dry_validation_predicates"
|
|
6
|
-
require "pact_broker/messages"
|
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
|
2
|
+
require "pact_broker/api/contracts/consumer_version_selector_contract"
|
|
3
|
+
require "pact_broker/logging"
|
|
7
4
|
|
|
8
5
|
module PactBroker
|
|
9
6
|
module Api
|
|
10
7
|
module Contracts
|
|
11
|
-
class PactsForVerificationJSONQuerySchema
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
using PactBroker::HashRefinements
|
|
16
|
-
using PactBroker::StringRefinements
|
|
17
|
-
|
|
18
|
-
BRANCH_KEYS = [:latest, :tag, :fallbackTag, :branch, :fallbackBranch, :matchingBranch]
|
|
19
|
-
ENVIRONMENT_KEYS = [:environment, :deployed, :released, :deployedOrReleased]
|
|
20
|
-
ALL_KEYS = BRANCH_KEYS + ENVIRONMENT_KEYS
|
|
21
|
-
|
|
22
|
-
SCHEMA = Dry::Validation.Schema do
|
|
23
|
-
configure do
|
|
24
|
-
predicates(DryValidationPredicates)
|
|
25
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
|
26
|
-
end
|
|
8
|
+
class PactsForVerificationJSONQuerySchema < BaseContract
|
|
9
|
+
json do
|
|
10
|
+
optional(:providerVersionBranch).maybe(:string)
|
|
27
11
|
optional(:providerVersionTags).maybe(:array?)
|
|
28
|
-
optional(:consumerVersionSelectors).
|
|
29
|
-
schema do
|
|
30
|
-
# configure do
|
|
31
|
-
# def self.messages
|
|
32
|
-
# super.merge(en: { errors: { fallbackTagMustBeForLatest: 'can only be set if latest=true' }})
|
|
33
|
-
# end
|
|
34
|
-
# end
|
|
35
|
-
|
|
36
|
-
optional(:mainBranch).filled(included_in?: [true])
|
|
37
|
-
optional(:tag).filled(:str?)
|
|
38
|
-
optional(:branch).filled(:str?)
|
|
39
|
-
optional(:matchingBranch).filled(included_in?: [true])
|
|
40
|
-
optional(:latest).filled(included_in?: [true, false])
|
|
41
|
-
optional(:fallbackTag).filled(:str?)
|
|
42
|
-
optional(:fallbackBranch).filled(:str?)
|
|
43
|
-
optional(:consumer).filled(:str?, :not_blank?)
|
|
44
|
-
optional(:deployed).filled(included_in?: [true])
|
|
45
|
-
optional(:released).filled(included_in?: [true])
|
|
46
|
-
optional(:deployedOrReleased).filled(included_in?: [true])
|
|
47
|
-
optional(:environment).filled(:str?, :environment_with_name_exists?)
|
|
48
|
-
|
|
49
|
-
# rule(fallbackTagMustBeForLatest: [:fallbackTag, :latest]) do | fallback_tag, latest |
|
|
50
|
-
# fallback_tag.filled?.then(latest.eql?(true))
|
|
51
|
-
# end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
12
|
+
optional(:consumerVersionSelectors).array(:hash)
|
|
54
13
|
optional(:includePendingStatus).filled(included_in?: [true, false])
|
|
55
|
-
optional(:includeWipPactsSince).filled(:date
|
|
14
|
+
optional(:includeWipPactsSince).filled(:date)
|
|
56
15
|
end
|
|
57
16
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
17
|
+
# The original implementation of pacts-for-verification unfortunately went out without any validation on the
|
|
18
|
+
# providerVersionBranch at all (most likely unintentionally.)
|
|
19
|
+
# When we added
|
|
20
|
+
# optional(:providerVersionBranch).filled(:string)
|
|
21
|
+
# during the dry-validation upgrade, we discovered that some users/pact clients were requesting pacts for verification
|
|
22
|
+
# with an empty string in the providerVersionBranch
|
|
23
|
+
# This complicated logic tries to not break those customers as much as possible, while still raising an error
|
|
24
|
+
# when the blank string is most likely a configuration error
|
|
25
|
+
# (eg. when the request performs logic that uses the provider version branch)
|
|
26
|
+
# It allows the providerVersionBranch to be unspecified/nil, as that most likely means the user did not
|
|
27
|
+
# specify the branch at all.
|
|
28
|
+
rule(:providerVersionBranch, :providerVersionTags, :includePendingStatus, :includeWipPactsSince) do
|
|
29
|
+
branch = values[:providerVersionBranch]
|
|
30
|
+
|
|
31
|
+
# a space is a clear user error - don't bother checking further
|
|
32
|
+
if branch && branch.size > 0
|
|
33
|
+
validate_not_blank_if_present(branch, key)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if !rule_error?
|
|
37
|
+
tags = values[:providerVersionTags]
|
|
38
|
+
include_pending = values[:includePendingStatus]
|
|
39
|
+
wip = values[:includeWipPactsSince]
|
|
40
|
+
|
|
41
|
+
# There are no tags, the user specified wip or pending, and they set a branch, but the branch is an empty or blank string...
|
|
42
|
+
if !tags&.any? && (wip || include_pending) && branch && ValidationHelpers.blank?(branch)
|
|
43
|
+
# most likely a user error - return a message
|
|
44
|
+
key.failure(validation_message("pacts_for_verification_selector_provider_version_branch_empty"))
|
|
74
45
|
end
|
|
75
46
|
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def self.not_provided?(value)
|
|
79
|
-
value.nil? || value.blank?
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# when setting a tag, latest=true and latest=false are both valid
|
|
83
|
-
# when setting the branch, it doesn't make sense to verify all pacts for a branch,
|
|
84
|
-
# so latest is not required, but cannot be set to false
|
|
85
|
-
# rubocop: disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
86
|
-
def self.validate_consumer_version_selector(selector, index, params)
|
|
87
|
-
errors = []
|
|
88
|
-
|
|
89
|
-
if selector[:fallbackTag] && !selector[:latest]
|
|
90
|
-
errors << "fallbackTag can only be set if latest is true (at index #{index})"
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
if selector[:fallbackBranch] && selector[:latest] == false
|
|
94
|
-
errors << "fallbackBranch can only be set if latest is true (at index #{index})"
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
if not_provided?(selector[:mainBranch]) &&
|
|
98
|
-
not_provided?(selector[:tag]) &&
|
|
99
|
-
not_provided?(selector[:branch]) &&
|
|
100
|
-
not_provided?(selector[:environment]) &&
|
|
101
|
-
selector[:matchingBranch] != true &&
|
|
102
|
-
selector[:deployed] != true &&
|
|
103
|
-
selector[:released] != true &&
|
|
104
|
-
selector[:deployedOrReleased] != true &&
|
|
105
|
-
selector[:latest] != true
|
|
106
|
-
errors << "must specify a value for environment or tag or branch, or specify mainBranch=true, matchingBranch=true, latest=true, deployed=true, released=true or deployedOrReleased=true (at index #{index})"
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
if selector[:mainBranch] && selector.slice(*ALL_KEYS - [:consumer, :mainBranch]).any?
|
|
110
|
-
errors << "cannot specify mainBranch=true with any other criteria apart from consumer (at index #{index})"
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
if selector[:matchingBranch] && selector.slice(*ALL_KEYS - [:consumer, :matchingBranch]).any?
|
|
114
|
-
errors << "cannot specify matchingBranch=true with any other criteria apart from consumer (at index #{index})"
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
if selector[:tag] && selector[:branch]
|
|
118
|
-
errors << "cannot specify both a tag and a branch (at index #{index})"
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
if selector[:fallbackBranch] && !selector[:branch]
|
|
122
|
-
errors << "a branch must be specified when a fallbackBranch is specified (at index #{index})"
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
if selector[:fallbackTag] && !selector[:tag]
|
|
126
|
-
errors << "a tag must be specified when a fallbackTag is specified (at index #{index})"
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
if selector[:branch] && selector[:latest] == false
|
|
130
|
-
errors << "cannot specify a branch with latest=false (at index #{index})"
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
if selector[:deployed] && selector[:released]
|
|
134
|
-
errors << "cannot specify both deployed=true and released=true (at index #{index})"
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
if selector[:deployed] && selector[:deployedOrReleased]
|
|
138
|
-
errors << "cannot specify both deployed=true and deployedOrReleased=true (at index #{index})"
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
if selector[:released] && selector[:deployedOrReleased]
|
|
142
|
-
errors << "cannot specify both released=true and deployedOrReleased=true (at index #{index})"
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
if selector[:matchingBranch] && not_provided?(params[:providerVersionBranch])
|
|
146
|
-
errors << "the providerVersionBranch must be specified when the selector matchingBranch=true is used (at index #{index})"
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
non_environment_fields = selector.slice(*BRANCH_KEYS).keys.sort
|
|
150
|
-
environment_related_fields = selector.slice(*ENVIRONMENT_KEYS).keys.sort
|
|
151
|
-
|
|
152
|
-
if (non_environment_fields.any? && environment_related_fields.any?)
|
|
153
|
-
errors << "cannot specify the #{pluralize("field", non_environment_fields.count)} #{non_environment_fields.join("/")} with the #{pluralize("field", environment_related_fields.count)} #{environment_related_fields.join("/")} (at index #{index})"
|
|
154
|
-
end
|
|
155
47
|
|
|
156
|
-
errors
|
|
157
48
|
end
|
|
158
|
-
|
|
49
|
+
rule(:consumerVersionSelectors).validate(validate_each_with_contract: ConsumerVersionSelectorContract)
|
|
159
50
|
end
|
|
160
51
|
end
|
|
161
52
|
end
|
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "pact_broker/api/contracts/dry_validation_workarounds"
|
|
3
|
-
require "pact_broker/api/contracts/dry_validation_predicates"
|
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
|
4
2
|
|
|
5
3
|
module PactBroker
|
|
6
4
|
module Api
|
|
7
5
|
module Contracts
|
|
8
|
-
class PactsForVerificationQueryStringSchema
|
|
9
|
-
|
|
10
|
-
using PactBroker::HashRefinements
|
|
11
|
-
|
|
12
|
-
SCHEMA = Dry::Validation.Schema do
|
|
13
|
-
configure do
|
|
14
|
-
predicates(DryValidationPredicates)
|
|
15
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
|
16
|
-
end
|
|
6
|
+
class PactsForVerificationQueryStringSchema < BaseContract
|
|
7
|
+
params do
|
|
17
8
|
optional(:provider_version_tags).maybe(:array?)
|
|
18
9
|
optional(:consumer_version_selectors).each do
|
|
19
10
|
schema do
|
|
20
|
-
required(:tag).filled(:
|
|
11
|
+
required(:tag).filled(:string)
|
|
21
12
|
optional(:latest).filled(included_in?: ["true", "false"])
|
|
22
|
-
optional(:fallback_tag).filled(:
|
|
23
|
-
optional(:consumer).filled(:
|
|
13
|
+
optional(:fallback_tag).filled(:string)
|
|
14
|
+
optional(:consumer).filled(:string)
|
|
24
15
|
end
|
|
25
16
|
end
|
|
26
17
|
optional(:include_pending_status).filled(included_in?: ["true", "false"])
|
|
27
|
-
optional(:include_wip_pacts_since).filled(:date
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def self.call(params)
|
|
31
|
-
select_first_message(flatten_indexed_messages(SCHEMA.call(params&.symbolize_keys).messages(full: true)))
|
|
18
|
+
optional(:include_wip_pacts_since).filled(:date)
|
|
32
19
|
end
|
|
33
20
|
end
|
|
34
21
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
|
2
|
+
require "pact_broker/api/contracts/utf_8_validation"
|
|
3
|
+
|
|
4
|
+
# The contract for the contract object in the publish contracts request
|
|
5
|
+
module PactBroker
|
|
6
|
+
module Api
|
|
7
|
+
module Contracts
|
|
8
|
+
class PublishContractsContractContract < BaseContract
|
|
9
|
+
json do
|
|
10
|
+
required(:consumerName).filled(:string)
|
|
11
|
+
required(:providerName).filled(:string)
|
|
12
|
+
required(:content).filled(:string)
|
|
13
|
+
required(:contentType).filled(included_in?: ["application/json"])
|
|
14
|
+
required(:specification).filled(included_in?: ["pact"])
|
|
15
|
+
optional(:onConflict).filled(included_in?:["overwrite", "merge"])
|
|
16
|
+
optional(:decodedParsedContent) # set in the resource
|
|
17
|
+
optional(:decodedContent) # set in the resource
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
rule(:consumerName).validate(:not_blank_if_present)
|
|
21
|
+
rule(:providerName).validate(:not_blank_if_present)
|
|
22
|
+
|
|
23
|
+
# validate_consumer_name_in_content
|
|
24
|
+
rule(:decodedParsedContent, :consumerName, :specification) do
|
|
25
|
+
consumer_name_in_content = values.dig(:decodedParsedContent, :consumer, :name)
|
|
26
|
+
if consumer_name_in_content && consumer_name_in_content != values[:consumerName]
|
|
27
|
+
base.failure(validation_message("consumer_name_in_content_mismatch", { consumer_name_in_content: consumer_name_in_content, consumer_name: values[:consumerName] }))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# validate_provider_name_in_content
|
|
32
|
+
rule(:decodedParsedContent, :providerName) do
|
|
33
|
+
provider_name_in_content = values.dig(:decodedParsedContent, :provider, :name)
|
|
34
|
+
if provider_name_in_content && provider_name_in_content != values[:providerName]
|
|
35
|
+
base.failure(validation_message("provider_name_in_content_mismatch", { provider_name_in_content: provider_name_in_content, provider_name: values[:providerName] }))
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# validate_encoding
|
|
40
|
+
rule(:decodedContent) do
|
|
41
|
+
if value.nil?
|
|
42
|
+
base.failure(validation_message("base64"))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if value
|
|
46
|
+
char_number, fragment = PactBroker::Api::Contracts::UTF8Validation.fragment_before_invalid_utf_8_char(value)
|
|
47
|
+
if char_number
|
|
48
|
+
base.failure(validation_message("non_utf_8_char_in_contract", char_number: char_number, fragment: fragment))
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# validate_content_matches_content_type
|
|
54
|
+
rule(:decodedParsedContent, :contentType) do
|
|
55
|
+
if values[:decodedParsedContent].nil? && values[:contentType]
|
|
56
|
+
base.failure(validation_message("invalid_content_for_content_type", { content_type: values[:contentType] }))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -1,134 +1,35 @@
|
|
|
1
|
-
require "
|
|
2
|
-
require "pact_broker/api/contracts/
|
|
3
|
-
require "pact_broker/api/contracts/dry_validation_predicates"
|
|
4
|
-
require "pact_broker/messages"
|
|
5
|
-
require "pact_broker/api/contracts/utf_8_validation"
|
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
|
2
|
+
require "pact_broker/api/contracts/publish_contracts_contract_contract"
|
|
6
3
|
|
|
7
4
|
module PactBroker
|
|
8
5
|
module Api
|
|
9
6
|
module Contracts
|
|
10
|
-
class PublishContractsSchema
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
required(:providerName).filled(:str?, :not_blank?)
|
|
34
|
-
required(:content).filled(:str?)
|
|
35
|
-
required(:contentType).filled(included_in?: ["application/json"])
|
|
36
|
-
required(:specification).filled(included_in?: ["pact"])
|
|
37
|
-
optional(:onConflict).filled(included_in?:["overwrite", "merge"])
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def self.call(params)
|
|
42
|
-
dry_results = SCHEMA.call(params&.symbolize_keys).messages(full: true)
|
|
43
|
-
dry_results.then do | results |
|
|
44
|
-
add_cross_field_validation_errors(params&.symbolize_keys, results)
|
|
45
|
-
end.then do | results |
|
|
46
|
-
select_first_message(results)
|
|
47
|
-
end.then do | results |
|
|
48
|
-
flatten_indexed_messages(results)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def self.add_cross_field_validation_errors(params, errors)
|
|
53
|
-
if params[:contracts].is_a?(Array)
|
|
54
|
-
params[:contracts].each_with_index do | contract, i |
|
|
55
|
-
if contract.is_a?(Hash)
|
|
56
|
-
validate_consumer_name(params, contract, i, errors)
|
|
57
|
-
validate_consumer_name_in_content(params, contract, i, errors)
|
|
58
|
-
validate_provider_name_in_content(contract, i, errors)
|
|
59
|
-
validate_encoding(contract, i, errors)
|
|
60
|
-
validate_content_matches_content_type(contract, i, errors)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
errors
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def self.validate_consumer_name(params, contract, i, errors)
|
|
68
|
-
if params[:pacticipantName] && contract[:consumerName] && (contract[:consumerName] != params[:pacticipantName])
|
|
69
|
-
add_contract_error(:consumerName, validation_message("consumer_name_in_contract_mismatch_pacticipant_name", { consumer_name_in_contract: contract[:consumerName], pacticipant_name: params[:pacticipantName] } ), i, errors)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def self.validate_consumer_name_in_content(params, contract, i, errors)
|
|
74
|
-
consumer_name_in_content = contract.dig(:decodedParsedContent, :consumer, :name)
|
|
75
|
-
if consumer_name_in_content && consumer_name_in_content != params[:pacticipantName]
|
|
76
|
-
add_contract_error(:consumerName, validation_message("consumer_name_in_content_mismatch_pacticipant_name", { consumer_name_in_content: consumer_name_in_content, pacticipant_name: params[:pacticipantName] } ), i, errors)
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def self.validate_provider_name_in_content(contract, i, errors)
|
|
81
|
-
provider_name_in_content = contract.dig(:decodedParsedContent, :provider, :name)
|
|
82
|
-
if provider_name_in_content && provider_name_in_content != contract[:providerName]
|
|
83
|
-
add_contract_error(:providerName, validation_message("provider_name_in_content_mismatch", { provider_name_in_content: provider_name_in_content, provider_name: contract[:providerName] } ), i, errors)
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def self.validate_encoding(contract, i, errors)
|
|
88
|
-
if contract[:decodedContent].nil?
|
|
89
|
-
add_contract_error(:content, message("errors.base64?", scope: nil), i, errors)
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
if contract[:decodedContent]
|
|
93
|
-
char_number, fragment = fragment_before_invalid_utf_8_char(contract[:decodedContent])
|
|
94
|
-
if char_number
|
|
95
|
-
error_message = message("errors.non_utf_8_char_in_contract", char_number: char_number, fragment: fragment)
|
|
96
|
-
add_contract_error(:content, error_message, i, errors)
|
|
7
|
+
class PublishContractsSchema < BaseContract
|
|
8
|
+
json do
|
|
9
|
+
required(:pacticipantName).filled(:string)
|
|
10
|
+
required(:pacticipantVersionNumber).filled(:string)
|
|
11
|
+
optional(:tags).maybe{ array? & each { filled? } }
|
|
12
|
+
optional(:branch).maybe(:string)
|
|
13
|
+
optional(:buildUrl).maybe(:string)
|
|
14
|
+
required(:contracts).array(:hash)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
rule(:pacticipantName).validate(:not_blank_if_present)
|
|
18
|
+
rule(:pacticipantVersionNumber).validate(:not_blank_if_present, :not_multiple_lines)
|
|
19
|
+
rule(:branch).validate(:not_blank_if_present, :not_multiple_lines)
|
|
20
|
+
rule(:buildUrl).validate(:not_multiple_lines)
|
|
21
|
+
rule(:tags).validate(:array_values_not_blank_if_any)
|
|
22
|
+
|
|
23
|
+
rule(:contracts).validate(validate_each_with_contract: PublishContractsContractContract)
|
|
24
|
+
|
|
25
|
+
# validate_consumer_name_matches_pacticipant_name
|
|
26
|
+
rule(:contracts, :pacticipantName) do
|
|
27
|
+
values[:contracts]&.each_with_index do | contract, index |
|
|
28
|
+
if values[:pacticipantName] && contract[:consumerName] && (contract[:consumerName] != values[:pacticipantName])
|
|
29
|
+
key([:contracts, index]).failure(validation_message("consumer_name_in_contract_mismatch_pacticipant_name", { consumer_name_in_contract: contract[:consumerName], pacticipant_name: values[:pacticipantName] }))
|
|
97
30
|
end
|
|
98
31
|
end
|
|
99
32
|
end
|
|
100
|
-
|
|
101
|
-
def self.validate_content_matches_content_type(contract, i, errors)
|
|
102
|
-
if contract[:decodedParsedContent].nil? && contract[:contentType]
|
|
103
|
-
add_contract_error(:content, validation_message("invalid_content_for_content_type", { content_type: contract[:contentType]}), i, errors)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def self.add_contract_error(field, message, i, errors)
|
|
108
|
-
errors[:contracts] ||= {}
|
|
109
|
-
errors[:contracts][i] ||= {}
|
|
110
|
-
errors[:contracts][i][field] ||= []
|
|
111
|
-
errors[:contracts][i][field] << message
|
|
112
|
-
errors
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Need to fix this whole dry-validation eff up
|
|
116
|
-
def self.select_first_message(results)
|
|
117
|
-
case results
|
|
118
|
-
when Hash then results.each_with_object({}) { |(key, value), new_hash| new_hash[key] = select_first_message(value) }
|
|
119
|
-
when Array then select_first_message_from_array(results)
|
|
120
|
-
else
|
|
121
|
-
results
|
|
122
|
-
end
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
def self.select_first_message_from_array(results)
|
|
126
|
-
if results.all?{ |value| value.is_a?(String) }
|
|
127
|
-
results[0...1]
|
|
128
|
-
else
|
|
129
|
-
results.collect { |value| select_first_message(value) }
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
33
|
end
|
|
133
34
|
end
|
|
134
35
|
end
|
|
@@ -1,45 +1,40 @@
|
|
|
1
1
|
require "pact_broker/api/contracts/base_contract"
|
|
2
|
+
require "pact_broker/api/contracts/validation_helpers"
|
|
2
3
|
|
|
3
4
|
module PactBroker
|
|
4
5
|
module Api
|
|
5
6
|
module Contracts
|
|
6
7
|
class PutPacticipantNameContract < BaseContract
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
json do
|
|
9
|
+
required(:name).maybe(:string)
|
|
10
|
+
required(:name_in_pact).maybe(:string)
|
|
11
|
+
end
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
rule(:name, :name_in_pact) do
|
|
14
|
+
if name_in_pact_does_not_match_name_in_url_path?(values)
|
|
15
|
+
key.failure(validation_message("pact_name_in_path_mismatch_name_in_pact", name_in_pact: values[:name_in_pact], name_in_path: values[:name]))
|
|
15
16
|
end
|
|
17
|
+
end
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
rule(name_in_path_matches_name_in_pact?: [:name, :name_in_pact]) do |name, name_in_pact|
|
|
21
|
-
name_in_pact.filled?.then(name.eql?(value(:name_in_pact)))
|
|
22
|
-
end
|
|
19
|
+
def name_in_pact_does_not_match_name_in_url_path?(values)
|
|
20
|
+
provided?(values[:name_in_pact]) && values[:name] != values[:name_in_pact]
|
|
23
21
|
end
|
|
24
22
|
end
|
|
25
23
|
|
|
26
24
|
class PutPactParamsContract < BaseContract
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
json do
|
|
26
|
+
required(:consumer).filled(:hash)
|
|
27
|
+
required(:provider).filled(:hash)
|
|
28
|
+
required(:consumer_version_number).filled(:string)
|
|
29
|
+
end
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
|
31
|
+
rule(:consumer).validate(validate_with_contract: PutPacticipantNameContract)
|
|
32
|
+
rule(:provider).validate(validate_with_contract: PutPacticipantNameContract)
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
return true if PactBroker.configuration.order_versions_by_date
|
|
37
|
-
parsed_version_number = PactBroker.configuration.version_parser.call(value)
|
|
38
|
-
!parsed_version_number.nil?
|
|
39
|
-
end
|
|
40
|
-
end
|
|
34
|
+
rule(:consumer_version_number).validate(:not_blank_if_present)
|
|
41
35
|
|
|
42
|
-
|
|
36
|
+
rule(:consumer_version_number) do
|
|
37
|
+
validate_version_number(value, key) if !rule_error?(:consumer_version_number)
|
|
43
38
|
end
|
|
44
39
|
end
|
|
45
40
|
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require "pact_broker/services"
|
|
2
|
+
require "pact_broker/string_refinements"
|
|
3
|
+
require "pact_broker/configuration"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
module PactBroker
|
|
7
|
+
module Api
|
|
8
|
+
module Contracts
|
|
9
|
+
module ValidationHelpers
|
|
10
|
+
extend self
|
|
11
|
+
using PactBroker::StringRefinements
|
|
12
|
+
|
|
13
|
+
def multiple_lines?(value)
|
|
14
|
+
value && value.is_a?(String) && value.include?("\n")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def includes_space?(value)
|
|
18
|
+
value && value.is_a?(String) && value.include?(" ")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @return true if there is a value present, and it only contains whitespace
|
|
22
|
+
def blank?(value)
|
|
23
|
+
value&.blank?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The tins gem has screwed up the present? method by not using refinements
|
|
27
|
+
# Return true if the object is not nil, and if a String, is not blank.
|
|
28
|
+
# @param [Object]
|
|
29
|
+
def provided?(value)
|
|
30
|
+
if value.is_a?(String)
|
|
31
|
+
value.strip.size > 0
|
|
32
|
+
else
|
|
33
|
+
!value.nil?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def not_provided?(value)
|
|
38
|
+
!provided?(value)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def valid_url?(url)
|
|
42
|
+
URI(url)
|
|
43
|
+
rescue URI::InvalidURIError, ArgumentError
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def valid_http_method?(http_method)
|
|
48
|
+
Net::HTTP.const_defined?(http_method.capitalize)
|
|
49
|
+
rescue StandardError
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def pacticipant_with_name_exists?(value)
|
|
54
|
+
PactBroker::Services.pacticipant_service.find_pacticipant_by_name(value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def environment_with_name_exists?(value)
|
|
58
|
+
PactBroker::Services.environment_service.find_by_name(value)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def valid_version_number?(value)
|
|
62
|
+
if PactBroker.configuration.order_versions_by_date
|
|
63
|
+
true
|
|
64
|
+
else
|
|
65
|
+
!!PactBroker.configuration.version_parser.call(value)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|