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.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.dockerignore +2 -0
  3. data/.github/FUNDING.yml +4 -0
  4. data/.travis.yml +1 -1
  5. data/CHANGELOG.md +34 -0
  6. data/DEVELOPER_DOCUMENTATION.md +24 -1
  7. data/DEVELOPER_SETUP.md +40 -20
  8. data/Dockerfile +22 -0
  9. data/db/migrations/000028_create_all_pact_publications.rb +0 -1
  10. data/db/migrations/20180311_optimise_head_matrix.rb +0 -1
  11. data/lib/pact/doc/doc_file.rb +0 -1
  12. data/lib/pact_broker/api/contracts/webhook_contract.rb +1 -1
  13. data/lib/pact_broker/api/decorators/decorator_context.rb +5 -5
  14. data/lib/pact_broker/api/decorators/pact_decorator.rb +0 -1
  15. data/lib/pact_broker/api/decorators/pact_pacticipant_decorator.rb +0 -1
  16. data/lib/pact_broker/api/decorators/tag_decorator.rb +0 -1
  17. data/lib/pact_broker/api/decorators/webhook_decorator.rb +0 -1
  18. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +8 -5
  19. data/lib/pact_broker/api/decorators/webhook_request_template_decorator.rb +1 -4
  20. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +1 -2
  21. data/lib/pact_broker/api/resources/base_resource.rb +0 -1
  22. data/lib/pact_broker/api/resources/error_handler.rb +14 -7
  23. data/lib/pact_broker/api/resources/pact.rb +4 -9
  24. data/lib/pact_broker/api/resources/pact_webhooks.rb +0 -1
  25. data/lib/pact_broker/api/resources/verifications.rb +4 -8
  26. data/lib/pact_broker/api/resources/webhook.rb +4 -8
  27. data/lib/pact_broker/api/resources/webhook_execution.rb +36 -17
  28. data/lib/pact_broker/api/resources/webhook_execution_methods.rb +13 -0
  29. data/lib/pact_broker/api/resources/webhook_resource_methods.rb +8 -15
  30. data/lib/pact_broker/api/resources/webhooks.rb +3 -12
  31. data/lib/pact_broker/api.rb +1 -1
  32. data/lib/pact_broker/app.rb +2 -0
  33. data/lib/pact_broker/domain/webhook.rb +29 -18
  34. data/lib/pact_broker/hash_refinements.rb +13 -0
  35. data/lib/pact_broker/locale/en.yml +16 -0
  36. data/lib/pact_broker/logging.rb +1 -1
  37. data/lib/pact_broker/pacts/diff.rb +0 -1
  38. data/lib/pact_broker/pacts/pact_version.rb +13 -7
  39. data/lib/pact_broker/pacts/repository.rb +4 -6
  40. data/lib/pact_broker/pacts/service.rb +3 -4
  41. data/lib/pact_broker/repositories/helpers.rb +1 -1
  42. data/lib/pact_broker/string_refinements.rb +13 -0
  43. data/lib/pact_broker/tags/repository.rb +0 -1
  44. data/lib/pact_broker/ui/views/index/_css_and_js.haml +1 -0
  45. data/lib/pact_broker/ui/views/index/show-with-tags.haml +8 -2
  46. data/lib/pact_broker/ui/views/matrix/show.haml +12 -2
  47. data/lib/pact_broker/verifications/repository.rb +0 -1
  48. data/lib/pact_broker/verifications/service.rb +7 -5
  49. data/lib/pact_broker/version.rb +1 -1
  50. data/lib/pact_broker/versions/parse_semantic_version.rb +0 -1
  51. data/lib/pact_broker/webhooks/execution.rb +0 -1
  52. data/lib/pact_broker/webhooks/execution_configuration.rb +45 -0
  53. data/lib/pact_broker/webhooks/job.rb +5 -8
  54. data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +39 -12
  55. data/lib/pact_broker/webhooks/redact_logs.rb +21 -4
  56. data/lib/pact_broker/webhooks/render.rb +11 -2
  57. data/lib/pact_broker/webhooks/repository.rb +11 -5
  58. data/lib/pact_broker/webhooks/service.rb +21 -27
  59. data/lib/pact_broker/webhooks/triggered_webhook.rb +0 -1
  60. data/lib/pact_broker/webhooks/webhook_request_logger.rb +13 -0
  61. data/lib/pact_broker/webhooks/webhook_request_template.rb +32 -18
  62. data/lib/rack/pact_broker/add_vary_header.rb +39 -0
  63. data/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb +2 -0
  64. data/lib/webmachine/rack_adapter_monkey_patch.rb +0 -1
  65. data/public/javascripts/clipboard.js +73 -0
  66. data/public/javascripts/matrix.js +0 -2
  67. data/public/stylesheets/index.css +19 -0
  68. data/script/prod/clean-up.sql +11 -0
  69. data/script/query.rb +0 -1
  70. data/script/seed-matrix.rb +0 -1
  71. data/script/seed.rb +0 -1
  72. data/spec/features/delete_version_spec.rb +0 -1
  73. data/spec/features/execute_unsaved_webhook_spec.rb +56 -0
  74. data/spec/features/execute_webhook_spec.rb +2 -5
  75. data/spec/features/get_version_spec.rb +0 -1
  76. data/spec/lib/pact_broker/api/contracts/put_pact_params_contract_spec.rb +0 -1
  77. data/spec/lib/pact_broker/api/contracts/verification_contract_spec.rb +0 -1
  78. data/spec/lib/pact_broker/api/decorators/relationships_csv_decorator_spec.rb +0 -2
  79. data/spec/lib/pact_broker/api/decorators/verification_decorator_spec.rb +0 -1
  80. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +26 -9
  81. data/spec/lib/pact_broker/api/decorators/webhook_request_template_decorator_spec.rb +3 -1
  82. data/spec/lib/pact_broker/api/decorators/webhooks_decorator_spec.rb +3 -2
  83. data/spec/lib/pact_broker/api/resources/badge_spec.rb +0 -2
  84. data/spec/lib/pact_broker/api/resources/dashboard_spec.rb +0 -1
  85. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +3 -0
  86. data/spec/lib/pact_broker/api/resources/group_spec.rb +0 -1
  87. data/spec/lib/pact_broker/api/resources/pact_spec.rb +0 -2
  88. data/spec/lib/pact_broker/api/resources/pacticipant_spec.rb +0 -1
  89. data/spec/lib/pact_broker/api/resources/tag_spec.rb +0 -2
  90. data/spec/lib/pact_broker/api/resources/verifications_spec.rb +9 -7
  91. data/spec/lib/pact_broker/api/resources/webhook_execution_spec.rb +13 -15
  92. data/spec/lib/pact_broker/api/resources/webhooks_spec.rb +0 -1
  93. data/spec/lib/pact_broker/certificates/service_spec.rb +11 -5
  94. data/spec/lib/pact_broker/domain/group_spec.rb +0 -1
  95. data/spec/lib/pact_broker/domain/webhook_spec.rb +11 -4
  96. data/spec/lib/pact_broker/feature_toggle_spec.rb +0 -1
  97. data/spec/lib/pact_broker/groups/service_spec.rb +0 -1
  98. data/spec/lib/pact_broker/hash_refinements_spec.rb +15 -0
  99. data/spec/lib/pact_broker/matrix/repository_spec.rb +0 -2
  100. data/spec/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names_spec.rb +0 -1
  101. data/spec/lib/pact_broker/pacts/pact_version_spec.rb +16 -0
  102. data/spec/lib/pact_broker/pacts/repository_spec.rb +11 -0
  103. data/spec/lib/pact_broker/pacts/service_spec.rb +12 -5
  104. data/spec/lib/pact_broker/relationships/groupify_spec.rb +0 -2
  105. data/spec/lib/pact_broker/tags/repository_spec.rb +0 -1
  106. data/spec/lib/pact_broker/verifications/service_spec.rb +10 -3
  107. data/spec/lib/pact_broker/versions/repository_spec.rb +0 -1
  108. data/spec/lib/pact_broker/webhooks/execution_configuration_spec.rb +18 -0
  109. data/spec/lib/pact_broker/webhooks/job_spec.rb +21 -24
  110. data/spec/lib/pact_broker/webhooks/redact_logs_spec.rb +16 -5
  111. data/spec/lib/pact_broker/webhooks/repository_spec.rb +16 -1
  112. data/spec/lib/pact_broker/webhooks/service_spec.rb +23 -64
  113. data/spec/lib/pact_broker/webhooks/webhook_request_template_spec.rb +108 -24
  114. data/spec/migrations/23_pact_versions_spec.rb +3 -1
  115. data/spec/spec_helper.rb +4 -2
  116. data/spec/support/database_cleaner.rb +0 -1
  117. metadata +19 -5
  118. 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
- class RedactLogs
4
- def self.call logs
5
- logs.gsub(/(Authorization: )(.*)/i,'\1[REDACTED]')
6
- .gsub(/(Token: )(.*)/i,'\1[REDACTED]')
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
- Execution.create(
129
- triggered_webhook: triggered_webhook,
130
- success: webhook_execution_result.success?,
131
- logs: webhook_execution_result.logs)
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, options
71
- logging_options = options[:logging_options].merge(
72
- failure_log_message: "Webhook execution failed",
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
- # # TODO delete?
85
- # def self.execute_webhook_now webhook, pact, verification = nil
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(context)
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: 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(url.gsub(PactBroker::Webhooks::Render::TEMPLATE_PARAMETER_REGEXP, 'placeholder')).host}"
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
@@ -17,5 +17,4 @@ puts time.real
17
17
 
18
18
  __END__
19
19
 
20
-
21
20
  select * from matrix
@@ -88,4 +88,3 @@ TestDataBuilder.new
88
88
  # .create_pact
89
89
  # .create_verification(provider_version: '57fa24e44efc4d8aa42bb855a8217f145b5b1b5b', success: true)
90
90
 
91
-
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
@@ -16,7 +16,6 @@ describe "Delete version" do
16
16
  .create_consumer_version("1.2.4")
17
17
  end
18
18
 
19
-
20
19
  it "returns a 200 HAL JSON response" do
21
20
  expect(subject.status).to eq 204
22
21
  end
@@ -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 path; last_response }
15
+ subject { post(path) }
19
16
 
20
17
  context "when the execution is successful" do
21
18
  let!(:request) do
@@ -18,7 +18,6 @@ describe "Get version" do
18
18
  .create_consumer_version("1.2.4")
19
19
  end
20
20
 
21
-
22
21
  it "returns a 200 HAL JSON response" do
23
22
  expect(subject).to be_a_hal_json_success_response
24
23
  end
@@ -83,7 +83,6 @@ module PactBroker
83
83
  end
84
84
  end
85
85
 
86
-
87
86
  context "with a consumer name in the pact that does not match the consumer name in the path" do
88
87
  let(:attributes) do
89
88
  valid_attributes.merge(consumer_name: "another consumer")
@@ -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'}
@@ -33,7 +33,6 @@ module PactBroker
33
33
 
34
34
  let(:options) { { user_options: { base_url: 'http://example.org' } } }
35
35
 
36
-
37
36
  let(:json) { VerificationDecorator.new(verification).to_json(options) }
38
37
 
39
38
  subject { JSON.parse json, symbolize_names: true }