Skip to content

Commit a34d5f7

Browse files
committed
feat: allow verification status badges to be served via a redirect instead of proxying the response
1 parent 3ae072b commit a34d5f7

File tree

5 files changed

+90
-33
lines changed

5 files changed

+90
-33
lines changed

lib/pact_broker/api/resources/badge.rb

+13-4
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,22 @@
55
module PactBroker
66
module Api
77
module Resources
8-
98
class Badge < BaseResource
109

1110
def allowed_methods
12-
["GET", "OPTIONS", "OPTIONS"]
11+
["GET", "OPTIONS"]
1312
end
1413

1514
def content_types_provided
1615
[['image/svg+xml', :to_svg]]
1716
end
1817

1918
def resource_exists?
19+
!badge_service.can_provide_badge_using_redirect?
20+
end
21+
22+
# Only called if resource_exists? returns false
23+
def previously_existed?
2024
true
2125
end
2226

@@ -28,13 +32,18 @@ def forbidden?
2832
false
2933
end
3034

31-
private
32-
3335
def to_svg
3436
response.headers['Cache-Control'] = 'no-cache'
3537
comment + badge_service.pact_verification_badge(pact, label, initials, pseudo_branch_verification_status)
3638
end
3739

40+
def moved_temporarily?
41+
response.headers['Cache-Control'] = 'no-cache'
42+
badge_service.pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status)
43+
end
44+
45+
private
46+
3847
def pact
3948
@pact ||= pact_service.find_latest_pact(identifier_from_path)
4049
end

lib/pact_broker/badges/service.rb

+14-7
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,21 @@ module Service
1515
SPACE_DASH_UNDERSCORE = /[\s_\-]/
1616
CACHE = {}
1717

18+
def can_provide_badge_using_redirect?
19+
PactBroker.configuration.badge_provider_mode == :redirect && !!PactBroker.configuration.shields_io_base_url
20+
end
21+
1822
def pact_verification_badge pact, label, initials, pseudo_branch_verification_status
1923
return static_svg(pact, pseudo_branch_verification_status) unless pact
2024

21-
title = badge_title pact, label, initials
22-
status = badge_status pseudo_branch_verification_status
23-
color = badge_color pseudo_branch_verification_status
25+
dynamic_svg(pact, label, initials, pseudo_branch_verification_status) || static_svg(pact, pseudo_branch_verification_status)
26+
end
2427

25-
dynamic_svg(title, status, color) || static_svg(pact, pseudo_branch_verification_status)
28+
def pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status)
29+
title = badge_title(pact, label, initials)
30+
status = badge_status(pseudo_branch_verification_status)
31+
color = badge_color(pseudo_branch_verification_status)
32+
build_shield_io_uri(title, status, color)
2633
end
2734

2835
def clear_cache
@@ -78,9 +85,9 @@ def badge_color pseudo_branch_verification_status
7885
end
7986
end
8087

81-
def dynamic_svg left_text, right_text, color
88+
def dynamic_svg pact, label, initials, pseudo_branch_verification_status
8289
return nil unless PactBroker.configuration.shields_io_base_url
83-
uri = build_uri(left_text, right_text, color)
90+
uri = pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status)
8491
begin
8592
response = do_request(uri)
8693
response.code == '200' ? response.body : nil
@@ -93,7 +100,7 @@ def dynamic_svg left_text, right_text, color
93100
end
94101
end
95102

96-
def build_uri left_text, right_text, color
103+
def build_shield_io_uri left_text, right_text, color
97104
shield_base_url = PactBroker.configuration.shields_io_base_url
98105
path = "/badge/#{escape_text(left_text)}-#{escape_text(right_text)}-#{color}.svg"
99106
URI.parse(shield_base_url + path)

lib/pact_broker/configuration.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class Configuration
3030
:webhook_scheme_whitelist,
3131
:webhook_host_whitelist,
3232
:base_equality_only_on_content_that_affects_verification_results,
33-
:seed_example_data
33+
:seed_example_data,
34+
:badge_provider_mode
3435
]
3536

3637
attr_accessor :base_url, :log_dir, :database_connection, :auto_migrate_db, :auto_migrate_db_data, :example_data_seeder, :seed_example_data, :use_hal_browser, :html_pact_renderer, :use_rack_protection
@@ -40,7 +41,7 @@ class Configuration
4041
attr_accessor :webhook_retry_schedule
4142
attr_reader :webhook_http_method_whitelist, :webhook_scheme_whitelist, :webhook_host_whitelist
4243
attr_accessor :semver_formats
43-
attr_accessor :enable_public_badge_access, :shields_io_base_url
44+
attr_accessor :enable_public_badge_access, :shields_io_base_url, :badge_provider_mode
4445
attr_accessor :disable_ssl_verification
4546
attr_accessor :base_equality_only_on_content_that_affects_verification_results
4647
attr_reader :api_error_reporters
@@ -69,6 +70,7 @@ def self.default_configuration
6970
config.enable_diagnostic_endpoints = true
7071
config.enable_public_badge_access = false # For security
7172
config.shields_io_base_url = "https://img.shields.io".freeze
73+
config.badge_provider_mode = :proxy # other option is :redirect
7274
config.use_case_sensitive_resource_names = true
7375
config.html_pact_renderer = default_html_pact_render
7476
config.version_parser = PactBroker::Versions::ParseSemanticVersion

spec/lib/pact_broker/api/resources/badge_spec.rb

+39-15
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ module Resources
1313
allow(PactBroker::Pacts::Service).to receive(:find_latest_pact).and_return(pact)
1414
allow(PactBroker::Verifications::Service).to receive(:find_latest_verification_for).and_return(verification)
1515
allow(PactBroker::Badges::Service).to receive(:pact_verification_badge).and_return("badge")
16+
allow(PactBroker::Badges::Service).to receive(:pact_verification_badge_url).and_return("http://badge")
1617
allow(PactBroker::Verifications::PseudoBranchStatus).to receive(:new).and_return(pseudo_branch_verification_status)
18+
allow(PactBroker::Badges::Service).to receive(:can_provide_badge_using_redirect?).and_return(false)
1719
end
1820

1921
let(:pact) { instance_double("PactBroker::Domain::Pact", consumer: consumer, provider: provider, consumer_version_number: "2", revision_number: "1") }
@@ -47,7 +49,6 @@ module Resources
4749
end
4850

4951
context "when enable_public_badge_access is true" do
50-
5152
before do
5253
PactBroker.configuration.enable_public_badge_access = true
5354
end
@@ -67,25 +68,48 @@ module Resources
6768
subject
6869
end
6970

70-
it "creates a badge" do
71-
expect(PactBroker::Badges::Service).to receive(:pact_verification_badge).with(pact, nil, false, :verified)
72-
subject
73-
end
71+
context "when can_provide_badge_using_redirect? is false" do
72+
before do
73+
allow(PactBroker::Badges::Service).to receive(:can_provide_badge_using_redirect?).and_return(false)
74+
end
7475

75-
it "returns a 200 status" do
76-
expect(subject.status).to eq 200
77-
end
76+
it "creates a badge" do
77+
expect(PactBroker::Badges::Service).to receive(:pact_verification_badge).with(pact, nil, false, :verified)
78+
subject
79+
end
7880

79-
it "does not allow caching" do
80-
expect(subject.headers['Cache-Control']).to eq 'no-cache'
81-
end
81+
it "returns a 200 status" do
82+
expect(subject.status).to eq 200
83+
end
8284

83-
it "returns the badge" do
84-
expect(subject.body).to end_with "badge"
85+
it "does not allow caching" do
86+
expect(subject.headers['Cache-Control']).to eq 'no-cache'
87+
end
88+
89+
it "returns the badge" do
90+
expect(subject.body).to end_with "badge"
91+
end
92+
93+
it "returns a comment with the consumer and provider numbers" do
94+
expect(subject.body).to include "<!-- consumer version 2 revision 1 provider version 3 number 7 -->"
95+
end
8596
end
8697

87-
it "returns a comment with the consumer and provider numbers" do
88-
expect(subject.body).to include "<!-- consumer version 2 revision 1 provider version 3 number 7 -->"
98+
context "when can_provide_badge_using_redirect? is true" do
99+
before do
100+
allow(PactBroker::Badges::Service).to receive(:can_provide_badge_using_redirect?).and_return(true)
101+
end
102+
103+
it "determines the URL of the badge to redirect to" do
104+
expect(PactBroker::Badges::Service).to receive(:pact_verification_badge_url).with(pact, nil, false, :verified)
105+
subject
106+
end
107+
108+
it "returns a 301 redirect to the badge URL" do
109+
expect(subject.status).to eq 307
110+
expect(subject.headers['Location']).to eq 'http://badge'
111+
expect(subject.headers['Cache-Control']).to eq 'no-cache'
112+
end
89113
end
90114

91115
context "when the label param is specified" do

spec/lib/pact_broker/badges/service_spec.rb

+20-5
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,16 @@ module Service
1414
let(:expected_url) { "https://img.shields.io/badge/#{expected_left_text}-#{expected_right_text}-#{expected_color}.svg" }
1515
let(:expected_color) { "brightgreen" }
1616
let(:expected_right_text) { "verified" }
17-
let(:expected_left_text) { "foo--bar%2Fthing__blah%20pact" }
17+
let(:expected_left_text) { "foo--bar%2fthing__blah%20pact" }
1818
let(:response_status) { 200 }
1919
let!(:http_request) do
2020
stub_request(:get, expected_url).to_return(:status => response_status, :body => "svg")
2121
end
2222

2323
subject { PactBroker::Badges::Service.pact_verification_badge pact, label, initials, pseudo_branch_verification_status }
2424

25+
let(:pact_verification_badge_url) { PactBroker::Badges::Service.pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status) }
26+
2527
before do
2628
Service.clear_cache
2729
allow(Service).to receive(:logger).and_return(logger)
@@ -41,48 +43,53 @@ module Service
4143
it "creates a badge with the consumer and provider names" do
4244
subject
4345
expect(http_request).to have_been_made
46+
expect(pact_verification_badge_url).to eq URI(expected_url)
4447
end
4548

4649
context "when initials is true" do
47-
let(:expected_left_text) { "fb%2Ftb%20pact" }
50+
let(:expected_left_text) { "fb%2ftb%20pact" }
4851
let(:initials) { true }
4952

5053
it "creates a badge with the consumer and provider initials" do
5154
subject
5255
expect(http_request).to have_been_made
56+
expect(pact_verification_badge_url).to eq URI(expected_url)
5357
end
5458
end
5559

5660
context "when initials is true but the consumer and provider names only contain one word" do
57-
let(:expected_left_text) { "foo%2Fbar%20pact" }
61+
let(:expected_left_text) { "foo%2fbar%20pact" }
5862
let(:initials) { true }
5963
let(:pact) { double("pact", consumer_name: "Foo", provider_name: "Bar") }
6064

6165
it "creates a badge with the consumer and provider names, not initials" do
6266
subject
6367
expect(http_request).to have_been_made
68+
expect(pact_verification_badge_url).to eq URI(expected_url)
6469
end
6570
end
6671

6772
context "when initials is true but the consumer and provider names are one camel cased word" do
68-
let(:expected_left_text) { "fa%2Fbp%20pact" }
73+
let(:expected_left_text) { "fa%2fbp%20pact" }
6974
let(:initials) { true }
7075
let(:pact) { double("pact", consumer_name: "FooApp", provider_name: "barProvider") }
7176

7277
it "creates a badge with the consumer and provider names, not initials" do
7378
subject
7479
expect(http_request).to have_been_made
80+
expect(pact_verification_badge_url).to eq URI(expected_url)
7581
end
7682
end
7783

7884
context "when initials is true but the consumer and provider names are one camel cased word" do
79-
let(:expected_left_text) { "fa%2Fdat%20pact" }
85+
let(:expected_left_text) { "fa%2fdat%20pact" }
8086
let(:initials) { true }
8187
let(:pact) { double("pact", consumer_name: "FooApp", provider_name: "doAThing") }
8288

8389
it "creates a badge with the consumer and provider names, not initials" do
8490
subject
8591
expect(http_request).to have_been_made
92+
expect(pact_verification_badge_url).to eq URI(expected_url)
8693
end
8794
end
8895
end
@@ -94,6 +101,7 @@ module Service
94101
it "creates a badge with only the consumer name" do
95102
subject
96103
expect(http_request).to have_been_made
104+
expect(pact_verification_badge_url).to eq URI(expected_url)
97105
end
98106

99107
context "when initials is true" do
@@ -103,6 +111,7 @@ module Service
103111
it "creates a badge with only the consumer initials" do
104112
subject
105113
expect(http_request).to have_been_made
114+
expect(pact_verification_badge_url).to eq URI(expected_url)
106115
end
107116
end
108117
end
@@ -114,6 +123,7 @@ module Service
114123
it "creates a badge with only the provider name" do
115124
subject
116125
expect(http_request).to have_been_made
126+
expect(pact_verification_badge_url).to eq URI(expected_url)
117127
end
118128

119129
context "when initials is true" do
@@ -123,6 +133,7 @@ module Service
123133
it "creates a badge with only the provider initials" do
124134
subject
125135
expect(http_request).to have_been_made
136+
expect(pact_verification_badge_url).to eq URI(expected_url)
126137
end
127138
end
128139
end
@@ -131,6 +142,7 @@ module Service
131142
it "create a green badge with left text 'verified'" do
132143
subject
133144
expect(http_request).to have_been_made
145+
expect(pact_verification_badge_url).to eq URI(expected_url)
134146
end
135147
end
136148

@@ -142,6 +154,7 @@ module Service
142154
it "create a lightgrey badge with left text 'unknown'" do
143155
subject
144156
expect(http_request).to have_been_made
157+
expect(pact_verification_badge_url).to eq URI(expected_url)
145158
end
146159
end
147160

@@ -153,6 +166,7 @@ module Service
153166
it "create a red badge with left text 'failed'" do
154167
subject
155168
expect(http_request).to have_been_made
169+
expect(pact_verification_badge_url).to eq URI(expected_url)
156170
end
157171
end
158172

@@ -164,6 +178,7 @@ module Service
164178
it "create a orange badge with left text 'changed'" do
165179
subject
166180
expect(http_request).to have_been_made
181+
expect(pact_verification_badge_url).to eq URI(expected_url)
167182
end
168183
end
169184

0 commit comments

Comments
 (0)