pact_broker 2.106.0 → 2.107.0.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 +6 -17
- metadata +56 -72
- 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
|