pact_broker 2.107.1 → 2.108.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/Gemfile +5 -4
- data/README.md +1 -0
- data/db/migrations/20230615_add_integrations_contract_data_updated_at.rb +13 -0
- data/db/migrations/20230616_set_integrations_contract_data_updated_at.rb +11 -0
- data/db/migrations/20231002_add_version_id_index_to_released_version.rb +21 -0
- data/db/migrations/20231003_add_version_id_index_to_deployed_version.rb +21 -0
- data/docs/CONFIGURATION.md +10 -0
- data/lib/pact_broker/api/contracts/base_contract.rb +2 -1
- data/lib/pact_broker/api/contracts/dry_validation_errors_formatter.rb +2 -0
- data/lib/pact_broker/api/contracts/pagination_query_params_schema.rb +19 -0
- data/lib/pact_broker/api/contracts/publish_contracts_contract_contract.rb +29 -18
- data/lib/pact_broker/api/decorators/base_decorator.rb +40 -1
- data/lib/pact_broker/api/decorators/branch_decorator.rb +35 -0
- data/lib/pact_broker/api/decorators/branch_version_decorator.rb +16 -0
- data/lib/pact_broker/api/decorators/configuration.rb +19 -0
- data/lib/pact_broker/api/decorators/custom_error_problem_json_decorator.rb +1 -1
- data/lib/pact_broker/api/decorators/decorator_context_creator.rb +47 -2
- data/lib/pact_broker/api/decorators/deployed_version_decorator.rb +2 -1
- data/lib/pact_broker/api/decorators/deployed_versions_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/dry_validation_errors_decorator.rb +32 -0
- data/lib/pact_broker/api/decorators/dry_validation_errors_problem_json_decorator.rb +24 -0
- data/lib/pact_broker/api/decorators/embedded_branch_version_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/embedded_deployed_version_decorator.rb +30 -0
- data/lib/pact_broker/api/decorators/embedded_error_problem_json_decorator.rb +84 -0
- data/lib/pact_broker/api/decorators/embedded_released_version_decorator.rb +27 -0
- data/lib/pact_broker/api/decorators/embedded_version_decorator.rb +0 -3
- data/lib/pact_broker/api/decorators/error_decorator.rb +30 -0
- data/lib/pact_broker/api/decorators/extended_pact_decorator.rb +6 -1
- data/lib/pact_broker/api/decorators/integration_decorator.rb +3 -2
- data/lib/pact_broker/api/decorators/integrations_decorator.rb +3 -0
- data/lib/pact_broker/api/decorators/notices_decorator.rb +11 -0
- data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +1 -5
- data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/pact_webhooks_status_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/pacticipant_branches_decorator.rb +32 -0
- data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +11 -1
- data/lib/pact_broker/api/decorators/{pacticipant_collection_decorator.rb → pacticipants_decorator.rb} +8 -4
- data/lib/pact_broker/api/decorators/pagination_links.rb +2 -2
- data/lib/pact_broker/api/decorators/released_versions_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/runtime_error_problem_json_decorator.rb +2 -2
- data/lib/pact_broker/api/decorators/validation_errors_decorator.rb +30 -0
- data/lib/pact_broker/api/decorators/validation_errors_problem_json_decorator.rb +4 -6
- data/lib/pact_broker/api/decorators/version_decorator.rb +5 -3
- data/lib/pact_broker/api/decorators/versions_decorator.rb +24 -14
- data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +2 -0
- data/lib/pact_broker/api/middleware/configuration.rb +2 -0
- data/lib/pact_broker/api/pact_broker_urls.rb +18 -2
- data/lib/pact_broker/api/resources/after_reply.rb +15 -0
- data/lib/pact_broker/api/resources/all_webhooks.rb +3 -3
- data/lib/pact_broker/api/resources/badge_methods.rb +2 -1
- data/lib/pact_broker/api/resources/base_resource.rb +6 -4
- data/lib/pact_broker/api/resources/branch.rb +40 -0
- data/lib/pact_broker/api/resources/branch_versions.rb +59 -0
- data/lib/pact_broker/api/resources/can_i_deploy_pacticipant_version_by_branch_to_environment_badge.rb +1 -1
- data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +1 -1
- data/lib/pact_broker/api/resources/currently_supported_versions_for_environment.rb +1 -1
- data/lib/pact_broker/api/resources/dashboard.rb +10 -0
- data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +1 -1
- data/lib/pact_broker/api/resources/environments.rb +1 -1
- data/lib/pact_broker/api/resources/error_handler.rb +18 -52
- data/lib/pact_broker/api/resources/error_handling_methods.rb +40 -14
- data/lib/pact_broker/api/resources/error_response_generator.rb +23 -6
- data/lib/pact_broker/api/resources/event_methods.rb +15 -0
- data/lib/pact_broker/api/resources/filter_methods.rb +15 -0
- data/lib/pact_broker/api/resources/group.rb +11 -2
- data/lib/pact_broker/api/resources/index.rb +6 -0
- data/lib/pact_broker/api/resources/integrations.rb +18 -4
- data/lib/pact_broker/api/resources/latest_version.rb +2 -0
- data/lib/pact_broker/api/resources/pact.rb +11 -6
- data/lib/pact_broker/api/resources/pacticipant_branches.rb +67 -0
- data/lib/pact_broker/api/resources/pacticipants.rb +26 -7
- data/lib/pact_broker/api/resources/pacticipants_for_label.rb +2 -2
- data/lib/pact_broker/api/resources/pagination_methods.rb +11 -1
- data/lib/pact_broker/api/resources/verifications.rb +9 -4
- data/lib/pact_broker/api/resources/versions.rb +12 -0
- data/lib/pact_broker/api.rb +5 -0
- data/lib/pact_broker/app.rb +10 -4
- data/lib/pact_broker/application_context.rb +29 -25
- data/lib/pact_broker/async/after_reply.rb +30 -0
- data/lib/pact_broker/config/runtime_configuration.rb +9 -21
- data/lib/pact_broker/config/runtime_configuration_coercion_methods.rb +32 -2
- data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
- data/lib/pact_broker/config/runtime_configuration_logging_methods.rb +15 -5
- data/lib/pact_broker/configuration.rb +29 -12
- data/lib/pact_broker/contracts/contracts_to_publish.rb +8 -0
- data/lib/pact_broker/contracts/service.rb +7 -1
- data/lib/pact_broker/dataset/page.rb +22 -0
- data/lib/pact_broker/dataset.rb +122 -0
- data/lib/pact_broker/db/data_migrations/set_contract_data_updated_at_for_integrations.rb +47 -0
- data/lib/pact_broker/db/migrate_data.rb +1 -0
- data/lib/pact_broker/db/models.rb +1 -1
- data/lib/pact_broker/deployments/currently_deployed_version_id.rb +2 -5
- data/lib/pact_broker/deployments/deployed_version.rb +3 -3
- data/lib/pact_broker/deployments/environment.rb +0 -2
- data/lib/pact_broker/deployments/released_version.rb +4 -5
- data/lib/pact_broker/doc/views/index/pacticipant-branch.markdown +25 -0
- data/lib/pact_broker/domain/label.rb +6 -3
- data/lib/pact_broker/domain/pact.rb +10 -5
- data/lib/pact_broker/domain/pacticipant.rb +4 -34
- data/lib/pact_broker/domain/tag.rb +4 -5
- data/lib/pact_broker/domain/verification.rb +2 -4
- data/lib/pact_broker/domain/version.rb +12 -19
- data/lib/pact_broker/errors/error_reporter.rb +30 -0
- data/lib/pact_broker/errors.rb +2 -15
- data/lib/pact_broker/events/subscriber.rb +12 -4
- data/lib/pact_broker/feature_toggle.rb +1 -1
- data/lib/pact_broker/groups/service.rb +38 -5
- data/lib/pact_broker/index/service.rb +1 -2
- data/lib/pact_broker/integrations/event_listener.rb +23 -0
- data/lib/pact_broker/integrations/integration.rb +24 -2
- data/lib/pact_broker/integrations/repository.rb +34 -1
- data/lib/pact_broker/integrations/service.rb +17 -18
- data/lib/pact_broker/labels/repository.rb +4 -8
- data/lib/pact_broker/locale/en.yml +5 -0
- data/lib/pact_broker/matrix/every_row.rb +58 -40
- data/lib/pact_broker/matrix/integration_row.rb +95 -0
- data/lib/pact_broker/matrix/integrations_repository.rb +133 -0
- data/lib/pact_broker/matrix/matrix_row.rb +88 -0
- data/lib/pact_broker/matrix/matrix_row_dataset_module.rb +185 -0
- data/lib/pact_broker/matrix/matrix_row_instance_methods.rb +150 -0
- data/lib/pact_broker/matrix/matrix_row_verification_dataset_module.rb +83 -0
- data/lib/pact_broker/matrix/parse_query.rb +1 -0
- data/lib/pact_broker/matrix/repository.rb +62 -285
- data/lib/pact_broker/matrix/resolved_selector.rb +13 -4
- data/lib/pact_broker/matrix/resolved_selector_builder.rb +84 -0
- data/lib/pact_broker/matrix/resolved_selectors_builder.rb +39 -0
- data/lib/pact_broker/matrix/row_ignorer.rb +36 -0
- data/lib/pact_broker/matrix/selector_ignorer.rb +59 -0
- data/lib/pact_broker/matrix/selector_resolver.rb +130 -0
- data/lib/pact_broker/metrics/service.rb +4 -9
- data/lib/pact_broker/pacticipants/latest_version_for_pacticipant_eager_loader.rb +33 -0
- data/lib/pact_broker/pacticipants/repository.rb +7 -9
- data/lib/pact_broker/pacticipants/service.rb +2 -2
- data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +2 -4
- data/lib/pact_broker/pacts/metadata.rb +3 -1
- data/lib/pact_broker/pacts/pact_publication.rb +23 -5
- data/lib/pact_broker/pacts/pact_publication_dataset_module.rb +5 -1
- data/lib/pact_broker/pacts/pact_version.rb +2 -3
- data/lib/pact_broker/pacts/pacts_for_verification_repository.rb +2 -5
- data/lib/pact_broker/pacts/placeholder_pact.rb +3 -1
- data/lib/pact_broker/pacts/repository.rb +12 -12
- data/lib/pact_broker/pacts/service.rb +1 -1
- data/lib/pact_broker/repositories.rb +9 -1
- data/lib/pact_broker/string_refinements.rb +4 -0
- data/lib/pact_broker/tags/head_pact_tags.rb +2 -5
- data/lib/pact_broker/tags/repository.rb +3 -7
- data/lib/pact_broker/test/test_data_builder.rb +50 -1
- data/lib/pact_broker/ui/controllers/groups.rb +2 -1
- data/lib/pact_broker/ui/view_models/matrix_line.rb +4 -4
- data/lib/pact_broker/ui/views/groups/show.html.erb +2 -1
- data/lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb +0 -3
- data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +2 -4
- data/lib/pact_broker/verifications/repository.rb +2 -5
- data/lib/pact_broker/verifications/sequence.rb +1 -4
- data/lib/pact_broker/verifications/service.rb +4 -0
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/branch.rb +2 -5
- data/lib/pact_broker/versions/branch_head.rb +0 -3
- data/lib/pact_broker/versions/branch_repository.rb +76 -0
- data/lib/pact_broker/versions/branch_service.rb +13 -25
- data/lib/pact_broker/versions/branch_version.rb +0 -3
- data/lib/pact_broker/versions/branch_version_repository.rb +17 -0
- data/lib/pact_broker/versions/repository.rb +19 -2
- data/lib/pact_broker/versions/sequence.rb +1 -3
- data/lib/pact_broker/versions/service.rb +4 -0
- data/lib/pact_broker/webhooks/execution.rb +3 -7
- data/lib/pact_broker/webhooks/job.rb +16 -7
- data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +2 -2
- data/lib/pact_broker/webhooks/repository.rb +0 -1
- data/lib/pact_broker/webhooks/trigger_service.rb +11 -12
- data/lib/pact_broker/webhooks/triggered_webhook.rb +3 -4
- data/lib/pact_broker/webhooks/webhook.rb +2 -2
- data/lib/pact_broker/webhooks/webhook_event.rb +2 -5
- data/lib/rack/pact_broker/add_cache_header.rb +14 -0
- data/lib/rack/pact_broker/application_context.rb +16 -0
- data/lib/rack/pact_broker/configurable_make_it_later.rb +1 -1
- data/lib/rack/pact_broker/invalid_uri_protection.rb +19 -3
- data/lib/webmachine/describe_routes.rb +55 -39
- data/lib/webmachine/render_error_monkey_patch.rb +13 -4
- data/pact_broker.gemspec +4 -4
- metadata +52 -29
- data/lib/pact_broker/api/decorators/decorator_context.rb +0 -22
- data/lib/pact_broker/matrix/query_builder.rb +0 -90
- data/lib/pact_broker/matrix/query_ids.rb +0 -40
- data/lib/pact_broker/matrix/quick_row.rb +0 -458
- data/lib/pact_broker/relationships/groupify.rb +0 -45
- data/lib/pact_broker/repositories/helpers.rb +0 -96
- data/lib/pact_broker/repositories/page.rb +0 -24
- data/lib/pact_broker/tags/tag_with_latest_flag.rb +0 -28
@@ -36,11 +36,15 @@ module PactBroker
|
|
36
36
|
def perform_with_triggered_webhook
|
37
37
|
@error_count = data[:error_count] || 0
|
38
38
|
begin
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
if triggered_webhook.webhook
|
40
|
+
webhook_execution_result = PactBroker::Webhooks::TriggerService.execute_triggered_webhook_now(triggered_webhook, webhook_options(data))
|
41
|
+
if webhook_execution_result.success?
|
42
|
+
handle_success
|
43
|
+
else
|
44
|
+
handle_failure
|
45
|
+
end
|
42
46
|
else
|
43
|
-
|
47
|
+
handle_webhook_deleted
|
44
48
|
end
|
45
49
|
rescue StandardError => e
|
46
50
|
handle_error e
|
@@ -73,19 +77,24 @@ module PactBroker
|
|
73
77
|
end
|
74
78
|
|
75
79
|
def handle_success
|
76
|
-
update_triggered_webhook_status
|
80
|
+
update_triggered_webhook_status(TriggeredWebhook::STATUS_SUCCESS)
|
77
81
|
end
|
78
82
|
|
79
83
|
def handle_failure
|
80
84
|
if reschedule_job?
|
81
85
|
reschedule_job
|
82
|
-
update_triggered_webhook_status
|
86
|
+
update_triggered_webhook_status(TriggeredWebhook::STATUS_RETRYING)
|
83
87
|
else
|
84
88
|
logger.info "Failed to execute webhook #{triggered_webhook.webhook_uuid} after #{retry_schedule.size + 1} attempts."
|
85
|
-
update_triggered_webhook_status
|
89
|
+
update_triggered_webhook_status(TriggeredWebhook::STATUS_FAILURE)
|
86
90
|
end
|
87
91
|
end
|
88
92
|
|
93
|
+
def handle_webhook_deleted
|
94
|
+
logger.info("Webhook with uuid #{triggered_webhook.webhook_uuid} cannot be executed it has been deleted. Marking triggered webhook as failed.")
|
95
|
+
update_triggered_webhook_status(TriggeredWebhook::STATUS_FAILURE)
|
96
|
+
end
|
97
|
+
|
89
98
|
def reschedule_job?
|
90
99
|
error_count < retry_schedule.size
|
91
100
|
end
|
@@ -134,7 +134,7 @@ module PactBroker
|
|
134
134
|
webhook_context[:consumer_version_tags].join(", ")
|
135
135
|
else
|
136
136
|
if pact
|
137
|
-
pact.
|
137
|
+
pact.consumer_version_tag_names.join(", ")
|
138
138
|
else
|
139
139
|
""
|
140
140
|
end
|
@@ -145,7 +145,7 @@ module PactBroker
|
|
145
145
|
if webhook_context.key?(:consumer_version_branch)
|
146
146
|
webhook_context[:consumer_version_branch] || ""
|
147
147
|
else
|
148
|
-
pact&.
|
148
|
+
pact&.consumer_version_branch_names&.last || ""
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|
@@ -13,11 +13,21 @@ module PactBroker
|
|
13
13
|
include PactBroker::Logging
|
14
14
|
using PactBroker::HashRefinements
|
15
15
|
|
16
|
+
# the main entry point
|
17
|
+
def create_triggered_webhooks_for_event pact, verification, event_name, event_context
|
18
|
+
webhooks = webhook_repository.find_webhooks_to_trigger(consumer: pact.consumer, provider: pact.provider, event_name: event_name)
|
19
|
+
|
20
|
+
if webhooks.any?
|
21
|
+
create_triggered_webhooks_for_webhooks(webhooks, pact, verification, event_name, event_context.merge(event_name: event_name))
|
22
|
+
else
|
23
|
+
[]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
16
27
|
def next_uuid
|
17
28
|
SecureRandom.uuid
|
18
29
|
end
|
19
30
|
|
20
|
-
# TODO support currently deployed
|
21
31
|
def test_execution webhook, event_context, execution_configuration
|
22
32
|
merged_options = execution_configuration.with_failure_log_message("Webhook execution failed").to_hash
|
23
33
|
|
@@ -36,17 +46,6 @@ module PactBroker
|
|
36
46
|
webhook_execution_result
|
37
47
|
end
|
38
48
|
|
39
|
-
# the main entry point
|
40
|
-
def create_triggered_webhooks_for_event pact, verification, event_name, event_context
|
41
|
-
webhooks = webhook_repository.find_webhooks_to_trigger(consumer: pact.consumer, provider: pact.provider, event_name: event_name)
|
42
|
-
|
43
|
-
if webhooks.any?
|
44
|
-
create_triggered_webhooks_for_webhooks(webhooks, pact, verification, event_name, event_context.merge(event_name: event_name))
|
45
|
-
else
|
46
|
-
[]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
49
|
def schedule_webhooks(triggered_webhooks, options)
|
51
50
|
triggered_webhooks.each_with_index do | triggered_webhook, i |
|
52
51
|
logger.info "Scheduling job for webhook with uuid #{triggered_webhook.webhook.uuid}, context: #{triggered_webhook.event_context}"
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require "
|
2
|
-
require "pact_broker/repositories/helpers"
|
1
|
+
require "pact_broker/dataset"
|
3
2
|
require "pact_broker/webhooks/execution"
|
4
3
|
require "pact_broker/hash_refinements"
|
5
4
|
|
@@ -23,7 +22,7 @@ module PactBroker
|
|
23
22
|
STATUS_FAILURE = "failure".freeze
|
24
23
|
|
25
24
|
dataset_module do
|
26
|
-
include PactBroker::
|
25
|
+
include PactBroker::Dataset
|
27
26
|
|
28
27
|
def delete
|
29
28
|
require "pact_broker/webhooks/execution"
|
@@ -70,7 +69,7 @@ module PactBroker
|
|
70
69
|
def execute options
|
71
70
|
# getting a random 'no method to_domain for null' error
|
72
71
|
# not sure on which object, so splitting this out into two lines
|
73
|
-
pact = pact_publication.to_domain
|
72
|
+
pact = pact_publication.with_version_branches_and_tags.to_domain
|
74
73
|
webhook.to_domain.execute(pact, verification, event_context.symbolize_keys, options)
|
75
74
|
end
|
76
75
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "
|
1
|
+
require "pact_broker/dataset"
|
2
2
|
require "pact_broker/domain/webhook"
|
3
3
|
require "pact_broker/webhooks/webhook_request_template"
|
4
4
|
require "pact_broker/domain/pacticipant"
|
@@ -15,7 +15,7 @@ module PactBroker
|
|
15
15
|
one_to_many :events, :class => "PactBroker::Webhooks::WebhookEvent", :reciprocal => :webhook
|
16
16
|
|
17
17
|
dataset_module do
|
18
|
-
include PactBroker::
|
18
|
+
include PactBroker::Dataset
|
19
19
|
|
20
20
|
# Keep the triggered webhooks after the webhook has been deleted
|
21
21
|
def delete
|
@@ -1,5 +1,4 @@
|
|
1
|
-
require "
|
2
|
-
require "pact_broker/repositories/helpers"
|
1
|
+
require "pact_broker/dataset"
|
3
2
|
|
4
3
|
module PactBroker
|
5
4
|
module Webhooks
|
@@ -17,9 +16,7 @@ module PactBroker
|
|
17
16
|
|
18
17
|
EVENT_NAMES = [CONTRACT_PUBLISHED, CONTRACT_CONTENT_CHANGED, VERIFICATION_PUBLISHED, VERIFICATION_SUCCEEDED, VERIFICATION_FAILED, CONTRACT_REQUIRING_VERIFICATION_PUBLISHED]
|
19
18
|
|
20
|
-
dataset_module
|
21
|
-
include PactBroker::Repositories::Helpers
|
22
|
-
end
|
19
|
+
dataset_module(PactBroker::Dataset)
|
23
20
|
|
24
21
|
def contract_published?
|
25
22
|
name == CONTRACT_PUBLISHED
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rack
|
2
|
+
module PactBroker
|
3
|
+
class AddCacheHeader
|
4
|
+
def initialize app
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
status, headers, body = @app.call(env)
|
10
|
+
[status, { "Cache-Control" => "no-cache" }.merge(headers || {}), body]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Sets the PactBroker::ApplicationContext on the rack env if it is not already set.
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
module PactBroker
|
5
|
+
class ApplicationContext
|
6
|
+
def initialize(app, application_context)
|
7
|
+
@app = app
|
8
|
+
@application_context = application_context
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
@app.call({ "pactbroker.application_context" => @application_context }.merge(env))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
# Allows a default Rack
|
2
|
+
# Allows a default Rack middleware implementation to be set,
|
3
3
|
# and then be optionally changed out for a different implementation
|
4
4
|
# after the app has been built.
|
5
5
|
# Used for allowing the authorization code to set after the
|
@@ -12,6 +12,8 @@ module Rack
|
|
12
12
|
class InvalidUriProtection
|
13
13
|
include ::PactBroker::Messages
|
14
14
|
|
15
|
+
CONSECUTIVE_SLASH = /\/{2,}/
|
16
|
+
|
15
17
|
def initialize app
|
16
18
|
@app = app
|
17
19
|
end
|
@@ -19,12 +21,12 @@ module Rack
|
|
19
21
|
def call env
|
20
22
|
if (uri = valid_uri?(env))
|
21
23
|
if (error_message = validate(uri))
|
22
|
-
[422,
|
24
|
+
[422, headers, [body(env, error_message, "Unprocessable", "invalid-request-parameter-value", 422)]]
|
23
25
|
else
|
24
26
|
app.call(env)
|
25
27
|
end
|
26
28
|
else
|
27
|
-
[404,
|
29
|
+
[404, headers, [body(env, "Empty path component found", "Not Found", "not-found", 404)]]
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
@@ -34,7 +36,9 @@ module Rack
|
|
34
36
|
|
35
37
|
def valid_uri? env
|
36
38
|
begin
|
37
|
-
parse(::Rack::Request.new(env).url)
|
39
|
+
uri = parse(::Rack::Request.new(env).url)
|
40
|
+
return nil if CONSECUTIVE_SLASH.match(uri.path)
|
41
|
+
uri
|
38
42
|
rescue URI::InvalidURIError, ArgumentError
|
39
43
|
nil
|
40
44
|
end
|
@@ -52,6 +56,18 @@ module Rack
|
|
52
56
|
message("errors.tab_in_url_path")
|
53
57
|
end
|
54
58
|
end
|
59
|
+
|
60
|
+
def headers
|
61
|
+
{"Content-Type" => "application/problem+json"}
|
62
|
+
end
|
63
|
+
|
64
|
+
def body(env, detail, title, type, status)
|
65
|
+
env["pactbroker.application_context"]
|
66
|
+
.decorator_configuration
|
67
|
+
.class_for(:custom_error_problem_json_decorator)
|
68
|
+
.new(detail: detail, title: title, type: type, status: status)
|
69
|
+
.to_json(user_options: { base_url: env["pactbroker.base_url"] })
|
70
|
+
end
|
55
71
|
end
|
56
72
|
end
|
57
73
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require "webmachine/adapters/rack_mapped"
|
2
2
|
require "pact_broker/string_refinements"
|
3
3
|
|
4
|
+
# Code to describe the routes in a Webmachine API, including
|
5
|
+
# path, resource class, allowed methods, schemas, policy class.
|
6
|
+
# Used in tests and in the pact_broker:routes task
|
7
|
+
|
4
8
|
module Webmachine
|
5
9
|
class DescribeRoutes
|
6
10
|
using PactBroker::StringRefinements
|
@@ -12,18 +16,11 @@ module Webmachine
|
|
12
16
|
:resource_name,
|
13
17
|
:resource_class_location,
|
14
18
|
:allowed_methods,
|
15
|
-
:
|
19
|
+
:policy_names,
|
20
|
+
:policy_classes, # only used by pactflow
|
16
21
|
:schemas,
|
17
22
|
keyword_init: true) do
|
18
23
|
|
19
|
-
def [](key)
|
20
|
-
if respond_to?(key)
|
21
|
-
send(key)
|
22
|
-
else
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
24
|
def path_include?(component)
|
28
25
|
path.include?(component)
|
29
26
|
end
|
@@ -47,7 +44,7 @@ module Webmachine
|
|
47
44
|
}.merge(path_params)
|
48
45
|
|
49
46
|
rack_req = ::Rack::Request.new({ "REQUEST_METHOD" => "GET", "rack.input" => StringIO.new("") }.merge(env) )
|
50
|
-
|
47
|
+
request = Webmachine::Adapters::Rack::RackRequest.new(
|
51
48
|
rack_req.env["REQUEST_METHOD"],
|
52
49
|
path,
|
53
50
|
Webmachine::Headers.from_cgi({"HTTP_HOST" => "example.org"}.merge(env)),
|
@@ -56,13 +53,13 @@ module Webmachine
|
|
56
53
|
{},
|
57
54
|
rack_req.env
|
58
55
|
)
|
59
|
-
|
60
|
-
resource_class.new(
|
56
|
+
request.path_info = path_info
|
57
|
+
resource_class.new(request, Webmachine::Response.new)
|
61
58
|
end
|
62
59
|
end
|
63
60
|
|
64
61
|
def self.call(webmachine_applications, search_term: nil)
|
65
|
-
path_mappings = webmachine_applications.flat_map { | webmachine_application |
|
62
|
+
path_mappings = webmachine_applications.flat_map { | webmachine_application | build_routes(webmachine_application) }
|
66
63
|
|
67
64
|
if search_term
|
68
65
|
path_mappings = path_mappings.select{ |(route, _)| route[:path].include?(search_term) }
|
@@ -71,8 +68,10 @@ module Webmachine
|
|
71
68
|
path_mappings.sort_by{ | mapping | mapping[:path] }
|
72
69
|
end
|
73
70
|
|
74
|
-
|
75
|
-
|
71
|
+
# Build a Route object to describe every Webmachine route defined in the app.routes block
|
72
|
+
# @return [Array<Webmachine::DescribeRoutes::Route>]
|
73
|
+
def self.build_routes(webmachine_application)
|
74
|
+
webmachine_routes_to_describe(webmachine_application).collect do | webmachine_route |
|
76
75
|
resource_path_absolute = Pathname.new(source_location_for(webmachine_route.resource))
|
77
76
|
Route.new({
|
78
77
|
path: "/" + webmachine_route.path_spec.collect{ |part| part.is_a?(Symbol) ? ":#{part}" : part }.join("/"),
|
@@ -80,22 +79,23 @@ module Webmachine
|
|
80
79
|
resource_class: webmachine_route.resource,
|
81
80
|
resource_name: webmachine_route.instance_variable_get(:@bindings)[:resource_name],
|
82
81
|
resource_class_location: resource_path_absolute.relative_path_from(Pathname.pwd).to_s
|
83
|
-
}.merge(
|
84
|
-
end
|
82
|
+
}.merge(properties_for_webmachine_route(webmachine_route, webmachine_application.application_context)))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.webmachine_routes_to_describe(webmachine_application)
|
87
|
+
webmachine_application.routes.reject{ | route | route.resource == Webmachine::Trace::TraceResource }.collect
|
85
88
|
end
|
86
89
|
|
87
|
-
def self.
|
90
|
+
def self.properties_for_webmachine_route(webmachine_route, application_context)
|
88
91
|
with_no_logging do
|
89
92
|
path_info = { application_context: application_context, pacticipant_name: "foo", pacticipant_version_number: "1", resource_name: "foo" }
|
90
93
|
path_info.default = "1"
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
if
|
95
|
-
|
96
|
-
allowed_methods: dummy_resource.allowed_methods,
|
97
|
-
schemas: dummy_resource.respond_to?(:schema, true) && dummy_resource.send(:schema) ? schemas(dummy_resource.allowed_methods, webmachine_route.resource, path_info) : nil
|
98
|
-
}.compact
|
94
|
+
request = build_request(http_method: "GET", path_info: path_info)
|
95
|
+
|
96
|
+
resource = webmachine_route.resource.new(request, Webmachine::Response.new)
|
97
|
+
if resource
|
98
|
+
properties_for_resource(resource.allowed_methods - ["OPTIONS"], webmachine_route, application_context)
|
99
99
|
else
|
100
100
|
{}
|
101
101
|
end
|
@@ -105,22 +105,38 @@ module Webmachine
|
|
105
105
|
{}
|
106
106
|
end
|
107
107
|
|
108
|
-
#
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
108
|
+
# Return the properties of the resource that can only be determined by instantiating the resource
|
109
|
+
# @return [Hash]
|
110
|
+
def self.properties_for_resource(allowed_methods, webmachine_route, application_context)
|
111
|
+
schemas = []
|
112
|
+
policy_names = []
|
113
|
+
allowed_methods.each do | http_method |
|
114
|
+
resource = build_resource(webmachine_route, http_method, application_context)
|
115
|
+
if (schema_class = resource.respond_to?(:schema, true) && resource.send(:schema))
|
116
|
+
schemas << { http_method: http_method, class: schema_class, location: source_location_for(schema_class)}
|
117
|
+
end
|
118
|
+
|
119
|
+
policy_names << resource.policy_name
|
117
120
|
end
|
121
|
+
|
122
|
+
{
|
123
|
+
allowed_methods: allowed_methods,
|
124
|
+
schemas: schemas,
|
125
|
+
policy_names: policy_names.uniq
|
126
|
+
}
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.build_resource(webmachine_route, http_method, application_context)
|
130
|
+
path_info = { application_context: application_context, pacticipant_name: "foo", pacticipant_version_number: "1", resource_name: "foo" }
|
131
|
+
path_info.default = "1"
|
132
|
+
request = build_request(http_method: http_method, path_info: path_info)
|
133
|
+
webmachine_route.resource.new(request, Webmachine::Response.new)
|
118
134
|
end
|
119
135
|
|
120
|
-
def self.
|
121
|
-
|
122
|
-
|
123
|
-
|
136
|
+
def self.build_request(http_method: "GET", path_info: )
|
137
|
+
request = Webmachine::Adapters::Rack::RackRequest.new(http_method, "/", Webmachine::Headers["host" => "example.org"], nil, {}, {}, { "REQUEST_METHOD" => http_method })
|
138
|
+
request.path_info = path_info
|
139
|
+
request
|
124
140
|
end
|
125
141
|
|
126
142
|
def self.source_location_for(clazz)
|
@@ -35,7 +35,7 @@ module Webmachine
|
|
35
35
|
title = options[:title] if options[:title]
|
36
36
|
message = options[:message] if options[:message]
|
37
37
|
|
38
|
-
res.body = error_response_body(message, title, title.dasherize.gsub(/^\d+\-/, ""), code, req)
|
38
|
+
res.body = error_response_body(req, message, title, title.dasherize.gsub(/^\d+\-/, ""), code, req)
|
39
39
|
res.headers[CONTENT_TYPE] = error_response_content_type(req)
|
40
40
|
end
|
41
41
|
ensure_content_length(res)
|
@@ -60,11 +60,20 @@ module Webmachine
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
-
|
63
|
+
# rubocop: disable Metrics/ParameterLists
|
64
|
+
def self.error_response_body(req, detail, title, type, status, request)
|
64
65
|
if problem_json_error_content_type?(request)
|
65
|
-
|
66
|
+
decorator_configuration(req)
|
67
|
+
.class_for(:custom_error_problem_json_decorator)
|
68
|
+
.new(detail: detail, title: title, type: type, status: status)
|
69
|
+
.to_json(user_options: { base_url: req.env["pactbroker.base_url"] })
|
66
70
|
else
|
67
|
-
|
71
|
+
decorator_configuration(req).class_for(:error_decorator).new(detail).to_json
|
68
72
|
end
|
69
73
|
end
|
74
|
+
# rubocop: enable Metrics/ParameterLists
|
75
|
+
|
76
|
+
def self.decorator_configuration(req)
|
77
|
+
req.env["pactbroker.application_context"].decorator_configuration
|
78
|
+
end
|
70
79
|
end
|
data/pact_broker.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.summary = %q{See description}
|
15
15
|
gem.homepage = "https://github.com/pact-foundation/pact_broker"
|
16
16
|
|
17
|
-
gem.required_ruby_version = ">= 2.
|
17
|
+
gem.required_ruby_version = ">= 2.7.0"
|
18
18
|
|
19
19
|
gem.files = begin
|
20
20
|
if Dir.exist?(".git")
|
@@ -58,14 +58,14 @@ Gem::Specification.new do |gem|
|
|
58
58
|
gem.add_runtime_dependency "webmachine", ">= 2.0.0.beta", "< 3.0"
|
59
59
|
gem.add_runtime_dependency "webrick", "~> 1.8" # webmachine requires webrick, but doesn't declare it as a dependency :shrug:
|
60
60
|
gem.add_runtime_dependency "semver2", "~> 3.4.2"
|
61
|
-
gem.add_runtime_dependency "rack", ">= 2.2.3", "~> 2.2"
|
61
|
+
gem.add_runtime_dependency "rack", ">= 2.2.3", "~> 2.2" # TODO update to 3
|
62
62
|
gem.add_runtime_dependency "redcarpet", ">= 3.5.1", "~>3.5"
|
63
63
|
gem.add_runtime_dependency "pact-support" , "~> 1.16", ">= 1.16.4"
|
64
64
|
gem.add_runtime_dependency "padrino-core", ">= 0.14.3", "~> 0.14"
|
65
|
-
gem.add_runtime_dependency "sinatra", "
|
65
|
+
gem.add_runtime_dependency "sinatra", "~> 3.0"
|
66
66
|
gem.add_runtime_dependency "haml", "~>5.0"
|
67
67
|
gem.add_runtime_dependency "sucker_punch", "~>2.0"
|
68
|
-
gem.add_runtime_dependency "rack-protection", "
|
68
|
+
gem.add_runtime_dependency "rack-protection", "~> 3.0"
|
69
69
|
gem.add_runtime_dependency "table_print", "~> 1.5"
|
70
70
|
gem.add_runtime_dependency "semantic_logger", "~> 4.11"
|
71
71
|
gem.add_runtime_dependency "sanitize", "~> 6.0"
|