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 +4 -4
- data/.github/workflows/release_gem.yml +1 -0
- data/CHANGELOG.md +12 -0
- data/DEVELOPER_DOCUMENTATION.md +13 -0
- data/config.ru +1 -1
- data/docker-compose-dev-postgres.yml +9 -1
- data/lib/pact_broker/api/resources/default_base_resource.rb +5 -0
- data/lib/pact_broker/app.rb +1 -1
- data/lib/pact_broker/configuration.rb +4 -0
- data/lib/pact_broker/db/data_migrations/set_extra_columns_for_tags.rb +2 -2
- data/lib/pact_broker/ui/view_models/index_item.rb +10 -2
- data/lib/pact_broker/ui/view_models/matrix_line.rb +2 -2
- data/lib/pact_broker/ui/views/index/show-with-tags.haml +6 -6
- data/lib/pact_broker/ui/views/matrix/show.haml +2 -2
- data/lib/pact_broker/version.rb +1 -1
- data/lib/rack/pact_broker/set_base_url.rb +35 -5
- data/public/javascripts/clipboard.js +8 -2
- data/spec/lib/rack/pact_broker/set_base_url_spec.rb +86 -0
- data/tasks/database.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dcfded08b30d1554aaf7160b039a69d58bcb16d9c591938949d4c7799f84d29
|
4
|
+
data.tar.gz: 85f93ef0a51d494fc382efe6398e4c89d866d00c6c5efccf8674a845ccf3fe33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48de943377489d86ebadff9afac10cf9a725347ed68a5d86cc7a8e563ace8359f33f44df2db658a6d2a29dd1026a8dfa522e5cbfdcb04678792db234110c0d5e
|
7
|
+
data.tar.gz: 19c55a44ab12d418d1af72d138a45ea2b3269ec4c21c5644c81a3159d88ac703f2d885a700aa770ff6844e70a2407ab6a9efb75de9785b90ba7a8de8f41d1320
|
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
|
|
data/DEVELOPER_DOCUMENTATION.md
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
|
data/lib/pact_broker/app.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
-
|
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?
|
@@ -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.
|
44
|
-
- if index_item.
|
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.
|
68
|
-
- if index_item.
|
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
|
data/lib/pact_broker/version.rb
CHANGED
@@ -1,22 +1,52 @@
|
|
1
1
|
module Rack
|
2
2
|
module PactBroker
|
3
3
|
class SetBaseUrl
|
4
|
-
|
4
|
+
X_FORWARDED_PATTERN = /_X_FORWARDED_/.freeze
|
5
|
+
|
6
|
+
def initialize app, base_urls
|
5
7
|
@app = app
|
6
|
-
@
|
8
|
+
@base_urls = base_urls
|
7
9
|
end
|
8
10
|
|
9
|
-
def call
|
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" =>
|
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, :
|
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
|
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
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.
|
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-
|
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
|