pact_broker 2.4.2 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +4 -2
- data/CHANGELOG.md +54 -0
- data/DEVELOPER_DOCUMENTATION.md +11 -7
- data/README.md +5 -1
- data/UPGRADING.md +18 -0
- data/db/migrations/19_make_pact_version_content_sha_not_nullable.rb +9 -1
- data/db/migrations/25_make_pv_pacticipants_mandatory.rb +8 -0
- data/db/migrations/38_create_triggered_webhooks_table.rb +19 -0
- data/db/migrations/39_add_triggered_webhooks_fk_to_execution.rb +24 -0
- data/db/migrations/40_create_latest_triggered_webhooks_view.rb +24 -0
- data/db/migrations/41_migrate_execution_data.rb +47 -0
- data/db/test/backwards_compatibility/.rspec +3 -0
- data/db/test/backwards_compatibility/Appraisals +49 -0
- data/db/test/backwards_compatibility/Gemfile +11 -0
- data/db/test/backwards_compatibility/Rakefile +55 -0
- data/db/test/backwards_compatibility/config.ru +18 -0
- data/db/test/backwards_compatibility/gemfiles/1.18.0.gemfile +14 -0
- data/db/test/backwards_compatibility/gemfiles/1.18.0.gemfile.lock +210 -0
- data/db/test/backwards_compatibility/gemfiles/2.0.0.gemfile +14 -0
- data/db/test/backwards_compatibility/gemfiles/2.0.0.gemfile.lock +208 -0
- data/db/test/backwards_compatibility/gemfiles/2.1.0.gemfile +14 -0
- data/db/test/backwards_compatibility/gemfiles/2.1.0.gemfile.lock +209 -0
- data/db/test/backwards_compatibility/gemfiles/2.2.0.gemfile +14 -0
- data/db/test/backwards_compatibility/gemfiles/2.2.0.gemfile.lock +197 -0
- data/db/test/backwards_compatibility/gemfiles/2.3.0.gemfile +13 -0
- data/db/test/backwards_compatibility/gemfiles/2.3.0.gemfile.lock +196 -0
- data/db/test/backwards_compatibility/gemfiles/2.4.2.gemfile +13 -0
- data/db/test/backwards_compatibility/gemfiles/2.4.2.gemfile.lock +196 -0
- data/db/test/backwards_compatibility/gemfiles/head.gemfile +13 -0
- data/db/test/backwards_compatibility/gemfiles/head.gemfile.lock +200 -0
- data/db/test/backwards_compatibility/spec/fixtures/foo-bar.json +22 -0
- data/db/test/backwards_compatibility/spec/publish_pact_spec.rb +72 -0
- data/db/test/backwards_compatibility/spec/spec_helper.rb +20 -0
- data/db/test/backwards_compatibility/spec/support/fixture_helpers.rb +12 -0
- data/db/test/backwards_compatibility/spec/support/request_helpers.rb +20 -0
- data/example/Gemfile +2 -2
- data/example/pact_broker_database.sqlite3 +0 -0
- data/lib/pact_broker/api/decorators/pact_collection_decorator.rb +1 -2
- data/lib/pact_broker/api/decorators/pact_decorator.rb +12 -10
- data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +1 -2
- data/lib/pact_broker/api/decorators/pact_webhooks_status_decorator.rb +123 -0
- data/lib/pact_broker/api/decorators/versions_decorator.rb +17 -6
- data/lib/pact_broker/api/decorators/webhook_decorator.rb +8 -10
- data/lib/pact_broker/api/decorators/webhooks_decorator.rb +0 -1
- data/lib/pact_broker/api/pact_broker_urls.rb +13 -1
- data/lib/pact_broker/api/renderers/html_pact_renderer.rb +47 -3
- data/lib/pact_broker/api/resources/badge.rb +3 -3
- data/lib/pact_broker/api/resources/base_resource.rb +1 -1
- data/lib/pact_broker/api/resources/latest_pact.rb +5 -1
- data/lib/pact_broker/api/resources/pact.rb +5 -1
- data/lib/pact_broker/api/resources/pact_webhooks_status.rb +61 -0
- data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +36 -0
- data/lib/pact_broker/api/resources/webhook.rb +31 -3
- data/lib/pact_broker/api/resources/webhook_execution.rb +12 -2
- data/lib/pact_broker/api.rb +3 -0
- data/lib/pact_broker/app.rb +11 -3
- data/lib/pact_broker/badges/service.rb +26 -5
- data/lib/pact_broker/configuration.rb +12 -5
- data/lib/pact_broker/constants.rb +1 -1
- data/lib/pact_broker/diagnostic/resources/heartbeat.rb +1 -2
- data/lib/pact_broker/doc/views/pact-webhooks.markdown +1 -1
- data/lib/pact_broker/doc/views/webhooks-webhooks.markdown +1 -1
- data/lib/pact_broker/doc/views/webhooks.markdown +1 -1
- data/lib/pact_broker/domain/relationship.rb +13 -4
- data/lib/pact_broker/domain/verification.rb +0 -4
- data/lib/pact_broker/domain/webhook.rb +2 -6
- data/lib/pact_broker/domain/webhook_execution_result.rb +1 -2
- data/lib/pact_broker/domain/webhook_request.rb +59 -40
- data/lib/pact_broker/pacticipants/service.rb +4 -3
- data/lib/pact_broker/pacts/repository.rb +8 -0
- data/lib/pact_broker/pacts/service.rb +2 -0
- data/lib/pact_broker/services.rb +1 -1
- data/lib/pact_broker/ui/view_models/relationship.rb +29 -2
- data/lib/pact_broker/ui/views/relationships/show.haml +7 -10
- data/lib/pact_broker/verifications/repository.rb +8 -1
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/webhooks/execution.rb +25 -4
- data/lib/pact_broker/webhooks/job.rb +55 -13
- data/lib/pact_broker/webhooks/latest_triggered_webhook.rb +9 -0
- data/lib/pact_broker/webhooks/redact_logs.rb +10 -0
- data/lib/pact_broker/webhooks/repository.rb +76 -8
- data/lib/pact_broker/webhooks/service.rb +48 -8
- data/lib/pact_broker/webhooks/status.rb +29 -0
- data/lib/pact_broker/webhooks/triggered_webhook.rb +96 -0
- data/lib/pact_broker/webhooks/webhook.rb +19 -8
- data/lib/rack/pact_broker/database_transaction.rb +9 -3
- data/pact_broker.gemspec +3 -3
- data/public/javascripts/pact.js +5 -0
- data/public/stylesheets/pact.css +14 -1
- data/public/stylesheets/relationships.css +0 -1
- data/script/db-spec.sh +7 -0
- data/script/seed.rb +13 -8
- data/spec/features/create_webhook_spec.rb +1 -1
- data/spec/features/delete_pact_spec.rb +5 -1
- data/spec/features/delete_webhook_spec.rb +2 -1
- data/spec/features/edit_webhook_spec.rb +61 -0
- data/spec/features/execute_webhook_spec.rb +73 -0
- data/spec/features/get_latest_pact_badge_spec.rb +1 -1
- data/spec/features/get_latest_tagged_pact_badge_spec.rb +1 -1
- data/spec/features/get_latest_untagged_pact_badge_spec.rb +1 -1
- data/spec/features/get_pact_spec.rb +1 -1
- data/spec/features/merge_pact_spec.rb +1 -1
- data/spec/features/publish_pact_spec.rb +1 -1
- data/spec/integration/app_spec.rb +1 -1
- data/spec/integration/endpoints/group.rb +1 -1
- data/spec/lib/pact_broker/api/decorators/latest_pact_decorator_spec.rb +2 -1
- data/spec/lib/pact_broker/api/decorators/pact_decorator_spec.rb +8 -6
- data/spec/lib/pact_broker/api/decorators/pact_webhooks_status_decorator_spec.rb +134 -0
- data/spec/lib/pact_broker/api/decorators/relationships_csv_decorator_spec.rb +1 -1
- data/spec/lib/pact_broker/api/decorators/representable_pact_spec.rb +1 -1
- data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +27 -1
- data/spec/lib/pact_broker/api/resources/badge_spec.rb +32 -15
- data/spec/lib/pact_broker/api/resources/base_resource_spec.rb +17 -0
- data/spec/lib/pact_broker/api/resources/latest_pact_spec.rb +5 -3
- data/spec/lib/pact_broker/api/resources/pact_spec.rb +9 -2
- data/spec/lib/pact_broker/api/resources/triggered_webhook_logs_spec.rb +28 -0
- data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +15 -5
- data/spec/lib/pact_broker/api/resources/webhook_spec.rb +43 -31
- data/spec/lib/pact_broker/app_spec.rb +12 -8
- data/spec/lib/pact_broker/badges/service_spec.rb +15 -1
- data/spec/lib/pact_broker/configuration_spec.rb +3 -2
- data/spec/lib/pact_broker/domain/relationship_spec.rb +24 -0
- data/spec/lib/pact_broker/domain/webhook_request_spec.rb +47 -31
- data/spec/lib/pact_broker/domain/webhook_spec.rb +4 -6
- data/spec/lib/pact_broker/pacticipants/service_spec.rb +16 -1
- data/spec/lib/pact_broker/pacts/repository_spec.rb +22 -1
- data/spec/lib/pact_broker/pacts/service_spec.rb +32 -1
- data/spec/lib/pact_broker/ui/view_models/relationship_spec.rb +44 -0
- data/spec/lib/pact_broker/verifications/repository_spec.rb +19 -0
- data/spec/lib/pact_broker/verifications/service_spec.rb +1 -1
- data/spec/lib/pact_broker/webhooks/job_spec.rb +80 -19
- data/spec/lib/pact_broker/webhooks/redact_logs_spec.rb +49 -0
- data/spec/lib/pact_broker/webhooks/repository_spec.rb +271 -21
- data/spec/lib/pact_broker/webhooks/service_spec.rb +70 -3
- data/spec/lib/pact_broker/webhooks/status_spec.rb +48 -0
- data/spec/lib/pact_broker/webhooks/triggered_webhook_spec.rb +40 -0
- data/spec/lib/rack/pact_broker/database_transaction_spec.rb +14 -4
- data/spec/migrations/23_pact_versions_spec.rb +8 -30
- data/spec/migrations/24_populate_pact_contents_spec.rb +3 -21
- data/spec/migrations/34_latest_tagged_pacts_spec.rb +1 -17
- data/spec/migrations/34_pact_revisions_spec.rb +7 -23
- data/spec/migrations/41_migrate_execution_data_spec.rb +109 -0
- data/spec/service_consumers/pact_helper.rb +5 -1
- data/spec/spec_helper.rb +15 -7
- data/spec/support/database_cleaner.rb +15 -2
- data/spec/support/migration_helpers.rb +16 -0
- data/spec/support/test_data_builder.rb +41 -9
- data/tasks/database.rb +7 -2
- data/tasks/db.rake +10 -0
- data/tasks/rspec.rake +1 -1
- data/vendor/hal-browser/browser.html +3 -2
- data/vendor/hal-browser/js/hal/resource.js +16 -2
- metadata +72 -13
- data/script/record_verification.sh +0 -4
@@ -3,12 +3,17 @@ require 'pact_broker/logging'
|
|
3
3
|
require 'pact_broker/webhooks/job'
|
4
4
|
require 'base64'
|
5
5
|
require 'securerandom'
|
6
|
+
require 'pact_broker/webhooks/triggered_webhook'
|
7
|
+
require 'pact_broker/webhooks/status'
|
6
8
|
|
7
9
|
module PactBroker
|
8
10
|
|
9
11
|
module Webhooks
|
10
12
|
class Service
|
11
13
|
|
14
|
+
PUBLICATION = PactBroker::Webhooks::TriggeredWebhook::TRIGGER_TYPE_PUBLICATION
|
15
|
+
USER = PactBroker::Webhooks::TriggeredWebhook::TRIGGER_TYPE_USER
|
16
|
+
|
12
17
|
extend Repositories
|
13
18
|
include Logging
|
14
19
|
|
@@ -30,25 +35,50 @@ module PactBroker
|
|
30
35
|
webhook_repository.find_by_uuid uuid
|
31
36
|
end
|
32
37
|
|
38
|
+
def self.update_by_uuid uuid, webhook
|
39
|
+
webhook_repository.update_by_uuid uuid, webhook
|
40
|
+
end
|
41
|
+
|
33
42
|
def self.delete_by_uuid uuid
|
34
|
-
webhook_repository.
|
43
|
+
webhook_repository.unlink_triggered_webhooks_by_webhook_uuid uuid
|
35
44
|
webhook_repository.delete_by_uuid uuid
|
36
45
|
end
|
37
46
|
|
38
|
-
def self.
|
47
|
+
def self.delete_all_webhhook_related_objects_by_pacticipant pacticipant
|
48
|
+
webhook_repository.delete_executions_by_pacticipant pacticipant
|
49
|
+
webhook_repository.delete_triggered_webhooks_by_pacticipant pacticipant
|
39
50
|
webhook_repository.delete_by_pacticipant pacticipant
|
40
51
|
end
|
41
52
|
|
53
|
+
def self.delete_all_webhook_related_objects_by_pact_publication_ids pact_publication_ids
|
54
|
+
webhook_repository.delete_triggered_webhooks_by_pact_publication_ids pact_publication_ids
|
55
|
+
end
|
56
|
+
|
42
57
|
def self.find_all
|
43
58
|
webhook_repository.find_all
|
44
59
|
end
|
45
60
|
|
46
|
-
def self.execute_webhook_now webhook
|
47
|
-
|
48
|
-
|
61
|
+
def self.execute_webhook_now webhook, pact
|
62
|
+
triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, USER)
|
63
|
+
webhook_execution_result = execute_triggered_webhook_now triggered_webhook, failure_log_message: "Webhook execution failed"
|
64
|
+
if webhook_execution_result.success?
|
65
|
+
webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_SUCCESS
|
66
|
+
else
|
67
|
+
webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_FAILURE
|
68
|
+
end
|
69
|
+
webhook_execution_result
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.execute_triggered_webhook_now triggered_webhook, options
|
73
|
+
webhook_execution_result = triggered_webhook.execute options
|
74
|
+
webhook_repository.create_execution triggered_webhook, webhook_execution_result
|
49
75
|
webhook_execution_result
|
50
76
|
end
|
51
77
|
|
78
|
+
def self.update_triggered_webhook_status triggered_webhook, status
|
79
|
+
webhook_repository.update_triggered_webhook_status triggered_webhook, status
|
80
|
+
end
|
81
|
+
|
52
82
|
def self.find_by_consumer_and_provider consumer, provider
|
53
83
|
webhook_repository.find_by_consumer_and_provider consumer, provider
|
54
84
|
end
|
@@ -57,22 +87,32 @@ module PactBroker
|
|
57
87
|
webhooks = webhook_repository.find_by_consumer_and_provider pact.consumer, pact.provider
|
58
88
|
|
59
89
|
if webhooks.any?
|
60
|
-
run_later(webhooks)
|
90
|
+
run_later(webhooks, pact)
|
61
91
|
else
|
62
92
|
logger.debug "No webhook found for consumer \"#{pact.consumer.name}\" and provider \"#{pact.provider.name}\""
|
63
93
|
end
|
64
94
|
end
|
65
95
|
|
66
|
-
def self.run_later webhooks
|
96
|
+
def self.run_later webhooks, pact
|
97
|
+
trigger_uuid = next_uuid
|
67
98
|
webhooks.each do | webhook |
|
68
99
|
begin
|
100
|
+
triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, PUBLICATION)
|
69
101
|
logger.info "Scheduling job for #{webhook.description} with uuid #{webhook.uuid}"
|
70
|
-
Job.perform_async
|
102
|
+
Job.perform_async triggered_webhook: triggered_webhook
|
71
103
|
rescue StandardError => e
|
72
104
|
log_error e
|
73
105
|
end
|
74
106
|
end
|
75
107
|
end
|
108
|
+
|
109
|
+
def self.find_latest_triggered_webhooks consumer, provider
|
110
|
+
webhook_repository.find_latest_triggered_webhooks consumer, provider
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.fail_retrying_triggered_webhooks
|
114
|
+
webhook_repository.fail_retrying_triggered_webhooks
|
115
|
+
end
|
76
116
|
end
|
77
117
|
end
|
78
118
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PactBroker
|
2
|
+
module Webhooks
|
3
|
+
class Status
|
4
|
+
|
5
|
+
def initialize pact, webhooks, latest_triggered_webhooks
|
6
|
+
@webhooks = webhooks
|
7
|
+
@latest_triggered_webhooks = latest_triggered_webhooks
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
to_sym.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_sym
|
15
|
+
return :none if webhooks.empty?
|
16
|
+
return :not_run if latest_triggered_webhooks.empty?
|
17
|
+
if latest_triggered_webhooks.any?{|w| w.status == "retrying" }
|
18
|
+
return :retrying
|
19
|
+
end
|
20
|
+
latest_triggered_webhooks.all?{|w| w.status == "success"} ? :success : :failure
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
attr_reader :webhooks, :latest_triggered_webhooks
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'pact_broker/repositories/helpers'
|
3
|
+
require 'pact_broker/webhooks/execution'
|
4
|
+
|
5
|
+
module PactBroker
|
6
|
+
module Webhooks
|
7
|
+
class TriggeredWebhook < Sequel::Model(:triggered_webhooks)
|
8
|
+
|
9
|
+
TRIGGER_TYPE_PUBLICATION = 'pact_publication'
|
10
|
+
TRIGGER_TYPE_USER = 'user'
|
11
|
+
|
12
|
+
STATUS_NOT_RUN = 'not_run'.freeze
|
13
|
+
STATUS_RETRYING = 'retrying'.freeze
|
14
|
+
STATUS_SUCCESS = 'success'.freeze
|
15
|
+
STATUS_FAILURE = 'failure'.freeze
|
16
|
+
|
17
|
+
dataset_module do
|
18
|
+
include PactBroker::Repositories::Helpers
|
19
|
+
|
20
|
+
def retrying
|
21
|
+
where(status: STATUS_RETRYING)
|
22
|
+
end
|
23
|
+
|
24
|
+
def successful
|
25
|
+
where(status: STATUS_SUCCESS)
|
26
|
+
end
|
27
|
+
|
28
|
+
def failed
|
29
|
+
where(status: STATUS_FAILURE)
|
30
|
+
end
|
31
|
+
|
32
|
+
def not_run
|
33
|
+
where(status: STATUS_NOT_RUN)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
associate(:one_to_many, :webhook_executions, :class => "PactBroker::Webhooks::Execution", :key => :triggered_webhook_id, :primary_key => :id, :order => :id)
|
38
|
+
associate(:many_to_one, :webhook, :class => "PactBroker::Webhooks::Webhook", :key => :webhook_id, :primary_key => :id)
|
39
|
+
associate(:many_to_one, :pact_publication, :class => "PactBroker::Pacts::PactPublication", :key => :pact_publication_id, :primary_key => :id)
|
40
|
+
associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
|
41
|
+
associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
|
42
|
+
|
43
|
+
|
44
|
+
def request_description
|
45
|
+
webhook.to_domain.request_description
|
46
|
+
end
|
47
|
+
|
48
|
+
def execute options
|
49
|
+
webhook.to_domain.execute options
|
50
|
+
end
|
51
|
+
|
52
|
+
def consumer_name
|
53
|
+
consumer && consumer.name
|
54
|
+
end
|
55
|
+
|
56
|
+
def provider_name
|
57
|
+
provider && provider.name
|
58
|
+
end
|
59
|
+
|
60
|
+
def success?
|
61
|
+
status == STATUS_SUCCESS
|
62
|
+
end
|
63
|
+
|
64
|
+
def failure?
|
65
|
+
status == STATUS_FAILURE
|
66
|
+
end
|
67
|
+
|
68
|
+
def retrying?
|
69
|
+
status == STATUS_RETRYING
|
70
|
+
end
|
71
|
+
|
72
|
+
def not_run?
|
73
|
+
status == STATUS_NOT_RUN
|
74
|
+
end
|
75
|
+
|
76
|
+
def number_of_attempts_made
|
77
|
+
webhook_executions.size
|
78
|
+
end
|
79
|
+
|
80
|
+
def finished?
|
81
|
+
success? || failure?
|
82
|
+
end
|
83
|
+
|
84
|
+
def number_of_attempts_remaining
|
85
|
+
if finished?
|
86
|
+
0
|
87
|
+
else
|
88
|
+
(PactBroker.configuration.webhook_retry_schedule.size + 1) - number_of_attempts_made
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
TriggeredWebhook.plugin :timestamps, update_on_create: true
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -15,16 +15,13 @@ module PactBroker
|
|
15
15
|
WebhookHeader.where(webhook_id: id).destroy
|
16
16
|
end
|
17
17
|
|
18
|
+
def update_from_domain webhook
|
19
|
+
set(self.class.properties_hash_from_domain(webhook))
|
20
|
+
end
|
21
|
+
|
18
22
|
def self.from_domain webhook, consumer, provider
|
19
|
-
is_json_request_body = !(String === webhook.request.body || webhook.request.body.nil?) # Can't rely on people to set content type
|
20
23
|
new(
|
21
|
-
uuid: webhook.uuid
|
22
|
-
method: webhook.request.method,
|
23
|
-
url: webhook.request.url,
|
24
|
-
username: webhook.request.username,
|
25
|
-
password: not_plain_text_password(webhook.request.password),
|
26
|
-
body: (is_json_request_body ? webhook.request.body.to_json : webhook.request.body),
|
27
|
-
is_json_request_body: is_json_request_body
|
24
|
+
properties_hash_from_domain(webhook).merge(uuid: webhook.uuid)
|
28
25
|
).tap do | db_webhook |
|
29
26
|
db_webhook.consumer_id = consumer.id
|
30
27
|
db_webhook.provider_id = provider.id
|
@@ -67,6 +64,20 @@ module PactBroker
|
|
67
64
|
end
|
68
65
|
end
|
69
66
|
|
67
|
+
private
|
68
|
+
|
69
|
+
def self.properties_hash_from_domain webhook
|
70
|
+
is_json_request_body = !(String === webhook.request.body || webhook.request.body.nil?) # Can't rely on people to set content type
|
71
|
+
{
|
72
|
+
method: webhook.request.method,
|
73
|
+
url: webhook.request.url,
|
74
|
+
username: webhook.request.username,
|
75
|
+
password: not_plain_text_password(webhook.request.password),
|
76
|
+
body: (is_json_request_body ? webhook.request.body.to_json : webhook.request.body),
|
77
|
+
is_json_request_body: is_json_request_body
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
70
81
|
end
|
71
82
|
|
72
83
|
Webhook.plugin :timestamps, :update_on_create=>true
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'pact_broker/
|
1
|
+
require 'pact_broker/constants'
|
2
2
|
require 'sequel'
|
3
3
|
|
4
4
|
module Rack
|
@@ -6,7 +6,7 @@ module Rack
|
|
6
6
|
class DatabaseTransaction
|
7
7
|
|
8
8
|
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
9
|
-
TRANS_METHODS = %{POST PUT PATCH DELETE}.freeze
|
9
|
+
TRANS_METHODS = %w{POST PUT PATCH DELETE}.freeze
|
10
10
|
|
11
11
|
def initialize app, database_connection
|
12
12
|
@app = app
|
@@ -33,10 +33,16 @@ module Rack
|
|
33
33
|
response = nil
|
34
34
|
@database_connection.transaction do
|
35
35
|
response = @app.call(env)
|
36
|
-
|
36
|
+
if response.first == 500
|
37
|
+
raise Sequel::Rollback unless do_not_rollback?(response)
|
38
|
+
end
|
37
39
|
end
|
38
40
|
response
|
39
41
|
end
|
42
|
+
|
43
|
+
def do_not_rollback? response
|
44
|
+
response[1].delete(::PactBroker::DO_NOT_ROLLBACK)
|
45
|
+
end
|
40
46
|
end
|
41
47
|
end
|
42
48
|
end
|
data/pact_broker.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |gem|
|
|
27
27
|
gem.add_runtime_dependency 'reform', '~> 2.2'
|
28
28
|
gem.add_runtime_dependency 'dry-validation', '~> 0.10.5'
|
29
29
|
gem.add_runtime_dependency 'sequel', '~> 4.23'
|
30
|
-
gem.add_runtime_dependency 'webmachine', '1.
|
30
|
+
gem.add_runtime_dependency 'webmachine', '1.5.0'
|
31
31
|
gem.add_runtime_dependency 'semver2', '~> 3.4.2'
|
32
32
|
gem.add_runtime_dependency 'rack', '~>2.0'
|
33
33
|
gem.add_runtime_dependency 'redcarpet', '>=3.3.2', '~>3.3'
|
@@ -39,7 +39,7 @@ Gem::Specification.new do |gem|
|
|
39
39
|
gem.add_runtime_dependency 'dry-types', '~> 0.10.3' # https://travis-ci.org/pact-foundation/pact_broker/jobs/249448621
|
40
40
|
|
41
41
|
gem.add_development_dependency 'bundler-audit', '~>0.4'
|
42
|
-
gem.add_development_dependency 'sqlite3'
|
42
|
+
gem.add_development_dependency 'sqlite3', '~>1.3'
|
43
43
|
gem.add_development_dependency 'pry-byebug'
|
44
44
|
gem.add_development_dependency 'rake', '~>10.0'
|
45
45
|
gem.add_development_dependency 'fakefs', '~>0.4'
|
@@ -48,7 +48,7 @@ Gem::Specification.new do |gem|
|
|
48
48
|
gem.add_development_dependency 'rspec', '~>3.0'
|
49
49
|
gem.add_development_dependency 'rspec-its', '~>1.2'
|
50
50
|
gem.add_development_dependency 'database_cleaner', '~>1.6'
|
51
|
-
gem.add_development_dependency 'pg'
|
51
|
+
gem.add_development_dependency 'pg', '~>0.21'
|
52
52
|
gem.add_development_dependency 'conventional-changelog', '~>1.3'
|
53
53
|
|
54
54
|
end
|
data/public/stylesheets/pact.css
CHANGED
data/script/db-spec.sh
ADDED
data/script/seed.rb
CHANGED
@@ -10,19 +10,20 @@ require 'logger'
|
|
10
10
|
DATABASE_CREDENTIALS = {logger: Logger.new($stdout), adapter: "sqlite", database: ARGV[0], :encoding => 'utf8'}
|
11
11
|
connection = Sequel.connect(DATABASE_CREDENTIALS)
|
12
12
|
connection.timezone = :utc
|
13
|
+
require 'pact_broker/db'
|
14
|
+
PactBroker::DB.connection = connection
|
13
15
|
require 'pact_broker'
|
14
16
|
require 'support/test_data_builder'
|
15
17
|
|
16
|
-
def random_build
|
17
|
-
rand * 100
|
18
|
-
end
|
19
18
|
|
20
|
-
tables_to_clean = [:labels, :webhook_executions, :verifications, :pact_publications, :pact_versions, :pacts, :pact_version_contents, :tags, :versions, :webhook_headers, :webhooks, :pacticipants]
|
19
|
+
tables_to_clean = [:labels, :webhook_executions, :triggered_webhooks, :verifications, :pact_publications, :pact_versions, :pacts, :pact_version_contents, :tags, :versions, :webhook_headers, :webhooks, :pacticipants]
|
21
20
|
|
22
21
|
tables_to_clean.each do | table_name |
|
23
22
|
connection[table_name].delete if connection.table_exists?(table_name)
|
24
23
|
end
|
25
24
|
|
25
|
+
|
26
|
+
|
26
27
|
class TestDataBuilder
|
27
28
|
def method_missing *args
|
28
29
|
self
|
@@ -52,7 +53,7 @@ TestDataBuilder.new
|
|
52
53
|
.create_label("microservice")
|
53
54
|
.create_provider("Bar")
|
54
55
|
.create_label("microservice")
|
55
|
-
.create_webhook(method: 'GET', url: 'http://
|
56
|
+
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
|
56
57
|
.create_consumer_version("1.2.100")
|
57
58
|
.publish_pact
|
58
59
|
.create_verification(provider_version: "1.4.234", success: true, execution_date: DateTime.now - 15)
|
@@ -62,7 +63,7 @@ TestDataBuilder.new
|
|
62
63
|
.create_consumer_version("1.2.102")
|
63
64
|
.publish_pact(created_at: (Date.today - 7).to_datetime)
|
64
65
|
.create_provider("Animals")
|
65
|
-
.create_webhook(method: 'GET', url: 'http://
|
66
|
+
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
|
66
67
|
.publish_pact(created_at: (Time.now - 140).to_datetime)
|
67
68
|
.create_verification(provider_version: "2.0.366", execution_date: Date.today - 2) #changed
|
68
69
|
.create_provider("Wiffles")
|
@@ -73,12 +74,16 @@ TestDataBuilder.new
|
|
73
74
|
.publish_pact(created_at: (Date.today - 1).to_datetime)
|
74
75
|
.create_consumer("The Android App")
|
75
76
|
.create_provider("The back end")
|
76
|
-
.create_webhook(method: 'GET', url: 'http://
|
77
|
+
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
|
77
78
|
.create_consumer_version("1.2.106")
|
78
79
|
.publish_pact
|
79
80
|
.create_consumer("Some other app")
|
80
81
|
.create_provider("A service")
|
81
|
-
.create_webhook(method: 'GET', url: 'http://
|
82
|
+
.create_webhook(method: 'GET', url: 'http://localhost:9393/')
|
83
|
+
.create_triggered_webhook(status: 'success')
|
84
|
+
.create_webhook_execution
|
85
|
+
.create_webhook(method: 'POST', url: 'http://foo:9393/')
|
86
|
+
.create_triggered_webhook(status: 'failure')
|
82
87
|
.create_webhook_execution
|
83
88
|
.create_consumer_version("1.2.106")
|
84
89
|
.publish_pact(created_at: (Date.today - 26).to_datetime)
|
@@ -3,7 +3,7 @@ require 'support/test_data_builder'
|
|
3
3
|
describe "Creating a webhook" do
|
4
4
|
|
5
5
|
before do
|
6
|
-
TestDataBuilder.new.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
|
6
|
+
TestDataBuilder.new.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider").and_return(:pact)
|
7
7
|
end
|
8
8
|
|
9
9
|
let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" }
|
@@ -8,7 +8,11 @@ describe "Deleting a pact" do
|
|
8
8
|
|
9
9
|
context "when the pact exists" do
|
10
10
|
before do
|
11
|
-
TestDataBuilder.new
|
11
|
+
TestDataBuilder.new
|
12
|
+
.create_pact_with_hierarchy("A Consumer", "1.2.3", "A Provider")
|
13
|
+
.create_webhook
|
14
|
+
.create_triggered_webhook
|
15
|
+
.create_deprecated_webhook_execution
|
12
16
|
end
|
13
17
|
|
14
18
|
it "deletes the pact" do
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'support/test_data_builder'
|
2
|
+
|
3
|
+
describe "Creating a webhook" do
|
4
|
+
|
5
|
+
let(:td) { TestDataBuilder.new }
|
6
|
+
|
7
|
+
before do
|
8
|
+
td.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
|
9
|
+
.create_webhook
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:path) { "/webhooks/#{td.webhook.uuid}" }
|
13
|
+
let(:headers) { {'CONTENT_TYPE' => 'application/json'} }
|
14
|
+
let(:response_body) { JSON.parse(last_response.body, symbolize_names: true)}
|
15
|
+
let(:webhook_json) do
|
16
|
+
h = load_json_fixture('webhook_valid.json')
|
17
|
+
h['request']['method'] = 'GET'
|
18
|
+
h.to_json
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:reloaded_webhook) { PactBroker::Webhooks::Repository.new.find_by_uuid(td.webhook.uuid) }
|
22
|
+
|
23
|
+
subject { put path, webhook_json, headers; last_response }
|
24
|
+
|
25
|
+
context "with invalid attributes" do
|
26
|
+
let(:webhook_json) { '{}' }
|
27
|
+
|
28
|
+
it "returns a 400" do
|
29
|
+
subject
|
30
|
+
expect(last_response.status).to be 400
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns the validation errors" do
|
34
|
+
subject
|
35
|
+
expect(response_body[:errors]).to_not be_empty
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does not update the webhook" do
|
39
|
+
expect(reloaded_webhook.request.method).to eq 'POST'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with valid attributes" do
|
44
|
+
let(:webhook_hash) { JSON.parse(webhook_json, symbolize_names: true) }
|
45
|
+
|
46
|
+
it "returns a 200 response" do
|
47
|
+
subject
|
48
|
+
expect(last_response.status).to be 200
|
49
|
+
end
|
50
|
+
|
51
|
+
it "returns a JSON Content Type" do
|
52
|
+
subject
|
53
|
+
expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "updates the webhook" do
|
57
|
+
subject
|
58
|
+
expect(reloaded_webhook.request.method).to eq 'GET'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'support/test_data_builder'
|
2
|
+
require 'webmock/rspec'
|
3
|
+
require 'rack/pact_broker/database_transaction'
|
4
|
+
|
5
|
+
describe "Execute a webhook" do
|
6
|
+
|
7
|
+
let(:td) { TestDataBuilder.new }
|
8
|
+
|
9
|
+
before do
|
10
|
+
td.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
|
11
|
+
.create_webhook(method: 'POST')
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:path) { "/webhooks/#{td.webhook.uuid}/execute" }
|
15
|
+
let(:response_body) { JSON.parse(last_response.body, symbolize_names: true)}
|
16
|
+
|
17
|
+
subject { post path; last_response }
|
18
|
+
|
19
|
+
context "when the execution is successful" do
|
20
|
+
let!(:request) do
|
21
|
+
stub_request(:post, /http/).to_return(:status => 200)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "performs the HTTP request" do
|
25
|
+
subject
|
26
|
+
expect(request).to have_been_made
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns a 200 response" do
|
30
|
+
expect(subject.status).to be 200
|
31
|
+
end
|
32
|
+
|
33
|
+
it "saves a TriggeredWebhook" do
|
34
|
+
expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(1)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "saves a WebhookExecution" do
|
38
|
+
expect { subject }.to change { PactBroker::Webhooks::Execution.count }.by(1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when an error occurs", no_db_clean: true do
|
43
|
+
let(:app) { Rack::PactBroker::DatabaseTransaction.new(PactBroker::API, PactBroker::DB.connection) }
|
44
|
+
|
45
|
+
let!(:request) do
|
46
|
+
stub_request(:post, /http/).to_raise(Errno::ECONNREFUSED)
|
47
|
+
end
|
48
|
+
|
49
|
+
before do
|
50
|
+
PactBroker::Database.truncate
|
51
|
+
td.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
|
52
|
+
.create_webhook(method: 'POST')
|
53
|
+
end
|
54
|
+
|
55
|
+
after do
|
56
|
+
PactBroker::Database.truncate
|
57
|
+
end
|
58
|
+
|
59
|
+
subject { post path; last_response }
|
60
|
+
|
61
|
+
it "returns a 500 response" do
|
62
|
+
expect(subject.status).to be 500
|
63
|
+
end
|
64
|
+
|
65
|
+
it "saves a TriggeredWebhook" do
|
66
|
+
expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "saves a WebhookExecution" do
|
70
|
+
expect { subject }.to change { PactBroker::Webhooks::Execution.count }.by(1)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -3,7 +3,7 @@ require 'webmock/rspec'
|
|
3
3
|
describe "get latest pact badge" do
|
4
4
|
|
5
5
|
before do
|
6
|
-
PactBroker.configuration.
|
6
|
+
PactBroker.configuration.enable_public_badge_access = true
|
7
7
|
TestDataBuilder.new
|
8
8
|
.create_consumer('consumer')
|
9
9
|
.create_provider('provider')
|
@@ -3,7 +3,7 @@ require 'webmock/rspec'
|
|
3
3
|
describe "get latest tagged pact badge" do
|
4
4
|
|
5
5
|
before do
|
6
|
-
PactBroker.configuration.
|
6
|
+
PactBroker.configuration.enable_public_badge_access = true
|
7
7
|
PactBroker.configuration.shields_io_base_url = nil
|
8
8
|
TestDataBuilder.new
|
9
9
|
.create_consumer('consumer')
|
@@ -3,7 +3,7 @@ require 'webmock/rspec'
|
|
3
3
|
describe "get latest untagged pact badge" do
|
4
4
|
|
5
5
|
before do
|
6
|
-
PactBroker.configuration.
|
6
|
+
PactBroker.configuration.enable_public_badge_access = true
|
7
7
|
PactBroker.configuration.shields_io_base_url = nil
|
8
8
|
TestDataBuilder.new
|
9
9
|
.create_consumer('consumer')
|
@@ -7,7 +7,7 @@ describe "retrieving a pact" do
|
|
7
7
|
let(:path) { "/pacts/provider/a%20provider/consumer/a%20consumer/version/1.2.3A" }
|
8
8
|
|
9
9
|
before do
|
10
|
-
TestDataBuilder.new.create_pact_with_hierarchy
|
10
|
+
TestDataBuilder.new.create_pact_with_hierarchy("A Consumer", "1.2.3a", "A Provider").and_return(:pact)
|
11
11
|
end
|
12
12
|
|
13
13
|
context "when case sensitivity is turned on" do
|
@@ -29,7 +29,7 @@ describe "Merging a pact" do
|
|
29
29
|
let(:merged_pact_content) { load_fixture('a_consumer-a_provider-merged.json') }
|
30
30
|
|
31
31
|
before do
|
32
|
-
TestDataBuilder.new.create_pact_with_hierarchy
|
32
|
+
TestDataBuilder.new.create_pact_with_hierarchy("A Consumer", "1.2.3", "A Provider", existing_pact_content).and_return(:pact)
|
33
33
|
end
|
34
34
|
|
35
35
|
it "returns a 200 Success" do
|
@@ -23,7 +23,7 @@ describe "Publishing a pact" do
|
|
23
23
|
context "when a pact for this consumer version does exist" do
|
24
24
|
|
25
25
|
before do
|
26
|
-
TestDataBuilder.new.create_pact_with_hierarchy
|
26
|
+
TestDataBuilder.new.create_pact_with_hierarchy("A Consumer", "1.2.3", "A Provider").and_return(:pact)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "returns a 200 Success" do
|