flipper-ui 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/flipper-ui.gemspec +1 -1
- data/lib/flipper/ui/action.rb +2 -2
- data/lib/flipper/ui/actions/actors_gate.rb +2 -2
- data/lib/flipper/ui/actions/boolean_gate.rb +1 -1
- data/lib/flipper/ui/actions/features.rb +2 -2
- data/lib/flipper/ui/actions/groups_gate.rb +2 -2
- data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +2 -2
- data/lib/flipper/ui/actions/percentage_of_time_gate.rb +2 -2
- data/lib/flipper/ui/configuration.rb +2 -2
- data/lib/flipper/ui/util.rb +10 -0
- data/lib/flipper/ui/views/add_actor.erb +2 -2
- data/lib/flipper/ui/views/add_group.erb +2 -2
- data/lib/flipper/ui/views/feature.erb +11 -11
- data/lib/flipper/ui/views/features.erb +1 -1
- data/lib/flipper/ui/views/layout.erb +1 -1
- data/lib/flipper/ui.rb +7 -2
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/actions/actors_gate_spec.rb +5 -5
- data/spec/flipper/ui/actions/boolean_gate_spec.rb +1 -1
- data/spec/flipper/ui/actions/feature_spec.rb +14 -0
- data/spec/flipper/ui/actions/features_spec.rb +26 -3
- data/spec/flipper/ui/actions/groups_gate_spec.rb +5 -5
- data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +3 -3
- data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +3 -3
- metadata +9 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be9d4667f30e16f2d6f4ccb72bbb3e3f46b7c0261274bea9ec2e31fa0c403210
|
4
|
+
data.tar.gz: '09bb8b603b54d6c4161f312b4ba0be1ac92299d0b55c29f21b5fdd5961034a0d'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a15b882c1e7a5248da12a71f7dd4c9df7f9d63a72cccad36efc7d64cd4b88280eaff9cf0999d3737eab2d7bd4745ee926fcc1cf59cd1f060944636264ff37202
|
7
|
+
data.tar.gz: baad050fea403866a36c3cba1520d0e1a9e8224a80f5d97c478bc098c583a66ce29cb35f99f6b21d2caebf48d84fa0e62629a36fafc2724d51dc5d5d7aa784b2
|
data/flipper-ui.gemspec
CHANGED
@@ -25,5 +25,5 @@ Gem::Specification.new do |gem|
|
|
25
25
|
gem.add_dependency 'rack-session', '>= 1.0.2', '< 3.0.0'
|
26
26
|
gem.add_dependency 'flipper', "~> #{Flipper::VERSION}"
|
27
27
|
gem.add_dependency 'erubi', '>= 1.0.0', '< 2.0.0'
|
28
|
-
gem.add_dependency 'sanitize', '<
|
28
|
+
gem.add_dependency 'sanitize', '< 8'
|
29
29
|
end
|
data/lib/flipper/ui/action.rb
CHANGED
@@ -12,7 +12,7 @@ module Flipper
|
|
12
12
|
def feature_name
|
13
13
|
@feature_name ||= begin
|
14
14
|
match = request.path_info.match(self.class.route_regex)
|
15
|
-
match ?
|
15
|
+
match ? Flipper::UI::Util.unescape(match[:feature_name]) : nil
|
16
16
|
end
|
17
17
|
end
|
18
18
|
private :feature_name
|
@@ -164,7 +164,7 @@ module Flipper
|
|
164
164
|
# location - The String location to set the Location header to.
|
165
165
|
def redirect_to(location)
|
166
166
|
status 302
|
167
|
-
header 'location', "#{script_name}#{
|
167
|
+
header 'location', "#{script_name}#{location}"
|
168
168
|
halt [@code, @headers, ['']]
|
169
169
|
end
|
170
170
|
|
@@ -25,7 +25,7 @@ module Flipper
|
|
25
25
|
|
26
26
|
if values.empty?
|
27
27
|
error = "#{value.inspect} is not a valid actor value."
|
28
|
-
redirect_to("/features/#{feature.key}/actors?error=#{error}")
|
28
|
+
redirect_to("/features/#{Flipper::UI::Util.escape feature.key}/actors?error=#{Flipper::UI::Util.escape error}")
|
29
29
|
end
|
30
30
|
|
31
31
|
values.each do |value|
|
@@ -39,7 +39,7 @@ module Flipper
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
redirect_to("/features/#{feature.key}")
|
42
|
+
redirect_to("/features/#{Flipper::UI::Util.escape feature.key}")
|
43
43
|
end
|
44
44
|
end
|
45
45
|
end
|
@@ -45,13 +45,13 @@ module Flipper
|
|
45
45
|
|
46
46
|
if Util.blank?(value)
|
47
47
|
error = "#{value.inspect} is not a valid feature name."
|
48
|
-
redirect_to("/features/new?error=#{error}")
|
48
|
+
redirect_to("/features/new?error=#{Flipper::UI::Util.escape error}")
|
49
49
|
end
|
50
50
|
|
51
51
|
feature = flipper[value]
|
52
52
|
feature.add
|
53
53
|
|
54
|
-
redirect_to "/features/#{value}"
|
54
|
+
redirect_to "/features/#{Flipper::UI::Util.escape value}"
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
@@ -30,10 +30,10 @@ module Flipper
|
|
30
30
|
feature.disable_group value
|
31
31
|
end
|
32
32
|
|
33
|
-
redirect_to("/features/#{feature.key}")
|
33
|
+
redirect_to("/features/#{Flipper::UI::Util.escape feature.key}")
|
34
34
|
else
|
35
35
|
error = "The group named #{value.inspect} has not been registered."
|
36
|
-
redirect_to("/features/#{feature.key}/groups?error=#{error}")
|
36
|
+
redirect_to("/features/#{Flipper::UI::Util.escape feature.key}/groups?error=#{Flipper::UI::Util.escape error}")
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -19,10 +19,10 @@ module Flipper
|
|
19
19
|
feature.enable_percentage_of_actors params['value']
|
20
20
|
rescue ArgumentError => exception
|
21
21
|
error = "Invalid percentage of actors value: #{exception.message}"
|
22
|
-
redirect_to("/features/#{@feature.key}?error=#{error}")
|
22
|
+
redirect_to("/features/#{Flipper::UI::Util.escape @feature.key}?error=#{Flipper::UI::Util.escape error}")
|
23
23
|
end
|
24
24
|
|
25
|
-
redirect_to "/features/#{@feature.key}"
|
25
|
+
redirect_to "/features/#{Flipper::UI::Util.escape @feature.key}"
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -19,10 +19,10 @@ module Flipper
|
|
19
19
|
feature.enable_percentage_of_time params['value']
|
20
20
|
rescue ArgumentError => exception
|
21
21
|
error = "Invalid percentage of time value: #{exception.message}"
|
22
|
-
redirect_to("/features/#{@feature.key}?error=#{error}")
|
22
|
+
redirect_to("/features/#{Flipper::UI::Util.escape @feature.key}?error=#{Flipper::UI::Util.escape error}")
|
23
23
|
end
|
24
24
|
|
25
|
-
redirect_to "/features/#{@feature.key}"
|
25
|
+
redirect_to "/features/#{Flipper::UI::Util.escape @feature.key}"
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -5,8 +5,8 @@ module Flipper
|
|
5
5
|
class Configuration
|
6
6
|
attr_reader :delete
|
7
7
|
|
8
|
-
attr_accessor :banner_text
|
9
|
-
|
8
|
+
attr_accessor :banner_text
|
9
|
+
attr_reader :banner_class
|
10
10
|
|
11
11
|
# Public: Is the UI in read only mode or not. Default is false. This
|
12
12
|
# supersedes all other write-related options such as
|
data/lib/flipper/ui/util.rb
CHANGED
@@ -1,9 +1,19 @@
|
|
1
|
+
require "rack/utils"
|
2
|
+
|
1
3
|
module Flipper
|
2
4
|
module UI
|
3
5
|
module Util
|
4
6
|
# Private: 0x3000: fullwidth whitespace
|
5
7
|
NON_WHITESPACE_REGEXP = /[^\s#{[0x3000].pack("U")}]/
|
6
8
|
|
9
|
+
def self.escape(str)
|
10
|
+
Rack::Utils.escape(str)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.unescape(str)
|
14
|
+
Rack::Utils.unescape(str)
|
15
|
+
end
|
16
|
+
|
7
17
|
def self.blank?(str)
|
8
18
|
str.to_s !~ NON_WHITESPACE_REGEXP
|
9
19
|
end
|
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
<div class="card">
|
8
8
|
<h4 class="card-header">
|
9
|
-
<a class="link-dark" href="<%= script_name %>/features/<%= @feature.key %>"><%= @feature.key %></a>
|
9
|
+
<a class="link-dark" href="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>"><%= @feature.key %></a>
|
10
10
|
/
|
11
11
|
Enable Actor
|
12
12
|
</h4>
|
@@ -14,7 +14,7 @@
|
|
14
14
|
<p>
|
15
15
|
Turn on this feature for actors.
|
16
16
|
</p>
|
17
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="row">
|
17
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/actors" method="post" class="row">
|
18
18
|
<%== csrf_input_tag %>
|
19
19
|
<input type="hidden" name="operation" value="enable">
|
20
20
|
<div class="col"><input type="text" name="value" placeholder="<%= Flipper::UI.configuration.add_actor_placeholder %>" class="form-control"></div>
|
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
<div class="card">
|
8
8
|
<h4 class="card-header">
|
9
|
-
<a class="link-dark" href="<%= script_name %>/features/<%= @feature.key %>"><%= @feature.key %></a>
|
9
|
+
<a class="link-dark" href="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>"><%= @feature.key %></a>
|
10
10
|
/
|
11
11
|
Enable Group</h4>
|
12
12
|
<div class="card-body">
|
@@ -16,7 +16,7 @@
|
|
16
16
|
<p>
|
17
17
|
Turn on this feature for an entire group of actors.
|
18
18
|
</p>
|
19
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/groups" method="post" class="row">
|
19
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/groups" method="post" class="row">
|
20
20
|
<%== csrf_input_tag %>
|
21
21
|
<input type="hidden" name="operation" value="enable">
|
22
22
|
<div class="col">
|
@@ -50,7 +50,7 @@
|
|
50
50
|
<button class="btn btn-outline-secondary js-toggle-trigger" data-bs-toggle="collapse" data-bs-target="#add-actor">Add an actor</button>
|
51
51
|
</div>
|
52
52
|
<div class="col toggle-block-when-on">
|
53
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post" class="row">
|
53
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/actors" method="post" class="row">
|
54
54
|
<%== csrf_input_tag %>
|
55
55
|
<input type="hidden" name="operation" value="enable">
|
56
56
|
<div class="col">
|
@@ -83,7 +83,7 @@
|
|
83
83
|
</div>
|
84
84
|
<div class="col col-auto">
|
85
85
|
<% if write_allowed? %>
|
86
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/actors" method="post">
|
86
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/actors" method="post">
|
87
87
|
<%== csrf_input_tag %>
|
88
88
|
<input type="hidden" name="operation" value="disable">
|
89
89
|
<input type="hidden" name="value" value="<%= item %>">
|
@@ -119,13 +119,13 @@
|
|
119
119
|
<% if @feature.disabled_groups.empty? %>
|
120
120
|
All groups enabled.
|
121
121
|
<% else %>
|
122
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/groups" method="post" class="row">
|
122
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/groups" method="post" class="row">
|
123
123
|
<%== csrf_input_tag %>
|
124
124
|
<input type="hidden" name="operation" value="enable">
|
125
125
|
<div class="col">
|
126
126
|
<select name="value" class="form-select">
|
127
127
|
<option value="">Select a group...</option>
|
128
|
-
<% @feature.disabled_groups.each do |group| %>
|
128
|
+
<% @feature.disabled_groups.sort_by(&:name).each do |group| %>
|
129
129
|
<option value="<%= group.name %>"><%= group.name %></option>
|
130
130
|
<% end %>
|
131
131
|
</select>
|
@@ -150,7 +150,7 @@
|
|
150
150
|
</div>
|
151
151
|
<div class="col col-auto">
|
152
152
|
<% if write_allowed? %>
|
153
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/groups" method="post">
|
153
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/groups" method="post">
|
154
154
|
<%== csrf_input_tag %>
|
155
155
|
<input type="hidden" name="operation" value="disable">
|
156
156
|
<input type="hidden" name="value" value="<%= item %>">
|
@@ -185,7 +185,7 @@
|
|
185
185
|
<% if write_allowed? %>
|
186
186
|
<div class="card-body border-bottom toggle-block-when-on bg-lightest">
|
187
187
|
<div class="row align-items-center">
|
188
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post" class="col">
|
188
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/percentage_of_actors" method="post" class="col">
|
189
189
|
<%== csrf_input_tag %>
|
190
190
|
<div class="btn-group">
|
191
191
|
<% @percentages.each do |number| %>
|
@@ -193,7 +193,7 @@
|
|
193
193
|
<% end %>
|
194
194
|
</div>
|
195
195
|
</form>
|
196
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_actors" method="post" class="col-auto row">
|
196
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/percentage_of_actors" method="post" class="col-auto row">
|
197
197
|
<%== csrf_input_tag %>
|
198
198
|
<div class="col-auto">
|
199
199
|
<input style="width:5em;" type="number" max="100" min="0" name="value" <% if @feature.percentage_of_actors_value > 0 %>value="<%= @feature.percentage_of_actors_value %>"<% end %> title="Custom percentage (26, 32, etc.)" placeholder="0" class="form-control">
|
@@ -226,7 +226,7 @@
|
|
226
226
|
<% if write_allowed? %>
|
227
227
|
<div class="card-body border-bottom toggle-block-when-on bg-lightest">
|
228
228
|
<div class="row align-items-center">
|
229
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post" class="col">
|
229
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/percentage_of_time" method="post" class="col">
|
230
230
|
<%== csrf_input_tag %>
|
231
231
|
<div class="btn-group">
|
232
232
|
<% @percentages.each do |number| %>
|
@@ -234,7 +234,7 @@
|
|
234
234
|
<% end %>
|
235
235
|
</div>
|
236
236
|
</form>
|
237
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/percentage_of_time" method="post" class="col-auto row">
|
237
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/percentage_of_time" method="post" class="col-auto row">
|
238
238
|
<%== csrf_input_tag %>
|
239
239
|
<div class="col-auto">
|
240
240
|
<input style="width:5em;" type="number" max="100" min="0" name="value" <% if @feature.percentage_of_time_value > 0 %>value="<%= @feature.percentage_of_time_value %>"<% end %> title="Custom percentage (26, 32, etc.)" placeholder="0" class="form-control">
|
@@ -249,7 +249,7 @@
|
|
249
249
|
|
250
250
|
<% if write_allowed? %>
|
251
251
|
<div class="card-body">
|
252
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>/boolean" method="post">
|
252
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>/boolean" method="post">
|
253
253
|
<%== csrf_input_tag %>
|
254
254
|
|
255
255
|
<div class="row">
|
@@ -301,7 +301,7 @@
|
|
301
301
|
<p>
|
302
302
|
<%= Flipper::UI.configuration.delete.description %>
|
303
303
|
</p>
|
304
|
-
<form action="<%= script_name %>/features/<%= @feature.key %>" method="post">
|
304
|
+
<form action="<%= script_name %>/features/<%= Flipper::UI::Util.escape @feature.key %>" method="post">
|
305
305
|
<%== csrf_input_tag %>
|
306
306
|
<input type="hidden" id="feature_name" name="_feature" value="<%= feature_name %>">
|
307
307
|
<input type="hidden" name="_method" value="DELETE">
|
@@ -36,7 +36,7 @@
|
|
36
36
|
</div>
|
37
37
|
</div>
|
38
38
|
<% @features.each do |feature| %>
|
39
|
-
<a href="<%= "#{script_name}/features/#{feature.key}" %>" class="list-group-item list-group-item-action">
|
39
|
+
<a href="<%= "#{script_name}/features/#{Flipper::UI::Util.escape(feature.key)}" %>" class="list-group-item list-group-item-action">
|
40
40
|
<div class="row align-items-center">
|
41
41
|
<div class="col-1 text-center">
|
42
42
|
<span class="status <%= feature.color_class %>" data-bs-toggle="tooltip" title=<%= feature.state.to_s.capitalize %>></span>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
<title><%= @page_title ? "#{@page_title} // " : "" %>Flipper</title>
|
4
|
+
<title><%= defined?(@page_title) ? "#{@page_title} // " : "" %>Flipper</title>
|
5
5
|
<meta charset="utf-8">
|
6
6
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
data/lib/flipper/ui.rb
CHANGED
@@ -20,12 +20,17 @@ module Flipper
|
|
20
20
|
|
21
21
|
def self.app(flipper = nil, options = {})
|
22
22
|
env_key = options.fetch(:env_key, 'flipper')
|
23
|
-
|
23
|
+
|
24
|
+
if options.key?(:rack_protection)
|
25
|
+
warn "[DEPRECATION] `rack_protection` option is deprecated. " +
|
26
|
+
"Flipper::UI now only includes Rack::Protection::AuthenticityToken middleware. " +
|
27
|
+
"If you need additional protection, you can add it yourself."
|
28
|
+
end
|
24
29
|
|
25
30
|
app = ->(_) { [200, { Rack::CONTENT_TYPE => 'text/html' }, ['']] }
|
26
31
|
builder = Rack::Builder.new
|
27
32
|
yield builder if block_given?
|
28
|
-
builder.use Rack::Protection
|
33
|
+
builder.use Rack::Protection::AuthenticityToken
|
29
34
|
builder.use Rack::MethodOverride
|
30
35
|
builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key
|
31
36
|
builder.use Flipper::UI::Middleware, flipper: flipper, env_key: env_key
|
data/lib/flipper/version.rb
CHANGED
@@ -35,7 +35,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it 'renders add new actor form' do
|
38
|
-
form = '<form action="/features/a/
|
38
|
+
form = '<form action="/features/a%2Fb/actors" method="post" class="row">'
|
39
39
|
expect(last_response.body).to include(form)
|
40
40
|
end
|
41
41
|
end
|
@@ -73,7 +73,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
73
73
|
|
74
74
|
context "when feature name contains space" do
|
75
75
|
before do
|
76
|
-
post 'features/sp
|
76
|
+
post 'features/sp+ace/actors',
|
77
77
|
{ 'value' => value, 'operation' => 'enable', 'authenticity_token' => token },
|
78
78
|
'rack.session' => session
|
79
79
|
end
|
@@ -84,7 +84,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
84
84
|
|
85
85
|
it "redirects back to feature" do
|
86
86
|
expect(last_response.status).to be(302)
|
87
|
-
expect(last_response.headers['location']).to eq('/features/sp
|
87
|
+
expect(last_response.headers['location']).to eq('/features/sp+ace')
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
@@ -114,7 +114,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
114
114
|
|
115
115
|
it 'redirects back to feature' do
|
116
116
|
expect(last_response.status).to be(302)
|
117
|
-
expect(last_response.headers['location']).to eq('/features/search/actors?error=%22%22
|
117
|
+
expect(last_response.headers['location']).to eq('/features/search/actors?error=%22%22+is+not+a+valid+actor+value.')
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
@@ -123,7 +123,7 @@ RSpec.describe Flipper::UI::Actions::ActorsGate do
|
|
123
123
|
|
124
124
|
it 'redirects back to feature' do
|
125
125
|
expect(last_response.status).to be(302)
|
126
|
-
expect(last_response.headers['location']).to eq('/features/search/actors?error=%22%22
|
126
|
+
expect(last_response.headers['location']).to eq('/features/search/actors?error=%22%22+is+not+a+valid+actor+value.')
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -43,7 +43,7 @@ RSpec.describe Flipper::UI::Actions::BooleanGate do
|
|
43
43
|
|
44
44
|
it 'redirects back to feature' do
|
45
45
|
expect(last_response.status).to be(302)
|
46
|
-
expect(last_response.headers['location']).to eq('/features/sp
|
46
|
+
expect(last_response.headers['location']).to eq('/features/sp+ace')
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -197,4 +197,18 @@ RSpec.describe Flipper::UI::Actions::Feature do
|
|
197
197
|
expect(last_response.body).to include('a/b')
|
198
198
|
end
|
199
199
|
end
|
200
|
+
|
201
|
+
describe 'GET /features/:feature with dot dot slash repeated in feature name' do
|
202
|
+
before do
|
203
|
+
get '/features/..%2F..%2F..%2F..%2Fblah'
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'responds with success' do
|
207
|
+
expect(last_response.status).to be(200)
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'renders template' do
|
211
|
+
expect(last_response.body).to include('../../../../blah')
|
212
|
+
end
|
213
|
+
end
|
200
214
|
end
|
@@ -28,6 +28,16 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
it "escapes keys that are junky" do
|
32
|
+
flipper.add("../../../../blah")
|
33
|
+
flipper.add("this that")
|
34
|
+
flipper.add("foo/bar")
|
35
|
+
get '/features'
|
36
|
+
expect(last_response.body).to include("..%2F..%2F..%2F..%2Fblah")
|
37
|
+
expect(last_response.body).to include("this+that")
|
38
|
+
expect(last_response.body).to include("foo%2Fbar")
|
39
|
+
end
|
40
|
+
|
31
41
|
context "when there are no features to list" do
|
32
42
|
before do
|
33
43
|
@original_fun_enabled = Flipper::UI.configuration.fun
|
@@ -124,7 +134,20 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
124
134
|
|
125
135
|
it 'redirects to feature' do
|
126
136
|
expect(last_response.status).to be(302)
|
127
|
-
expect(last_response.headers['location']).to eq('/features/notifications
|
137
|
+
expect(last_response.headers['location']).to eq('/features/notifications+next')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'feature name contains ../' do
|
142
|
+
let(:feature_name) { '../../../foo' }
|
143
|
+
|
144
|
+
it 'adds feature with space' do
|
145
|
+
expect(flipper.features.map(&:key)).to include(feature_name)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'redirects to feature' do
|
149
|
+
expect(last_response.status).to be(302)
|
150
|
+
expect(last_response.headers['location']).to eq('/features/..%2F..%2F..%2Ffoo')
|
128
151
|
end
|
129
152
|
end
|
130
153
|
|
@@ -138,7 +161,7 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
138
161
|
|
139
162
|
it 'redirects back to feature' do
|
140
163
|
expect(last_response.status).to be(302)
|
141
|
-
expect(last_response.headers['location']).to eq('/features/new?error=%22%22
|
164
|
+
expect(last_response.headers['location']).to eq('/features/new?error=%22%22+is+not+a+valid+feature+name.')
|
142
165
|
end
|
143
166
|
end
|
144
167
|
|
@@ -151,7 +174,7 @@ RSpec.describe Flipper::UI::Actions::Features do
|
|
151
174
|
|
152
175
|
it 'redirects back to feature' do
|
153
176
|
expect(last_response.status).to be(302)
|
154
|
-
expect(last_response.headers['location']).to eq('/features/new?error=%22%22
|
177
|
+
expect(last_response.headers['location']).to eq('/features/new?error=%22%22+is+not+a+valid+feature+name.')
|
155
178
|
end
|
156
179
|
end
|
157
180
|
end
|
@@ -60,7 +60,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
60
60
|
|
61
61
|
context 'feature name contains space' do
|
62
62
|
before do
|
63
|
-
post 'features/sp
|
63
|
+
post 'features/sp+ace/groups',
|
64
64
|
{ 'value' => group_name, 'operation' => 'enable', 'authenticity_token' => token },
|
65
65
|
'rack.session' => session
|
66
66
|
end
|
@@ -71,7 +71,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
71
71
|
|
72
72
|
it 'redirects back to feature' do
|
73
73
|
expect(last_response.status).to be(302)
|
74
|
-
expect(last_response.headers['location']).to eq('/features/sp
|
74
|
+
expect(last_response.headers['location']).to eq('/features/sp+ace')
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -89,7 +89,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
89
89
|
|
90
90
|
it 'redirects back to feature' do
|
91
91
|
expect(last_response.status).to be(302)
|
92
|
-
expect(last_response.headers['location']).to eq('/features/search/groups?error=The
|
92
|
+
expect(last_response.headers['location']).to eq('/features/search/groups?error=The+group+named+%22not_here%22+has+not+been+registered.')
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -98,7 +98,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
98
98
|
|
99
99
|
it 'redirects back to feature' do
|
100
100
|
expect(last_response.status).to be(302)
|
101
|
-
expect(last_response.headers['location']).to eq('/features/search/groups?error=The
|
101
|
+
expect(last_response.headers['location']).to eq('/features/search/groups?error=The+group+named+%22%22+has+not+been+registered.')
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
@@ -107,7 +107,7 @@ RSpec.describe Flipper::UI::Actions::GroupsGate do
|
|
107
107
|
|
108
108
|
it 'redirects back to feature' do
|
109
109
|
expect(last_response.status).to be(302)
|
110
|
-
expect(last_response.headers['location']).to eq('/features/search/groups?error=The
|
110
|
+
expect(last_response.headers['location']).to eq('/features/search/groups?error=The+group+named+%22%22+has+not+been+registered.')
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -30,7 +30,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfActorsGate do
|
|
30
30
|
|
31
31
|
context 'with space in feature name' do
|
32
32
|
before do
|
33
|
-
post 'features/sp
|
33
|
+
post 'features/sp+ace/percentage_of_actors',
|
34
34
|
{ 'value' => '24', 'authenticity_token' => token },
|
35
35
|
'rack.session' => session
|
36
36
|
end
|
@@ -41,7 +41,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfActorsGate do
|
|
41
41
|
|
42
42
|
it 'redirects back to feature' do
|
43
43
|
expect(last_response.status).to be(302)
|
44
|
-
expect(last_response.headers['location']).to eq('/features/sp
|
44
|
+
expect(last_response.headers['location']).to eq('/features/sp+ace')
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -58,7 +58,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfActorsGate do
|
|
58
58
|
|
59
59
|
it 'redirects back to feature' do
|
60
60
|
expect(last_response.status).to be(302)
|
61
|
-
expect(last_response.headers['location']).to eq('/features/search?error=Invalid%
|
61
|
+
expect(last_response.headers['location']).to eq('/features/search?error=Invalid+percentage+of+actors+value%3A+value+must+be+a+positive+number+less+than+or+equal+to+100%2C+but+was+555')
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -30,7 +30,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfTimeGate do
|
|
30
30
|
|
31
31
|
context 'with space in feature name' do
|
32
32
|
before do
|
33
|
-
post 'features/sp
|
33
|
+
post 'features/sp+ace/percentage_of_time',
|
34
34
|
{ 'value' => '24', 'authenticity_token' => token },
|
35
35
|
'rack.session' => session
|
36
36
|
end
|
@@ -41,7 +41,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfTimeGate do
|
|
41
41
|
|
42
42
|
it 'redirects back to feature' do
|
43
43
|
expect(last_response.status).to be(302)
|
44
|
-
expect(last_response.headers['location']).to eq('/features/sp
|
44
|
+
expect(last_response.headers['location']).to eq('/features/sp+ace')
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -58,7 +58,7 @@ RSpec.describe Flipper::UI::Actions::PercentageOfTimeGate do
|
|
58
58
|
|
59
59
|
it 'redirects back to feature' do
|
60
60
|
expect(last_response.status).to be(302)
|
61
|
-
expect(last_response.headers['location']).to eq('/features/search?error=Invalid%
|
61
|
+
expect(last_response.headers['location']).to eq('/features/search?error=Invalid+percentage+of+time+value%3A+value+must+be+a+positive+number+less+than+or+equal+to+100%2C+but+was+555')
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper-ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-02-24 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: rack
|
@@ -76,14 +75,14 @@ dependencies:
|
|
76
75
|
requirements:
|
77
76
|
- - "~>"
|
78
77
|
- !ruby/object:Gem::Version
|
79
|
-
version: 1.3.
|
78
|
+
version: 1.3.3
|
80
79
|
type: :runtime
|
81
80
|
prerelease: false
|
82
81
|
version_requirements: !ruby/object:Gem::Requirement
|
83
82
|
requirements:
|
84
83
|
- - "~>"
|
85
84
|
- !ruby/object:Gem::Version
|
86
|
-
version: 1.3.
|
85
|
+
version: 1.3.3
|
87
86
|
- !ruby/object:Gem::Dependency
|
88
87
|
name: erubi
|
89
88
|
requirement: !ruby/object:Gem::Requirement
|
@@ -110,15 +109,14 @@ dependencies:
|
|
110
109
|
requirements:
|
111
110
|
- - "<"
|
112
111
|
- !ruby/object:Gem::Version
|
113
|
-
version: '
|
112
|
+
version: '8'
|
114
113
|
type: :runtime
|
115
114
|
prerelease: false
|
116
115
|
version_requirements: !ruby/object:Gem::Requirement
|
117
116
|
requirements:
|
118
117
|
- - "<"
|
119
118
|
- !ruby/object:Gem::Version
|
120
|
-
version: '
|
121
|
-
description:
|
119
|
+
version: '8'
|
122
120
|
email: support@flippercloud.io
|
123
121
|
executables: []
|
124
122
|
extensions: []
|
@@ -200,8 +198,8 @@ metadata:
|
|
200
198
|
homepage_uri: https://www.flippercloud.io
|
201
199
|
source_code_uri: https://github.com/flippercloud/flipper
|
202
200
|
bug_tracker_uri: https://github.com/flippercloud/flipper/issues
|
203
|
-
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.
|
204
|
-
|
201
|
+
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.3
|
202
|
+
funding_uri: https://github.com/sponsors/flippercloud
|
205
203
|
rdoc_options: []
|
206
204
|
require_paths:
|
207
205
|
- lib
|
@@ -216,8 +214,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
216
214
|
- !ruby/object:Gem::Version
|
217
215
|
version: '0'
|
218
216
|
requirements: []
|
219
|
-
rubygems_version: 3.5
|
220
|
-
signing_key:
|
217
|
+
rubygems_version: 3.6.5
|
221
218
|
specification_version: 4
|
222
219
|
summary: Feature flag UI for the Flipper gem
|
223
220
|
test_files:
|