pact_broker 2.34.0 → 2.35.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.dockerignore +2 -0
- data/.github/FUNDING.yml +4 -0
- data/.travis.yml +1 -1
- data/CHANGELOG.md +34 -0
- data/DEVELOPER_DOCUMENTATION.md +24 -1
- data/DEVELOPER_SETUP.md +40 -20
- data/Dockerfile +22 -0
- data/db/migrations/000028_create_all_pact_publications.rb +0 -1
- data/db/migrations/20180311_optimise_head_matrix.rb +0 -1
- data/lib/pact/doc/doc_file.rb +0 -1
- data/lib/pact_broker/api/contracts/webhook_contract.rb +1 -1
- data/lib/pact_broker/api/decorators/decorator_context.rb +5 -5
- data/lib/pact_broker/api/decorators/pact_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/tag_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/webhook_decorator.rb +0 -1
- data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +8 -5
- data/lib/pact_broker/api/decorators/webhook_request_template_decorator.rb +1 -4
- data/lib/pact_broker/api/decorators/webhooks_decorator.rb +1 -2
- data/lib/pact_broker/api/resources/base_resource.rb +0 -1
- data/lib/pact_broker/api/resources/error_handler.rb +14 -7
- data/lib/pact_broker/api/resources/pact.rb +4 -9
- data/lib/pact_broker/api/resources/pact_webhooks.rb +0 -1
- data/lib/pact_broker/api/resources/verifications.rb +4 -8
- data/lib/pact_broker/api/resources/webhook.rb +4 -8
- data/lib/pact_broker/api/resources/webhook_execution.rb +36 -17
- data/lib/pact_broker/api/resources/webhook_execution_methods.rb +13 -0
- data/lib/pact_broker/api/resources/webhook_resource_methods.rb +8 -15
- data/lib/pact_broker/api/resources/webhooks.rb +3 -12
- data/lib/pact_broker/api.rb +1 -1
- data/lib/pact_broker/app.rb +2 -0
- data/lib/pact_broker/domain/webhook.rb +29 -18
- data/lib/pact_broker/hash_refinements.rb +13 -0
- data/lib/pact_broker/locale/en.yml +16 -0
- data/lib/pact_broker/logging.rb +1 -1
- data/lib/pact_broker/pacts/diff.rb +0 -1
- data/lib/pact_broker/pacts/pact_version.rb +13 -7
- data/lib/pact_broker/pacts/repository.rb +4 -6
- data/lib/pact_broker/pacts/service.rb +3 -4
- data/lib/pact_broker/repositories/helpers.rb +1 -1
- data/lib/pact_broker/string_refinements.rb +13 -0
- data/lib/pact_broker/tags/repository.rb +0 -1
- data/lib/pact_broker/ui/views/index/_css_and_js.haml +1 -0
- data/lib/pact_broker/ui/views/index/show-with-tags.haml +8 -2
- data/lib/pact_broker/ui/views/matrix/show.haml +12 -2
- data/lib/pact_broker/verifications/repository.rb +0 -1
- data/lib/pact_broker/verifications/service.rb +7 -5
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/parse_semantic_version.rb +0 -1
- data/lib/pact_broker/webhooks/execution.rb +0 -1
- data/lib/pact_broker/webhooks/execution_configuration.rb +45 -0
- data/lib/pact_broker/webhooks/job.rb +5 -8
- data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +39 -12
- data/lib/pact_broker/webhooks/redact_logs.rb +21 -4
- data/lib/pact_broker/webhooks/render.rb +11 -2
- data/lib/pact_broker/webhooks/repository.rb +11 -5
- data/lib/pact_broker/webhooks/service.rb +21 -27
- data/lib/pact_broker/webhooks/triggered_webhook.rb +0 -1
- data/lib/pact_broker/webhooks/webhook_request_logger.rb +13 -0
- data/lib/pact_broker/webhooks/webhook_request_template.rb +32 -18
- data/lib/rack/pact_broker/add_vary_header.rb +39 -0
- data/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb +2 -0
- data/lib/webmachine/rack_adapter_monkey_patch.rb +0 -1
- data/public/javascripts/clipboard.js +73 -0
- data/public/javascripts/matrix.js +0 -2
- data/public/stylesheets/index.css +19 -0
- data/script/prod/clean-up.sql +11 -0
- data/script/query.rb +0 -1
- data/script/seed-matrix.rb +0 -1
- data/script/seed.rb +0 -1
- data/spec/features/delete_version_spec.rb +0 -1
- data/spec/features/execute_unsaved_webhook_spec.rb +56 -0
- data/spec/features/execute_webhook_spec.rb +2 -5
- data/spec/features/get_version_spec.rb +0 -1
- data/spec/lib/pact_broker/api/contracts/put_pact_params_contract_spec.rb +0 -1
- data/spec/lib/pact_broker/api/contracts/verification_contract_spec.rb +0 -1
- data/spec/lib/pact_broker/api/decorators/relationships_csv_decorator_spec.rb +0 -2
- data/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +0 -1
- data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +26 -9
- data/spec/lib/pact_broker/api/decorators/webhook_request_template_decorator_spec.rb +3 -1
- data/spec/lib/pact_broker/api/decorators/webhooks_decorator_spec.rb +3 -2
- data/spec/lib/pact_broker/api/resources/badge_spec.rb +0 -2
- data/spec/lib/pact_broker/api/resources/dashboard_spec.rb +0 -1
- data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +3 -0
- data/spec/lib/pact_broker/api/resources/group_spec.rb +0 -1
- data/spec/lib/pact_broker/api/resources/pact_spec.rb +0 -2
- data/spec/lib/pact_broker/api/resources/pacticipant_spec.rb +0 -1
- data/spec/lib/pact_broker/api/resources/tag_spec.rb +0 -2
- data/spec/lib/pact_broker/api/resources/verifications_spec.rb +9 -7
- data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +13 -15
- data/spec/lib/pact_broker/api/resources/webhooks_spec.rb +0 -1
- data/spec/lib/pact_broker/certificates/service_spec.rb +11 -5
- data/spec/lib/pact_broker/domain/group_spec.rb +0 -1
- data/spec/lib/pact_broker/domain/webhook_spec.rb +11 -4
- data/spec/lib/pact_broker/feature_toggle_spec.rb +0 -1
- data/spec/lib/pact_broker/groups/service_spec.rb +0 -1
- data/spec/lib/pact_broker/hash_refinements_spec.rb +15 -0
- data/spec/lib/pact_broker/matrix/repository_spec.rb +0 -2
- data/spec/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names_spec.rb +0 -1
- data/spec/lib/pact_broker/pacts/pact_version_spec.rb +16 -0
- data/spec/lib/pact_broker/pacts/repository_spec.rb +11 -0
- data/spec/lib/pact_broker/pacts/service_spec.rb +12 -5
- data/spec/lib/pact_broker/relationships/groupify_spec.rb +0 -2
- data/spec/lib/pact_broker/tags/repository_spec.rb +0 -1
- data/spec/lib/pact_broker/verifications/service_spec.rb +10 -3
- data/spec/lib/pact_broker/versions/repository_spec.rb +0 -1
- data/spec/lib/pact_broker/webhooks/execution_configuration_spec.rb +18 -0
- data/spec/lib/pact_broker/webhooks/job_spec.rb +21 -24
- data/spec/lib/pact_broker/webhooks/redact_logs_spec.rb +16 -5
- data/spec/lib/pact_broker/webhooks/repository_spec.rb +16 -1
- data/spec/lib/pact_broker/webhooks/service_spec.rb +23 -64
- data/spec/lib/pact_broker/webhooks/webhook_request_template_spec.rb +108 -24
- data/spec/migrations/23_pact_versions_spec.rb +3 -1
- data/spec/spec_helper.rb +4 -2
- data/spec/support/database_cleaner.rb +0 -1
- metadata +19 -5
- data/spec/support/jobs.rb +0 -12
@@ -1,9 +1,26 @@
|
|
1
|
+
require 'pact_broker/string_refinements'
|
2
|
+
|
1
3
|
module PactBroker
|
2
4
|
module Webhooks
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
module RedactLogs
|
6
|
+
HEADER_SUBSTITUTIONS = [[/(Authorization: )(.*)/i, '\1[REDACTED]'], [ /(Token: )(.*)/i, '\1[REDACTED]']]
|
7
|
+
|
8
|
+
using PactBroker::StringRefinements
|
9
|
+
|
10
|
+
def redact_logs(logs, values)
|
11
|
+
RedactLogs.call(logs, values)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.call logs, values
|
15
|
+
substitutions = HEADER_SUBSTITUTIONS + value_substitutions(values)
|
16
|
+
|
17
|
+
substitutions.reduce(logs) do | logs, (find, replace) |
|
18
|
+
logs.gsub(find, replace)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.value_substitutions(values)
|
23
|
+
values.select(&:not_blank?).collect{ | value | [value, "********"] }
|
7
24
|
end
|
8
25
|
end
|
9
26
|
end
|
@@ -3,17 +3,26 @@ require 'pact_broker/webhooks/pact_and_verification_parameters'
|
|
3
3
|
module PactBroker
|
4
4
|
module Webhooks
|
5
5
|
class Render
|
6
|
-
|
7
6
|
TEMPLATE_PARAMETER_REGEXP = /\$\{pactbroker\.[^\}]+\}/
|
8
7
|
DEFAULT_ESCAPER = lambda { |it| it }
|
9
8
|
|
9
|
+
def self.includes_parameter?(value)
|
10
|
+
value =~ TEMPLATE_PARAMETER_REGEXP
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.render_with_placeholder(value, placeholder = 'placeholder')
|
14
|
+
value.gsub(TEMPLATE_PARAMETER_REGEXP, placeholder)
|
15
|
+
end
|
16
|
+
|
10
17
|
def self.call(template, params, &escaper)
|
11
18
|
render_template(escape_params(params, escaper || DEFAULT_ESCAPER), template)
|
12
19
|
end
|
13
20
|
|
21
|
+
private
|
22
|
+
|
14
23
|
def self.render_template(params, template)
|
15
24
|
params.inject(template) do | template, (key, value) |
|
16
|
-
template.gsub(key, value)
|
25
|
+
template.gsub("${#{key}}", value)
|
17
26
|
end
|
18
27
|
end
|
19
28
|
|
@@ -7,11 +7,12 @@ require 'pact_broker/webhooks/webhook_event'
|
|
7
7
|
require 'pact_broker/webhooks/triggered_webhook'
|
8
8
|
require 'pact_broker/webhooks/latest_triggered_webhook'
|
9
9
|
require 'pact_broker/webhooks/execution'
|
10
|
+
require 'pact_broker/logging'
|
10
11
|
|
11
12
|
module PactBroker
|
12
13
|
module Webhooks
|
13
|
-
|
14
14
|
class Repository
|
15
|
+
include PactBroker::Logging
|
15
16
|
|
16
17
|
include Repositories
|
17
18
|
|
@@ -125,10 +126,15 @@ module PactBroker
|
|
125
126
|
end
|
126
127
|
|
127
128
|
def create_execution triggered_webhook, webhook_execution_result
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
129
|
+
# TriggeredWebhook may have been deleted since the webhook execution started
|
130
|
+
if TriggeredWebhook.where(id: triggered_webhook.id).any?
|
131
|
+
Execution.create(
|
132
|
+
triggered_webhook: triggered_webhook,
|
133
|
+
success: webhook_execution_result.success?,
|
134
|
+
logs: webhook_execution_result.logs)
|
135
|
+
else
|
136
|
+
logger.info("Could not save webhook execution for triggered webhook with id #{triggered_webhook.id} as it has been delete from the database")
|
137
|
+
end
|
132
138
|
end
|
133
139
|
|
134
140
|
def delete_triggered_webhooks_by_pacticipant pacticipant
|
@@ -10,11 +10,15 @@ require 'pact_broker/webhooks/webhook_event'
|
|
10
10
|
require 'pact_broker/verifications/placeholder_verification'
|
11
11
|
require 'pact_broker/pacts/placeholder_pact'
|
12
12
|
require 'pact_broker/api/decorators/webhook_decorator'
|
13
|
+
require 'pact_broker/hash_refinements'
|
14
|
+
require 'pact_broker/webhooks/execution_configuration'
|
15
|
+
require 'pact_broker/messages'
|
16
|
+
require 'pact_broker/webhooks/pact_and_verification_parameters'
|
13
17
|
|
14
18
|
module PactBroker
|
15
|
-
|
16
19
|
module Webhooks
|
17
20
|
class Service
|
21
|
+
using PactBroker::HashRefinements
|
18
22
|
|
19
23
|
RESOURCE_CREATION = PactBroker::Webhooks::TriggeredWebhook::TRIGGER_TYPE_RESOURCE_CREATION
|
20
24
|
USER = PactBroker::Webhooks::TriggeredWebhook::TRIGGER_TYPE_USER
|
@@ -22,6 +26,7 @@ module PactBroker
|
|
22
26
|
extend Repositories
|
23
27
|
extend Services
|
24
28
|
include Logging
|
29
|
+
extend PactBroker::Messages
|
25
30
|
|
26
31
|
def self.next_uuid
|
27
32
|
SecureRandom.urlsafe_base64
|
@@ -67,11 +72,9 @@ module PactBroker
|
|
67
72
|
webhook_repository.find_all
|
68
73
|
end
|
69
74
|
|
70
|
-
def self.test_execution webhook,
|
71
|
-
|
72
|
-
|
73
|
-
)
|
74
|
-
merged_options = options.merge(logging_options: logging_options)
|
75
|
+
def self.test_execution webhook, execution_configuration
|
76
|
+
merged_options = execution_configuration.with_failure_log_message("Webhook execution failed").to_hash
|
77
|
+
|
75
78
|
verification = nil
|
76
79
|
if webhook.trigger_on_provider_verification_published?
|
77
80
|
verification = verification_service.search_for_latest(webhook.consumer_name, webhook.provider_name) || PactBroker::Verifications::PlaceholderVerification.new
|
@@ -81,21 +84,8 @@ module PactBroker
|
|
81
84
|
webhook.execute(pact, verification, merged_options)
|
82
85
|
end
|
83
86
|
|
84
|
-
|
85
|
-
|
86
|
-
# triggered_webhook = webhook_repository.create_triggered_webhook(next_uuid, webhook, pact, verification, USER)
|
87
|
-
# logging_options = { failure_log_message: "Webhook execution failed"}
|
88
|
-
# webhook_execution_result = execute_triggered_webhook_now triggered_webhook, logging_options
|
89
|
-
# if webhook_execution_result.success?
|
90
|
-
# webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_SUCCESS
|
91
|
-
# else
|
92
|
-
# webhook_repository.update_triggered_webhook_status triggered_webhook, TriggeredWebhook::STATUS_FAILURE
|
93
|
-
# end
|
94
|
-
# webhook_execution_result
|
95
|
-
# end
|
96
|
-
|
97
|
-
def self.execute_triggered_webhook_now triggered_webhook, webhook_options
|
98
|
-
webhook_execution_result = triggered_webhook.execute webhook_options
|
87
|
+
def self.execute_triggered_webhook_now triggered_webhook, webhook_execution_configuration_hash
|
88
|
+
webhook_execution_result = triggered_webhook.execute webhook_execution_configuration_hash
|
99
89
|
webhook_repository.create_execution triggered_webhook, webhook_execution_result
|
100
90
|
webhook_execution_result
|
101
91
|
end
|
@@ -132,12 +122,7 @@ module PactBroker
|
|
132
122
|
begin
|
133
123
|
triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, verification, RESOURCE_CREATION)
|
134
124
|
logger.info "Scheduling job for webhook with uuid #{webhook.uuid}"
|
135
|
-
job_data = {
|
136
|
-
triggered_webhook: triggered_webhook,
|
137
|
-
webhook_context: options.fetch(:webhook_context),
|
138
|
-
logging_options: options.fetch(:logging_options),
|
139
|
-
database_connector: options.fetch(:database_connector)
|
140
|
-
}
|
125
|
+
job_data = { triggered_webhook: triggered_webhook }.deep_merge(options)
|
141
126
|
# Delay slightly to make sure the request transaction has finished before we execute the webhook
|
142
127
|
Job.perform_in(5, job_data)
|
143
128
|
rescue StandardError => e
|
@@ -166,6 +151,15 @@ module PactBroker
|
|
166
151
|
webhook_repository.find_triggered_webhooks_for_verification(verification)
|
167
152
|
end
|
168
153
|
|
154
|
+
def self.parameters
|
155
|
+
PactAndVerificationParameters::ALL.collect do | parameter |
|
156
|
+
OpenStruct.new(
|
157
|
+
name: parameter,
|
158
|
+
description: message("messages.webhooks.parameters.#{parameter}")
|
159
|
+
)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
169
163
|
private
|
170
164
|
|
171
165
|
# Dirty hack to maintain existing password or Authorization header if it is submitted with value ****
|
@@ -44,7 +44,6 @@ module PactBroker
|
|
44
44
|
associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id)
|
45
45
|
associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id)
|
46
46
|
|
47
|
-
|
48
47
|
def request_description
|
49
48
|
webhook.to_domain.request_description
|
50
49
|
end
|
@@ -10,9 +10,22 @@ module PactBroker
|
|
10
10
|
|
11
11
|
attr_reader :execution_logger, :options
|
12
12
|
|
13
|
+
class Formatter < Logger::Formatter
|
14
|
+
Format = "[%s] %s: %s\n".freeze
|
15
|
+
|
16
|
+
def call(severity, time, progname, msg)
|
17
|
+
Format % [format_datetime(time), severity, msg2str(msg)]
|
18
|
+
end
|
19
|
+
|
20
|
+
def format_datetime(time)
|
21
|
+
time.strftime(@datetime_format || "%Y-%m-%dT%H:%M:%SZ".freeze)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
13
25
|
def initialize(options)
|
14
26
|
@log_stream = StringIO.new
|
15
27
|
@execution_logger = Logger.new(log_stream)
|
28
|
+
@execution_logger.formatter = Formatter.new
|
16
29
|
@options = options
|
17
30
|
end
|
18
31
|
|
@@ -2,6 +2,7 @@ require 'pact_broker/domain/webhook_request_header'
|
|
2
2
|
require 'pact_broker/webhooks/render'
|
3
3
|
require 'cgi'
|
4
4
|
require 'pact_broker/domain/webhook_request'
|
5
|
+
require 'pact_broker/string_refinements'
|
5
6
|
|
6
7
|
module PactBroker
|
7
8
|
module Webhooks
|
@@ -9,6 +10,8 @@ module PactBroker
|
|
9
10
|
|
10
11
|
include PactBroker::Logging
|
11
12
|
include PactBroker::Messages
|
13
|
+
using PactBroker::StringRefinements
|
14
|
+
|
12
15
|
HEADERS_TO_REDACT = [/authorization/i, /token/i]
|
13
16
|
|
14
17
|
attr_accessor :method, :url, :headers, :body, :username, :password, :uuid
|
@@ -27,40 +30,30 @@ module PactBroker
|
|
27
30
|
@uuid = attributes[:uuid]
|
28
31
|
end
|
29
32
|
|
30
|
-
def build(
|
31
|
-
template_params = PactBroker::Webhooks::PactAndVerificationParameters.new(context[:pact], context[:verification], context[:webhook_context]).to_hash
|
33
|
+
def build(template_params)
|
32
34
|
attributes = {
|
33
35
|
method: http_method,
|
34
36
|
url: build_url(template_params),
|
35
|
-
headers:
|
36
|
-
username: username,
|
37
|
-
password: password,
|
37
|
+
headers: build_headers(template_params),
|
38
|
+
username: build_string(username, template_params),
|
39
|
+
password: build_string(password, template_params),
|
38
40
|
uuid: uuid,
|
39
41
|
body: build_body(template_params)
|
40
42
|
}
|
41
43
|
PactBroker::Domain::WebhookRequest.new(attributes)
|
42
44
|
end
|
43
45
|
|
44
|
-
def build_url(template_params)
|
45
|
-
URI(PactBroker::Webhooks::Render.call(url, template_params){ | value | CGI::escape(value) if !value.nil? } ).to_s
|
46
|
-
end
|
47
|
-
|
48
|
-
def build_body(template_params)
|
49
|
-
body_string = String === body ? body : body.to_json
|
50
|
-
PactBroker::Webhooks::Render.call(body_string, template_params)
|
51
|
-
end
|
52
|
-
|
53
46
|
def description
|
54
|
-
"#{http_method.upcase} #{URI(
|
47
|
+
"#{http_method.upcase} #{URI(PactBroker::Webhooks::Render.render_with_placeholder(url)).host}"
|
55
48
|
end
|
56
49
|
|
57
50
|
def display_password
|
58
|
-
password.nil? ? nil : "**********"
|
51
|
+
password.nil? ? nil : (PactBroker::Webhooks::Render.includes_parameter?(password) ? password : "**********")
|
59
52
|
end
|
60
53
|
|
61
54
|
def redacted_headers
|
62
55
|
headers.each_with_object({}) do | (name, value), new_headers |
|
63
|
-
redact = HEADERS_TO_REDACT.any?{ | pattern | name =~ pattern }
|
56
|
+
redact = HEADERS_TO_REDACT.any?{ | pattern | name =~ pattern } && !PactBroker::Webhooks::Render.includes_parameter?(value)
|
64
57
|
new_headers[name] = redact ? "**********" : value
|
65
58
|
end
|
66
59
|
end
|
@@ -69,11 +62,32 @@ module PactBroker
|
|
69
62
|
@headers = Rack::Utils::HeaderHash.new(headers)
|
70
63
|
end
|
71
64
|
|
72
|
-
private
|
73
65
|
|
74
66
|
def to_s
|
75
67
|
"#{method.upcase} #{url}, username=#{username}, password=#{display_password}, headers=#{redacted_headers}, body=#{body}"
|
76
68
|
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def build_url(template_params)
|
73
|
+
URI(PactBroker::Webhooks::Render.call(url, template_params){ | value | CGI::escape(value) if !value.nil? } ).to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_body(template_params)
|
77
|
+
body_string = String === body ? body : body.to_json
|
78
|
+
build_string(body_string, template_params)
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_headers(template_params)
|
82
|
+
headers.each_with_object(Rack::Utils::HeaderHash.new) do | (key, value), new_headers |
|
83
|
+
new_headers[key] = build_string(value, template_params)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def build_string(string, template_params)
|
88
|
+
return string if string.nil? || string.blank?
|
89
|
+
PactBroker::Webhooks::Render.call(string, template_params)
|
90
|
+
end
|
77
91
|
end
|
78
92
|
end
|
79
93
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
This header should fix the situation where using the back button shows the json/csv instead of the html.
|
4
|
+
|
5
|
+
> Unlike intermediary caches (such as CDNs), browsers typically do not implement the capability to
|
6
|
+
store multiple variations per URL. The rationale for this is that the things we typically use Vary
|
7
|
+
for (mainly Accept-Encoding and Accept-Language) do not change frequently within the context of a
|
8
|
+
single user. Accept-Encoding might (but probably doesn’t) change upon a browser upgrade, and
|
9
|
+
Accept-Language would most likely only change if you edit your operating system’s language locale
|
10
|
+
settings. It also happens to be a lot easier to implement Vary in this way, although some specification
|
11
|
+
authors believe this was a mistake.
|
12
|
+
|
13
|
+
> It’s no great loss most of the time for a browser to store only one variation, but it is important
|
14
|
+
that we don’t accidentally use a variation that isn’t valid anymore if the “varied on” data does
|
15
|
+
happen to change.
|
16
|
+
|
17
|
+
> The compromise is to treat Vary as a validator, not a key. Browsers compute cache keys in the normal
|
18
|
+
way (essentially, using the URL), and then if they score a hit, they check that the request satisfies any
|
19
|
+
ry rules that are baked into the cached response. If it doesn’t, then the browser treats the request as a
|
20
|
+
iss on the cache, and it moves on to the next layer of cache or out to the network. When a fresh response
|
21
|
+
is received, it will then overwrite the cached version, even though it’s technically a different variation.
|
22
|
+
|
23
|
+
https://www.smashingmagazine.com/2017/11/understanding-vary-header/
|
24
|
+
=end
|
25
|
+
|
26
|
+
module Rack
|
27
|
+
module PactBroker
|
28
|
+
class AddVaryHeader
|
29
|
+
def initialize app
|
30
|
+
@app = app
|
31
|
+
end
|
32
|
+
|
33
|
+
def call(env)
|
34
|
+
status, headers, body = @app.call(env)
|
35
|
+
[status, { "Vary" => "Accept" }.merge(headers || {}), body]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -3,6 +3,8 @@ module Rack
|
|
3
3
|
# If the HTML and the CSV group resources are both requested by the browser,
|
4
4
|
# Chrome gets confused by the content types, and when you click back, it tries to load the CSV
|
5
5
|
# instead of the HTML page. So we have to give the CSV resource a different URL (.csv)
|
6
|
+
# Update: this should be fixed by lib/rack/pact_broker/add_vary_header.rb, but may as well
|
7
|
+
# leave it now!
|
6
8
|
|
7
9
|
class ConvertFileExtensionToAcceptHeader
|
8
10
|
|
@@ -14,7 +14,6 @@ module Webmachine
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
17
|
unless Webmachine::Adapters::Rack.private_instance_methods.include?(:build_webmachine_request)
|
19
18
|
raise "Webmachine::Adapters::Rack no longer has the private instance method #build_webmachine_request - rack env monkey patch won't work"
|
20
19
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
/**
|
2
|
+
* Include code to enable copy-to-clipboard functionality
|
3
|
+
* and currently used on index and matrix tables
|
4
|
+
* @example in Haml
|
5
|
+
* %div.clippable
|
6
|
+
* = Text to be copied
|
7
|
+
* %button.clippy.hidden{ title: "Copy to clipboard" }
|
8
|
+
* %span.glyphicon.glyphicon-copy
|
9
|
+
*/
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Bootstrap copy-to-clipboard functionality
|
13
|
+
* @param {string} selector CSS selector of elements that require
|
14
|
+
* copy-to-clipboard functionality
|
15
|
+
*/
|
16
|
+
function initializeClipper(selector) {
|
17
|
+
const elements = $(selector);
|
18
|
+
|
19
|
+
elements.hover(function() {
|
20
|
+
$(this).children(".clippy").toggleClass("hidden");
|
21
|
+
});
|
22
|
+
|
23
|
+
elements
|
24
|
+
.children(".clippy")
|
25
|
+
.click(function() {
|
26
|
+
const clippyButton = $(this);
|
27
|
+
const text = $.trim(clippyButton.closest(selector).text());
|
28
|
+
|
29
|
+
copyToClipboard(text);
|
30
|
+
flashClipped(clippyButton);
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Copy text to clipboard using execCommand
|
36
|
+
* @see https://gist.github.com/Chalarangelo/4ff1e8c0ec03d9294628efbae49216db#file-copytoclipboard-js
|
37
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
|
38
|
+
* @param {string} text text to be copied to clipboard
|
39
|
+
*/
|
40
|
+
function copyToClipboard(text) {
|
41
|
+
const el = document.createElement('textarea');
|
42
|
+
el.value = text;
|
43
|
+
el.setAttribute('readonly', '');
|
44
|
+
el.style.position = 'absolute';
|
45
|
+
el.style.left = '-9999px';
|
46
|
+
document.body.appendChild(el);
|
47
|
+
|
48
|
+
const selected =
|
49
|
+
document.getSelection().rangeCount > 0
|
50
|
+
? document.getSelection().getRangeAt(0)
|
51
|
+
: false;
|
52
|
+
el.select();
|
53
|
+
document.execCommand('copy');
|
54
|
+
document.body.removeChild(el);
|
55
|
+
if (selected) {
|
56
|
+
document.getSelection().removeAllRanges();
|
57
|
+
document.getSelection().addRange(selected);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Flash a success tick to indicate successful copy-to-clipboard action
|
63
|
+
* @param {jQuery Element} clipButton button to copy to clipboard
|
64
|
+
*/
|
65
|
+
function flashClipped(clippyButton) {
|
66
|
+
const icon = clippyButton.children("span");
|
67
|
+
icon.attr("class", "glyphicon glyphicon-ok success");
|
68
|
+
|
69
|
+
setTimeout(
|
70
|
+
function() { icon.attr("class", "glyphicon glyphicon-copy"); },
|
71
|
+
2000
|
72
|
+
);
|
73
|
+
}
|
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
function setTextboxVisibility(selectBox, cssSelector, visibility) {
|
3
2
|
var textbox = selectBox.closest('.selector').find(cssSelector);
|
4
3
|
textbox.toggle(visibility);
|
@@ -65,7 +64,6 @@ $(document).ready(function(){
|
|
65
64
|
}
|
66
65
|
});
|
67
66
|
|
68
|
-
|
69
67
|
$('[data-toggle="tooltip"]').each(function(index, el){
|
70
68
|
$(el).tooltip({container: $(el)});
|
71
69
|
});
|
@@ -56,6 +56,25 @@ body { padding-top: 10px; }
|
|
56
56
|
word-wrap: break-word;
|
57
57
|
}
|
58
58
|
|
59
|
+
.clippable {
|
60
|
+
display: flex;
|
61
|
+
justify-content: flex-start;
|
62
|
+
}
|
63
|
+
|
64
|
+
.clippy {
|
65
|
+
padding: 0 4px;
|
66
|
+
border: none;
|
67
|
+
background: none;
|
68
|
+
}
|
69
|
+
|
70
|
+
.clippy:hover {
|
71
|
+
color: #2a6496;
|
72
|
+
}
|
73
|
+
|
74
|
+
.success {
|
75
|
+
color: #5cb85c
|
76
|
+
}
|
77
|
+
|
59
78
|
span.pact {
|
60
79
|
display: inline-block;
|
61
80
|
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
-- Deletes all verifications and pact publications that are older than 60 days,
|
2
|
+
-- and cleans up orphan pacticipant versions and their tags.
|
3
|
+
-- Also removes webhook execution history.
|
4
|
+
|
5
|
+
DELETE FROM webhook_executions;
|
6
|
+
DELETE FROM triggered_webhooks;
|
7
|
+
DELETE FROM verifications WHERE created_at < now() - '60 days'::interval;
|
8
|
+
DELETE FROM pact_publications WHERE created_at < now() - '60 days'::interval;
|
9
|
+
DELETE FROM pact_versions WHERE id NOT IN (SELECT pact_version_id FROM pact_publications UNION SELECT pact_version_id FROM verifications);
|
10
|
+
DELETE FROM tags WHERE version_id NOT IN (SELECT consumer_version_id FROM pact_publications UNION SELECT provider_version_id FROM verifications);
|
11
|
+
DELETE FROM versions WHERE id NOT IN (SELECT consumer_version_id FROM pact_publications UNION SELECT provider_version_id FROM verifications);
|
data/script/query.rb
CHANGED
data/script/seed-matrix.rb
CHANGED
data/script/seed.rb
CHANGED
@@ -23,7 +23,6 @@ PactBroker::DB.connection = connection
|
|
23
23
|
require 'pact_broker'
|
24
24
|
require 'support/test_data_builder'
|
25
25
|
|
26
|
-
|
27
26
|
require 'database/table_dependency_calculator'
|
28
27
|
PactBroker::Database::TableDependencyCalculator.call(connection).each do | table_name |
|
29
28
|
connection[table_name].delete
|
@@ -0,0 +1,56 @@
|
|
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("Foo", "1", "Bar")
|
11
|
+
allow(PactBroker.configuration).to receive(:webhook_scheme_whitelist).and_return(%w[http])
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:params) do
|
15
|
+
{
|
16
|
+
request: {
|
17
|
+
method: 'POST',
|
18
|
+
url: 'http://example.org',
|
19
|
+
headers: {'Content-Type' => 'application/json'},
|
20
|
+
body: '${pactbroker.pactUrl}'
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
let(:rack_headers) { { "CONTENT_TYPE" => "application/json", "HTTP_ACCEPT" => "application/hal+json" } }
|
25
|
+
|
26
|
+
let(:path) { "/webhooks/execute" }
|
27
|
+
let(:response_body) { JSON.parse(last_response.body, symbolize_names: true)}
|
28
|
+
|
29
|
+
subject { post(path, params.to_json, rack_headers) }
|
30
|
+
|
31
|
+
context "when the execution is successful" do
|
32
|
+
let!(:request) do
|
33
|
+
stub_request(:post, /http/).with(body: expected_webhook_url).to_return(:status => 200, body: response_body)
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:expected_webhook_url) { %r{http://example.org/pacts/provider/Bar/consumer/Foo.*} }
|
37
|
+
let(:response_body) { "webhook-response-body" }
|
38
|
+
|
39
|
+
it "performs the HTTP request" do
|
40
|
+
subject
|
41
|
+
expect(request).to have_been_made
|
42
|
+
end
|
43
|
+
|
44
|
+
it "returns a 200 response" do
|
45
|
+
expect(subject.status).to be 200
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when there is a validation error" do
|
50
|
+
let(:params) { {} }
|
51
|
+
|
52
|
+
it "returns a 400" do
|
53
|
+
expect(subject.status).to be 400
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -3,19 +3,16 @@ require 'webmock/rspec'
|
|
3
3
|
require 'rack/pact_broker/database_transaction'
|
4
4
|
|
5
5
|
describe "Execute a webhook" do
|
6
|
-
|
7
|
-
let(:td) { TestDataBuilder.new }
|
8
|
-
|
9
6
|
before do
|
10
|
-
Thread.current[:pact_broker_thread_data] = OpenStruct.new(base_url: 'http://broker')
|
11
7
|
td.create_pact_with_hierarchy("Foo", "1", "Bar")
|
12
8
|
.create_webhook(method: 'POST', body: '${pactbroker.pactUrl}')
|
13
9
|
end
|
14
10
|
|
11
|
+
let(:td) { TestDataBuilder.new }
|
15
12
|
let(:path) { "/webhooks/#{td.webhook.uuid}/execute" }
|
16
13
|
let(:response_body) { JSON.parse(last_response.body, symbolize_names: true)}
|
17
14
|
|
18
|
-
subject { post
|
15
|
+
subject { post(path) }
|
19
16
|
|
20
17
|
context "when the execution is successful" do
|
21
18
|
let!(:request) do
|
@@ -12,7 +12,6 @@ module PactBroker
|
|
12
12
|
let(:valid_params) { {success: success, providerApplicationVersion: provider_version, buildUrl: build_url} }
|
13
13
|
let(:params) { valid_params }
|
14
14
|
|
15
|
-
|
16
15
|
let(:success) { true }
|
17
16
|
let(:provider_version) { "4.5.6" }
|
18
17
|
let(:build_url) { 'http://foo' }
|
@@ -12,10 +12,8 @@ module PactBroker
|
|
12
12
|
let(:pact) { TestDataBuilder.new.create_pact_with_hierarchy("My Consumer", "1.0", "My Provider").and_return(:pact) }
|
13
13
|
let(:pacts) { [pact]}
|
14
14
|
|
15
|
-
|
16
15
|
subject { RelationshipsCsvDecorator.new(pacts) }
|
17
16
|
|
18
|
-
|
19
17
|
describe "#to_csv" do
|
20
18
|
|
21
19
|
let(:line_1) { '1,My Consumer,1,3158419,0,1,2,0,0,0,0,0,0,0,0'}
|