pact_broker 2.105.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 +57 -1
- data/Gemfile +3 -0
- data/README.md +1 -1
- data/db/migrations/20221130_add_provider_version_id_index_to_verifications.rb +28 -0
- data/db/migrations/20221208_add_index_to_pact_version_provider_tag_successful_verifications.rb +21 -0
- data/db/migrations/20221215_add_prov_ver_id_ndx_to_latest_verifi_id_for_pact_ver_and_prov_ver.rb +21 -0
- data/db/migrations/20230131_add_cons_ver_id_ndx_to_latest_pp_id_for_cons_ver.rb +21 -0
- 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/docs/CONFIGURATION.md +1 -1
- data/docs/api/PAGINATION.md +43 -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 -112
- data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +21 -26
- data/lib/pact_broker/api/contracts/utf_8_validation.rb +19 -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 -170
- 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 +36 -0
- 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 +5 -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 +11 -0
- data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +34 -0
- data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +66 -0
- 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/badge_methods.rb +1 -1
- data/lib/pact_broker/api/resources/base_resource.rb +31 -68
- 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 +57 -0
- data/lib/pact_broker/api/resources/error_response_generator.rb +70 -0
- 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 +2 -2
- 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_version.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 +6 -3
- data/lib/pact_broker/api/resources/pacticipants_for_label.rb +1 -1
- data/lib/pact_broker/api/resources/pagination_methods.rb +8 -4
- 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 +6 -13
- 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 -13
- data/lib/pact_broker/app.rb +0 -2
- data/lib/pact_broker/application_context.rb +4 -4
- 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 +67 -59
- 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 +2 -2
- data/lib/pact_broker/integrations/integration.rb +1 -0
- data/lib/pact_broker/locale/en.yml +36 -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 +16 -14
- data/lib/pact_broker/pacticipants/service.rb +11 -8
- data/lib/pact_broker/pacts/pact_params.rb +6 -17
- data/lib/pact_broker/pacts/pact_version.rb +1 -0
- data/lib/pact_broker/pacts/selected_pact.rb +4 -0
- data/lib/pact_broker/policies.rb +4 -4
- data/lib/pact_broker/repositories/helpers.rb +11 -0
- data/lib/pact_broker/repositories/page.rb +24 -0
- data/lib/pact_broker/string_refinements.rb +4 -0
- data/lib/pact_broker/tasks/clean_task.rb +7 -3
- 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/ui/views/matrix/show.haml +1 -1
- 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/repository.rb +14 -11
- 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/versions/repository.rb +12 -0
- data/lib/pact_broker/versions/service.rb +4 -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/request_target.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 +15 -0
- data/lib/webmachine/describe_routes.rb +35 -8
- data/lib/webmachine/render_error_monkey_patch.rb +70 -0
- data/pact_broker.gemspec +7 -18
- metadata +72 -76
- 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/error_response_body_generator.rb +0 -41
- 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
- data/lib/rack/pact_broker/convert_404_to_hal.rb +0 -20
@@ -1,121 +1,34 @@
|
|
1
|
-
require "
|
2
|
-
require "pact_broker/api/contracts/
|
3
|
-
require "pact_broker/api/contracts/dry_validation_predicates"
|
4
|
-
require "pact_broker/messages"
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
2
|
+
require "pact_broker/api/contracts/publish_contracts_contract_contract"
|
5
3
|
|
6
4
|
module PactBroker
|
7
5
|
module Api
|
8
6
|
module Contracts
|
9
|
-
class PublishContractsSchema
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
optional(:onConflict).filled(included_in?:["overwrite", "merge"])
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.call(params)
|
37
|
-
dry_results = SCHEMA.call(params&.symbolize_keys).messages(full: true)
|
38
|
-
dry_results.then do | results |
|
39
|
-
add_cross_field_validation_errors(params&.symbolize_keys, results)
|
40
|
-
end.then do | results |
|
41
|
-
select_first_message(results)
|
42
|
-
end.then do | results |
|
43
|
-
flatten_indexed_messages(results)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.add_cross_field_validation_errors(params, errors)
|
48
|
-
if params[:contracts].is_a?(Array)
|
49
|
-
params[:contracts].each_with_index do | contract, i |
|
50
|
-
if contract.is_a?(Hash)
|
51
|
-
validate_consumer_name(params, contract, i, errors)
|
52
|
-
validate_consumer_name_in_content(params, contract, i, errors)
|
53
|
-
validate_provider_name_in_content(contract, i, errors)
|
54
|
-
validate_encoding(contract, i, errors)
|
55
|
-
validate_content_matches_content_type(contract, i, errors)
|
56
|
-
end
|
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] }))
|
57
30
|
end
|
58
31
|
end
|
59
|
-
errors
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.validate_consumer_name(params, contract, i, errors)
|
63
|
-
if params[:pacticipantName] && contract[:consumerName] && (contract[:consumerName] != params[:pacticipantName])
|
64
|
-
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)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.validate_consumer_name_in_content(params, contract, i, errors)
|
69
|
-
consumer_name_in_content = contract.dig(:decodedParsedContent, :consumer, :name)
|
70
|
-
if consumer_name_in_content && consumer_name_in_content != params[:pacticipantName]
|
71
|
-
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)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.validate_provider_name_in_content(contract, i, errors)
|
76
|
-
provider_name_in_content = contract.dig(:decodedParsedContent, :provider, :name)
|
77
|
-
if provider_name_in_content && provider_name_in_content != contract[:providerName]
|
78
|
-
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)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.validate_encoding(contract, i, errors)
|
83
|
-
if contract[:decodedContent].nil?
|
84
|
-
add_contract_error(:content, message("errors.base64?", scope: nil), i, errors)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.validate_content_matches_content_type(contract, i, errors)
|
89
|
-
if contract[:decodedParsedContent].nil? && contract[:contentType]
|
90
|
-
add_contract_error(:content, validation_message("invalid_content_for_content_type", { content_type: contract[:contentType]}), i, errors)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
|
-
def self.add_contract_error(field, message, i, errors)
|
96
|
-
errors[:contracts] ||= {}
|
97
|
-
errors[:contracts][i] ||= {}
|
98
|
-
errors[:contracts][i][field] ||= []
|
99
|
-
errors[:contracts][i][field] << message
|
100
|
-
errors
|
101
|
-
end
|
102
|
-
|
103
|
-
# Need to fix this whole dry-validation eff up
|
104
|
-
def self.select_first_message(results)
|
105
|
-
case results
|
106
|
-
when Hash then results.each_with_object({}) { |(key, value), new_hash| new_hash[key] = select_first_message(value) }
|
107
|
-
when Array then select_first_message_from_array(results)
|
108
|
-
else
|
109
|
-
results
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.select_first_message_from_array(results)
|
114
|
-
if results.all?{ |value| value.is_a?(String) }
|
115
|
-
results[0...1]
|
116
|
-
else
|
117
|
-
results.collect { |value| select_first_message(value) }
|
118
|
-
end
|
119
32
|
end
|
120
33
|
end
|
121
34
|
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,19 @@
|
|
1
|
+
module PactBroker
|
2
|
+
module Api
|
3
|
+
module Contracts
|
4
|
+
module UTF8Validation
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def fragment_before_invalid_utf_8_char(string)
|
8
|
+
string.force_encoding("UTF-8").each_char.with_index do | char, index |
|
9
|
+
if !char.valid_encoding?
|
10
|
+
fragment = index < 100 ? string[0...index] : string[index-100...index]
|
11
|
+
return index + 1, fragment
|
12
|
+
end
|
13
|
+
end
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
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
|
@@ -1,41 +1,22 @@
|
|
1
1
|
require "pact_broker/api/contracts/base_contract"
|
2
|
-
require "uri"
|
3
2
|
|
4
3
|
module PactBroker
|
5
4
|
module Api
|
6
5
|
module Contracts
|
7
6
|
class VerificationContract < BaseContract
|
7
|
+
json do
|
8
|
+
required(:success).filled(:bool)
|
9
|
+
required(:providerApplicationVersion).filled(:string)
|
10
|
+
optional(:buildUrl).maybe(:string)
|
11
|
+
end
|
8
12
|
|
9
|
-
|
10
|
-
property :provider_version, as: :providerApplicationVersion
|
11
|
-
property :build_url, as: :buildUrl
|
12
|
-
|
13
|
-
validation do
|
14
|
-
configure do
|
15
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
16
|
-
|
17
|
-
def not_blank? value
|
18
|
-
value && value.to_s.strip.size > 0
|
19
|
-
end
|
20
|
-
|
21
|
-
def valid_url? url
|
22
|
-
URI(url)
|
23
|
-
rescue URI::InvalidURIError, ArgumentError
|
24
|
-
nil
|
25
|
-
end
|
26
|
-
|
27
|
-
def valid_version_number?(value)
|
28
|
-
return true if PactBroker.configuration.order_versions_by_date
|
29
|
-
|
30
|
-
parsed_version_number = PactBroker.configuration.version_parser.call(value)
|
31
|
-
!!parsed_version_number
|
32
|
-
end
|
33
|
-
end
|
13
|
+
rule(:providerApplicationVersion).validate(:not_blank_if_present)
|
34
14
|
|
35
|
-
|
36
|
-
|
37
|
-
optional(:build_url).maybe(:valid_url?)
|
15
|
+
rule(:providerApplicationVersion) do
|
16
|
+
validate_version_number(value, key) unless rule_error?(:providerApplicationVersion)
|
38
17
|
end
|
18
|
+
|
19
|
+
rule(:buildUrl).validate(:valid_url_if_present)
|
39
20
|
end
|
40
21
|
end
|
41
22
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require "pact_broker/api/contracts/base_contract"
|
2
|
-
require "pact_broker/
|
3
|
-
require "pact_broker/
|
4
|
-
require "pact_broker/pacticipants/service"
|
2
|
+
require "pact_broker/api/contracts/webhook_request_contract"
|
3
|
+
require "pact_broker/api/contracts/webhook_pacticipant_contract"
|
5
4
|
require "pact_broker/webhooks/webhook_event"
|
6
5
|
|
7
6
|
module PactBroker
|
@@ -9,180 +8,31 @@ module PactBroker
|
|
9
8
|
module Contracts
|
10
9
|
class WebhookContract < BaseContract
|
11
10
|
|
12
|
-
|
13
|
-
result = super
|
14
|
-
# I just cannot seem to get the validation to stop on the first error.
|
15
|
-
# If one rule fails, they all come back failed, and it's driving me nuts.
|
16
|
-
# Why on earth would I want that behaviour?
|
17
|
-
# I cannot believe I have to do this shit.
|
18
|
-
@first_errors = errors
|
19
|
-
@first_errors.messages.keys.each do | key |
|
20
|
-
@first_errors.messages[key] = @first_errors.messages[key][0...1]
|
21
|
-
end
|
22
|
-
|
23
|
-
# rubocop: disable Lint/NestedMethodDefinition
|
24
|
-
def self.errors; @first_errors end
|
25
|
-
# rubocop: enable Lint/NestedMethodDefinition
|
26
|
-
|
27
|
-
result
|
28
|
-
end
|
29
|
-
|
30
|
-
validation do
|
31
|
-
configure do
|
32
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
33
|
-
end
|
34
|
-
|
35
|
-
optional(:consumer)
|
36
|
-
optional(:provider)
|
37
|
-
required(:request).filled
|
38
|
-
optional(:events).maybe(min_size?: 1)
|
39
|
-
end
|
40
|
-
|
41
|
-
property :consumer do
|
42
|
-
property :name
|
43
|
-
property :label
|
44
|
-
|
45
|
-
validation do
|
46
|
-
configure do
|
47
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
48
|
-
|
49
|
-
def pacticipant_exists?(name)
|
50
|
-
!!PactBroker::Pacticipants::Service.find_pacticipant_by_name(name)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
optional(:name)
|
55
|
-
.maybe(:pacticipant_exists?)
|
56
|
-
.when(:none?) { value(:label).filled? }
|
57
|
-
|
58
|
-
optional(:label)
|
59
|
-
.maybe(:str?)
|
60
|
-
.when(:none?) { value(:name).filled? }
|
61
|
-
|
62
|
-
rule(label: [:name, :label]) do |name, label|
|
63
|
-
(name.filled? & label.filled?) > label.none?
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
property :provider do
|
69
|
-
property :name
|
70
|
-
property :label
|
71
|
-
|
72
|
-
validation do
|
73
|
-
configure do
|
74
|
-
config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
|
75
|
-
|
76
|
-
def pacticipant_exists?(name)
|
77
|
-
!!PactBroker::Pacticipants::Service.find_pacticipant_by_name(name)
|
78
|
-
end
|
79
|
-
end
|
11
|
+
UUID_REGEX = /^[A-Za-z0-9_\-]{16,}$/
|
80
12
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
optional(:label)
|
86
|
-
.maybe(:str?)
|
87
|
-
.when(:none?) { value(:name).filled? }
|
88
|
-
|
89
|
-
rule(label: [:name, :label]) do |name, label|
|
90
|
-
(name.filled? & label.filled?) > label.none?
|
91
|
-
end
|
13
|
+
class EventContract < BaseContract
|
14
|
+
json do
|
15
|
+
required(:name).filled(included_in?: PactBroker::Webhooks::WebhookEvent::EVENT_NAMES)
|
92
16
|
end
|
93
17
|
end
|
94
18
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
def self.messages
|
104
|
-
super.merge(
|
105
|
-
en: {
|
106
|
-
errors: {
|
107
|
-
allowed_webhook_method?: http_method_error_message,
|
108
|
-
allowed_webhook_scheme?: scheme_error_message,
|
109
|
-
allowed_webhook_host?: host_error_message
|
110
|
-
}
|
111
|
-
}
|
112
|
-
)
|
113
|
-
end
|
114
|
-
|
115
|
-
def self.http_method_error_message
|
116
|
-
if PactBroker.configuration.webhook_http_method_whitelist.size == 1
|
117
|
-
"must be #{PactBroker.configuration.webhook_http_method_whitelist.first}. See /doc/webhooks#whitelist for more information."
|
118
|
-
else
|
119
|
-
"must be one of #{PactBroker.configuration.webhook_http_method_whitelist.join(", ")}. See /doc/webhooks#whitelist for more information."
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def self.scheme_error_message
|
124
|
-
"scheme must be #{PactBroker.configuration.webhook_scheme_whitelist.join(", ")}. See /doc/webhooks#whitelist for more information."
|
125
|
-
end
|
126
|
-
|
127
|
-
def self.host_error_message
|
128
|
-
"host must be in the whitelist #{PactBroker.configuration.webhook_host_whitelist.join(",")}. See /doc/webhooks#whitelist for more information."
|
129
|
-
end
|
130
|
-
|
131
|
-
def valid_method?(http_method)
|
132
|
-
Net::HTTP.const_defined?(http_method.capitalize)
|
133
|
-
end
|
134
|
-
|
135
|
-
def valid_url?(url)
|
136
|
-
uri = parse_uri(url)
|
137
|
-
uri.scheme && uri.host
|
138
|
-
rescue URI::InvalidURIError, ArgumentError
|
139
|
-
nil
|
140
|
-
end
|
141
|
-
|
142
|
-
def allowed_webhook_method?(http_method)
|
143
|
-
PactBroker.configuration.webhook_http_method_whitelist.any? do | allowed_method |
|
144
|
-
http_method.downcase == allowed_method.downcase
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def allowed_webhook_scheme?(url)
|
149
|
-
scheme = parse_uri(url).scheme
|
150
|
-
PactBroker.configuration.webhook_scheme_whitelist.any? do | allowed_scheme |
|
151
|
-
scheme.downcase == allowed_scheme.downcase
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
def allowed_webhook_host?(url)
|
156
|
-
if host_whitelist.any?
|
157
|
-
PactBroker::Webhooks::CheckHostWhitelist.call(parse_uri(url).host, host_whitelist).any?
|
158
|
-
else
|
159
|
-
true
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def non_templated_host?(url)
|
164
|
-
parse_uri(url).host == parse_uri(url, "differentplaceholder").host
|
165
|
-
end
|
166
|
-
|
167
|
-
def host_whitelist
|
168
|
-
PactBroker.configuration.webhook_host_whitelist
|
169
|
-
end
|
170
|
-
|
171
|
-
def parse_uri(uri_string, placeholder = "placeholder")
|
172
|
-
URI(PactBroker::Webhooks::Render.render_with_placeholder(uri_string, placeholder))
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
required(:http_method).filled(:valid_method?, :allowed_webhook_method?)
|
177
|
-
required(:url).filled(:valid_url?, :allowed_webhook_scheme?, :allowed_webhook_host?, :non_templated_host?)
|
178
|
-
end
|
19
|
+
json do
|
20
|
+
optional(:uuid).maybe(:string) # set in resource class from the path info - doesn't come in in the request body
|
21
|
+
optional(:consumer).maybe(:hash)
|
22
|
+
optional(:provider).maybe(:hash)
|
23
|
+
required(:request).filled(:hash)
|
24
|
+
optional(:events).maybe(min_size?: 1)
|
25
|
+
optional(:enabled).filled(:bool)
|
179
26
|
end
|
180
27
|
|
181
|
-
|
182
|
-
|
28
|
+
rule(:consumer).validate(validate_with_contract: WebhookPacticipantContract)
|
29
|
+
rule(:provider).validate(validate_with_contract: WebhookPacticipantContract)
|
30
|
+
rule(:request).validate(validate_with_contract: WebhookRequestContract)
|
31
|
+
rule(:events).validate(validate_each_with_contract: EventContract)
|
183
32
|
|
184
|
-
|
185
|
-
|
33
|
+
rule(:uuid) do
|
34
|
+
if value && !(value =~ UUID_REGEX)
|
35
|
+
key.failure(validation_message("invalid_webhook_uuid"))
|
186
36
|
end
|
187
37
|
end
|
188
38
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "pact_broker/api/contracts/base_contract"
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Api
|
5
|
+
module Contracts
|
6
|
+
class WebhookPacticipantContract < BaseContract
|
7
|
+
json do
|
8
|
+
optional(:name).maybe(:string)
|
9
|
+
optional(:label).maybe(:string)
|
10
|
+
end
|
11
|
+
|
12
|
+
register_macro(:name_or_label_required) do
|
13
|
+
if !provided?(values[:name]) && !provided?(values[:label])
|
14
|
+
key(path.keys).failure(validation_message("blank"))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
register_macro(:name_and_label_exclusive) do
|
19
|
+
if provided?(values[:name]) && provided?(values[:label])
|
20
|
+
key([:label]).failure(validation_message("cannot_be_provided_at_same_time", name_1: "name", name_2: "label"))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
rule(:name, :label).validate(:name_or_label_required)
|
25
|
+
rule(:name, :label).validate(:name_and_label_exclusive)
|
26
|
+
|
27
|
+
rule(:name) do
|
28
|
+
validate_pacticipant_with_name_exists(value, key) if provided?(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|