pact_broker 2.78.1 → 2.79.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b7b3f32acd207f0828dbe2c81fbffb7ed26fc7c522f04719d6d72a123a37331
4
- data.tar.gz: 28776d82bfb587c908fd20b1570d6b94c58cb8669c2aad5179308f38d512d0ce
3
+ metadata.gz: 9dcfded08b30d1554aaf7160b039a69d58bcb16d9c591938949d4c7799f84d29
4
+ data.tar.gz: 85f93ef0a51d494fc382efe6398e4c89d866d00c6c5efccf8674a845ccf3fe33
5
5
  SHA512:
6
- metadata.gz: 0a296391274edd0473b51db73d13d8e22f0519ca4b01e6d6fe3217f0881baead02496bf5423c15dfe5d1a7a9eae43da65eccbe94a967b2f51dd6f62e977fdea9
7
- data.tar.gz: 3f9bab9d45e6315e4dbec1ee575f5a40c929308af166db3028c3f471bc59b42e8a6631c774513e864523ff0665b7061014cae8ddf67e7c696912a603df53b21f
6
+ metadata.gz: 48de943377489d86ebadff9afac10cf9a725347ed68a5d86cc7a8e563ace8359f33f44df2db658a6d2a29dd1026a8dfa522e5cbfdcb04678792db234110c0d5e
7
+ data.tar.gz: 19c55a44ab12d418d1af72d138a45ea2b3269ec4c21c5644c81a3159d88ac703f2d885a700aa770ff6844e70a2407ab6a9efb75de9785b90ba7a8de8f41d1320
@@ -4,6 +4,7 @@ on:
4
4
  repository_dispatch:
5
5
  types:
6
6
  - release-triggered
7
+ workflow_dispatch:
7
8
 
8
9
  jobs:
9
10
  release:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ <a name="v2.79.0"></a>
2
+ ### v2.79.0 (2021-02-25)
3
+
4
+ #### Features
5
+
6
+ * allow multiple base URLs to be configured ([6d0dc8b3](/../../commit/6d0dc8b3))
7
+ * copy full git sha rather than abbreviated one when clicking the copy button next to an application version ([dae9cae5](/../../commit/dae9cae5))
8
+
9
+ #### Bug Fixes
10
+
11
+ * fix missing verification status colours in matrix rows ([687d4647](/../../commit/687d4647))
12
+
1
13
  <a name="v2.78.1"></a>
2
14
  ### v2.78.1 (2021-02-18)
3
15
 
@@ -148,3 +148,16 @@ In the beginning, I made a lot of Sequel models based on views that pulled in th
148
148
  * Write a spec for the repository.
149
149
  * Go back and make the original feature spec pass.
150
150
  * Profit.
151
+
152
+ ## Writing Data migrations
153
+
154
+ The same database may be used by multiple application instances to support highly available set ups and no downtime deployments. This can lead to the situation where the database migrations have been applied, but new data is written to the database by old application code, which may lead to some columns not being populated. The mitigation to this problem is to run the *data* migrations only each time an application instance starts up. This ensures that that any data inserted into the database by a previous version of the application are migrated. This is done automatically in the `PactBroker::App` class.
155
+
156
+ If you write a schema migration that then requires a data migration to populate or update any columns:
157
+
158
+ * Create a data migrations class in `lib/pact_broker/db/data_migrations`, copying the existing pattern.
159
+ * Add a call to the new class at the end of `lib/pact_broker/db/migrate_data.rb`
160
+ * Make sure you check for the existance of the required columns, because you don't know which version of the database might be running with this code.
161
+ * Add a null check (eg. `db[:my_table].where(my_column: nil).update(...)`) where appropriate to make sure that the data migration doesn't run more than once.
162
+ * Don't use any Sequel Models, as this will run before the model classes are loaded, and migrations should never depend on Models because models change as the schema migrations are applied.
163
+ * Create a migration file in `db/migrations` that calls the data migration (eg. like `db/migrations/20190603_migrate_webhook_headers.rb`)
data/config.ru CHANGED
@@ -27,7 +27,7 @@ app = PactBroker::App.new do | config |
27
27
  config.webhook_scheme_whitelist = ['http', 'https']
28
28
  config.webhook_http_method_whitelist = ['GET', 'POST']
29
29
  config.webhook_http_code_success = [200, 201, 202, 203, 204, 205, 206]
30
- #config.base_url = ENV['PACT_BROKER_BASE_URL']
30
+ config.base_url = ENV['PACT_BROKER_BASE_URL']
31
31
 
32
32
  database_logger = PactBroker::DB::LogQuietener.new(config.logger)
33
33
  config.database_connection = Sequel.connect(DATABASE_URL, DB_OPTIONS.merge(logger: database_logger))
@@ -23,7 +23,9 @@ services:
23
23
  environment:
24
24
  DATABASE_URL: postgres://postgres:postgres@postgres/postgres
25
25
  PACT_BROKER_HIDE_PACTFLOW_MESSAGES: 'true'
26
- command: dockerize -wait tcp://postgres:5432 /home/start.sh
26
+ PACT_BROKER_BASE_URL: 'http://localhost:9292 http://pact-broker:9292'
27
+ command: -wait tcp://postgres:5432 /usr/local/bin/start
28
+ entrypoint: dockerize
27
29
  volumes:
28
30
  - ./lib:/home/lib
29
31
  - ./db:/home/db
@@ -31,5 +33,11 @@ services:
31
33
  - ./tasks:/home/tasks
32
34
  - ./Rakefile:/home/Rakefile
33
35
 
36
+ shell:
37
+ image: ruby:2.5.3-alpine
38
+ depends_on:
39
+ - pact-broker
40
+ entrypoint: /bin/sh
41
+
34
42
  volumes:
35
43
  postgres-volume:
@@ -68,6 +68,11 @@ module PactBroker
68
68
  alias_method :path_info, :identifier_from_path
69
69
 
70
70
  def base_url
71
+ # Have to use something for the base URL here - we can't use an empty string as we can in the UI.
72
+ # Can't work out if cache poisoning is a vulnerability for APIs or not.
73
+ # Using the request base URI as a fallback if the base_url is not configured may be a vulnerability,
74
+ # but the documentation recommends that the
75
+ # base_url should be set in the configuration to mitigate this.
71
76
  request.env["pactbroker.base_url"] || request.base_uri.to_s.chomp('/')
72
77
  end
73
78
 
@@ -191,7 +191,7 @@ module PactBroker
191
191
  @app_builder.use Rack::Static, :urls => ["/favicon.ico"], :root => PactBroker.project_root.join("public/images"), header_rules: [[:all, {'Content-Type' => 'image/x-icon'}]]
192
192
  @app_builder.use Rack::PactBroker::ConvertFileExtensionToAcceptHeader
193
193
  # Rack::PactBroker::SetBaseUrl needs to be before the Rack::PactBroker::HalBrowserRedirect
194
- @app_builder.use Rack::PactBroker::SetBaseUrl, configuration.base_url
194
+ @app_builder.use Rack::PactBroker::SetBaseUrl, configuration.base_urls
195
195
 
196
196
  if configuration.use_hal_browser
197
197
  logger.info "Mounting HAL browser"
@@ -255,6 +255,10 @@ module PactBroker
255
255
  @webhook_host_whitelist = parse_space_delimited_string_list_property('webhook_host_whitelist', webhook_host_whitelist)
256
256
  end
257
257
 
258
+ def base_urls
259
+ base_url ? base_url.split(" ") : []
260
+ end
261
+
258
262
  def warning_error_classes
259
263
  warning_error_class_names.collect do | class_name |
260
264
  begin
@@ -9,7 +9,7 @@ module PactBroker
9
9
  def self.call(connection)
10
10
  if columns_exist?(connection, :tags, [:version_id, :pacticipant_id]) &&
11
11
  columns_exist?(connection, :versions, [:id, :pacticipant_id])
12
- connection[:tags].update(
12
+ connection[:tags].where(pacticipant_id: nil).update(
13
13
  pacticipant_id: connection[:versions].select(:pacticipant_id)
14
14
  .where(Sequel[:versions][:id] => Sequel[:tags][:version_id])
15
15
  )
@@ -17,7 +17,7 @@ module PactBroker
17
17
 
18
18
  if columns_exist?(connection, :tags, [:version_id, :version_order]) &&
19
19
  columns_exist?(connection, :versions, [:id, :order])
20
- connection[:tags].update(
20
+ connection[:tags].where(version_order: nil).update(
21
21
  version_order: connection[:versions].select(:order)
22
22
  .where(Sequel[:versions][:id] => Sequel[:tags][:version_id])
23
23
  )
@@ -30,7 +30,11 @@ module PactBroker
30
30
  end
31
31
 
32
32
  def consumer_version_number
33
- PactBroker::Versions::AbbreviateNumber.call(@relationship.consumer_version_number)
33
+ @relationship.consumer_version_number
34
+ end
35
+
36
+ def display_consumer_version_number
37
+ PactBroker::Versions::AbbreviateNumber.call(consumer_version_number)
34
38
  end
35
39
 
36
40
  def consumer_version_order
@@ -38,7 +42,11 @@ module PactBroker
38
42
  end
39
43
 
40
44
  def provider_version_number
41
- PactBroker::Versions::AbbreviateNumber.call(@relationship.provider_version_number)
45
+ @relationship.provider_version_number
46
+ end
47
+
48
+ def display_provider_version_number
49
+ PactBroker::Versions::AbbreviateNumber.call(provider_version_number)
42
50
  end
43
51
 
44
52
  def latest?
@@ -188,8 +188,8 @@ module PactBroker
188
188
 
189
189
  def verification_status_class
190
190
  case @line.success
191
- when true then 'success'
192
- when false then 'danger'
191
+ when true then 'table-success'
192
+ when false then 'table-danger'
193
193
  else ''
194
194
  end
195
195
  end
@@ -39,9 +39,9 @@
39
39
  %a{:href => index_item.consumer_group_url }
40
40
  = index_item.consumer_name
41
41
  %td.consumer-version-number{"data-text": index_item.consumer_version_order}
42
- %div.clippable
43
- = index_item.consumer_version_number
44
- - if index_item.consumer_version_number
42
+ %div.clippable{"data-clippable": index_item.consumer_version_number}
43
+ = index_item.display_consumer_version_number
44
+ - if index_item.display_consumer_version_number
45
45
  %button.clippy.invisible{ title: "Copy to clipboard" }
46
46
  %span.copy-icon
47
47
  - if index_item.latest?
@@ -63,9 +63,9 @@
63
63
  %a{ href: index_item.provider_group_url }
64
64
  = index_item.provider_name
65
65
  %td.provider-version-number
66
- %div.clippable
67
- = index_item.provider_version_number
68
- - if index_item.provider_version_number
66
+ %div.clippable{"data-clippable": index_item.provider_version_number}
67
+ = index_item.display_provider_version_number
68
+ - if index_item.display_provider_version_number
69
69
  %button.clippy.invisible{ title: "Copy to clipboard" }
70
70
  %span.copy-icon
71
71
  - if index_item.provider_version_branch
@@ -112,7 +112,7 @@
112
112
  %a{href: line.consumer_name_url}
113
113
  = line.consumer_name
114
114
  %td.consumer-version{'data-sort-value' => line.consumer_version_order, 'data-consumer-version-id' => line.consumer_version_id}
115
- %div.clippable
115
+ %div.clippable{'data-clippable': line.consumer_version_number}
116
116
  %a{href: line.consumer_version_number_url}
117
117
  = line.display_consumer_version_number
118
118
  - if line.display_consumer_version_number
@@ -144,7 +144,7 @@
144
144
  %a{href: line.provider_name_url}
145
145
  = line.provider_name
146
146
  %td.provider-version{'data-sort-value' => line.provider_version_order, 'data-provider-version-id' => line.provider_version_id }
147
- %div.clippable
147
+ %div.clippable{'data-clippable': line.provider_version_number}
148
148
  %a{href: line.provider_version_number_url}
149
149
  = line.display_provider_version_number
150
150
  - if line.display_provider_version_number
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.78.1'
2
+ VERSION = '2.79.0'
3
3
  end
@@ -1,22 +1,52 @@
1
1
  module Rack
2
2
  module PactBroker
3
3
  class SetBaseUrl
4
- def initialize app, base_url
4
+ X_FORWARDED_PATTERN = /_X_FORWARDED_/.freeze
5
+
6
+ def initialize app, base_urls
5
7
  @app = app
6
- @base_url = base_url
8
+ @base_urls = base_urls
7
9
  end
8
10
 
9
- def call env
11
+ def call(env)
10
12
  if env["pactbroker.base_url"]
11
13
  app.call(env)
12
14
  else
13
- app.call(env.merge("pactbroker.base_url" => base_url))
15
+ app.call(env.merge("pactbroker.base_url" => select_matching_base_url(env)))
14
16
  end
15
17
  end
16
18
 
17
19
  private
18
20
 
19
- attr_reader :app, :base_url
21
+ attr_reader :app, :base_urls
22
+
23
+ def select_matching_base_url(env)
24
+ if base_urls.size > 1
25
+ return matching_base_url_considering_x_forwarded_headers(env) ||
26
+ matching_base_url_not_considering_x_forwarded_headers(env) ||
27
+ default_base_url
28
+ end
29
+ default_base_url
30
+ end
31
+
32
+ def default_base_url
33
+ base_urls.first
34
+ end
35
+
36
+ def matching_base_url_considering_x_forwarded_headers(env)
37
+ matching_base_url(env)
38
+ end
39
+
40
+ def matching_base_url_not_considering_x_forwarded_headers(env)
41
+ matching_base_url(env.reject{ |k, _| k =~ X_FORWARDED_PATTERN} )
42
+ end
43
+
44
+ def matching_base_url(env)
45
+ request_base_url = Rack::Request.new(env).base_url
46
+ if base_urls.include?(request_base_url)
47
+ request_base_url
48
+ end
49
+ end
20
50
  end
21
51
  end
22
52
  end
@@ -24,9 +24,15 @@ function initializeClipper(selector) {
24
24
  .children(".clippy")
25
25
  .click(function() {
26
26
  const clippyButton = $(this);
27
- const text = $.trim(clippyButton.closest(selector).text());
27
+ const clipTarget = clippyButton.closest(selector);
28
+ let text = null;
29
+ if(clipTarget.data('clippable')) {
30
+ text = clipTarget.data('clippable');
31
+ } else {
32
+ text = clippyButton.closest(selector).text();
33
+ }
34
+ copyToClipboard($.trim(text));
28
35
 
29
- copyToClipboard(text);
30
36
  flashClipped(clippyButton);
31
37
  });
32
38
  }
@@ -0,0 +1,86 @@
1
+ require 'rack/pact_broker/set_base_url'
2
+
3
+ module Rack
4
+ module PactBroker
5
+ describe SetBaseUrl do
6
+ let(:base_urls) { ["http://pact-broker"] }
7
+ let(:rack_env) { {} }
8
+ let(:target_app) { double('app', call: [200, {}, []]) }
9
+ let(:app) { SetBaseUrl.new(target_app, base_urls) }
10
+
11
+ subject { get("/", {}, rack_env) }
12
+
13
+ describe "#call" do
14
+ context "when the base_url is already set" do
15
+ let(:rack_env) { { "pactbroker.base_url" => "http://foo"} }
16
+
17
+ it "does not overwrite it" do
18
+ expect(target_app).to receive(:call).with(hash_including("pactbroker.base_url" => "http://foo"))
19
+ subject
20
+ end
21
+ end
22
+
23
+ context "when there is one base URL" do
24
+ it "sets that base_url" do
25
+ expect(target_app).to receive(:call).with(hash_including("pactbroker.base_url" => "http://pact-broker"))
26
+ subject
27
+ end
28
+ end
29
+
30
+ context "when there are no base URLs" do
31
+ let(:base_urls) { [] }
32
+
33
+ it "sets the base URL to nil" do
34
+ expect(target_app).to receive(:call).with(hash_including("pactbroker.base_url" => nil))
35
+ subject
36
+ end
37
+ end
38
+
39
+ context "when there are multiple base URLs" do
40
+ let(:base_urls) { ["https://foo", "https://pact-broker-external", "http://pact-broker-internal"] }
41
+
42
+ let(:host) { "pact-broker-internal" }
43
+ let(:scheme) { "http" }
44
+ let(:forwarded_host) { "pact-broker-external" }
45
+ let(:forwarded_scheme) { "https" }
46
+ let(:rack_env) do
47
+ {
48
+ Rack::HTTP_HOST => host,
49
+ Rack::RACK_URL_SCHEME => scheme,
50
+ "HTTP_X_FORWARDED_HOST" => forwarded_host,
51
+ "HTTP_X_FORWARDED_SCHEME" => forwarded_scheme
52
+ }
53
+ end
54
+
55
+ context "when the base URL created taking any X-Forwarded headers into account matches one of the base URLs" do
56
+ it "uses that base URL" do
57
+ expect(target_app).to receive(:call).with(hash_including("pactbroker.base_url" => "https://pact-broker-external"))
58
+ subject
59
+ end
60
+ end
61
+
62
+ context "when the base URL created NOT taking the X-Forwarded headers into account matches one of the base URLs (potential cache poisoning)" do
63
+ let(:forwarded_host) { "pact-broker-external-wrong" }
64
+
65
+ it "uses that base URL" do
66
+ expect(target_app).to receive(:call).with(hash_including("pactbroker.base_url" => "http://pact-broker-internal"))
67
+ subject
68
+ end
69
+ end
70
+
71
+ context "when neither base URL matches the base URLs (potential cache poisoning)" do
72
+ before do
73
+ rack_env["HTTP_HOST"] = "silly-buggers-1"
74
+ rack_env["HTTP_X_FORWARDED_HOST"] = "silly-buggers-1"
75
+ end
76
+
77
+ it "uses the first base URL" do
78
+ expect(target_app).to receive(:call).with(hash_including("pactbroker.base_url" => "https://foo"))
79
+ subject
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
data/tasks/database.rb CHANGED
@@ -14,7 +14,7 @@ module PactBroker
14
14
  extend self
15
15
 
16
16
  def migrate target = nil
17
- opts = target ? {target: target} : {}
17
+ opts = target ? { target: target } : {}
18
18
  PactBroker::DB::Migrate.call(database, opts)
19
19
  end
20
20
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.78.1
4
+ version: 2.79.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bethany Skurrie
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-02-18 00:00:00.000000000 Z
13
+ date: 2021-02-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -1431,6 +1431,7 @@ files:
1431
1431
  - spec/lib/rack/pact_broker/database_transaction_spec.rb
1432
1432
  - spec/lib/rack/pact_broker/invalid_uri_protection_spec.rb
1433
1433
  - spec/lib/rack/pact_broker/request_target_spec.rb
1434
+ - spec/lib/rack/pact_broker/set_base_url_spec.rb
1434
1435
  - spec/lib/rack/pact_broker/use_when_spec.rb
1435
1436
  - spec/lib/sequel/plugins/insert_ignore_spec.rb
1436
1437
  - spec/lib/sequel/plugins/upsert_spec.rb
@@ -1848,6 +1849,7 @@ test_files:
1848
1849
  - spec/lib/rack/pact_broker/database_transaction_spec.rb
1849
1850
  - spec/lib/rack/pact_broker/invalid_uri_protection_spec.rb
1850
1851
  - spec/lib/rack/pact_broker/request_target_spec.rb
1852
+ - spec/lib/rack/pact_broker/set_base_url_spec.rb
1851
1853
  - spec/lib/rack/pact_broker/use_when_spec.rb
1852
1854
  - spec/lib/sequel/plugins/insert_ignore_spec.rb
1853
1855
  - spec/lib/sequel/plugins/upsert_spec.rb