flipper-ui 1.4.0 → 1.4.1
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/lib/flipper/ui/actions/boolean_gate.rb +4 -0
- data/lib/flipper/ui/configuration.rb +24 -0
- data/lib/flipper/ui/views/disable_fully_enable.erb +3 -0
- data/lib/flipper/ui/views/feature.erb +17 -9
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/ui/actions/boolean_gate_spec.rb +71 -0
- data/spec/flipper/ui/actions/feature_spec.rb +26 -0
- data/spec/flipper/ui/configuration_spec.rb +33 -0
- metadata +6 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '019dbf0664d70ba33fd6425f7ba20bff41a1be13ff42c97b184ea37c2c500fd5'
|
|
4
|
+
data.tar.gz: d1f2131f1d38cc5aa1ac4b0b5cc1298c914e8d4fd6e6f8693f218f6802977e8b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9cee5defa8de58857b30dabf3db81047e61b1138bfaa090817d2b9b0732bc662bf7b1eaa4e5973cb59ae1ded71ccda44c178901450ccd20c7b2ebec5b79f292b
|
|
7
|
+
data.tar.gz: 011ba0f8d3db3ef0efaab5e684d79beb9fee04c28dc660ec394b1cdfab37ac1c0af3c83ab3f1731a623cdd92a159d87c8f157f269b0d1eac269603603eba18be
|
|
@@ -16,6 +16,10 @@ module Flipper
|
|
|
16
16
|
@feature = Decorators::Feature.new(feature)
|
|
17
17
|
|
|
18
18
|
if params['action'] == 'Enable'
|
|
19
|
+
if Flipper::UI.configuration.disable_fully_enable
|
|
20
|
+
status 403
|
|
21
|
+
halt view_response(:disable_fully_enable)
|
|
22
|
+
end
|
|
19
23
|
feature.enable
|
|
20
24
|
else
|
|
21
25
|
feature.disable
|
|
@@ -77,6 +77,20 @@ module Flipper
|
|
|
77
77
|
# Default is false.
|
|
78
78
|
attr_accessor :confirm_disable
|
|
79
79
|
|
|
80
|
+
# Public: Set to disable the Fully Enable button in the UI, preventing
|
|
81
|
+
# users from fully enabling features via the web interface. Set to true
|
|
82
|
+
# for a default message, or a string for a custom message. Defaults to nil.
|
|
83
|
+
#
|
|
84
|
+
# Note: This only affects the UI. If flipper-api is mounted, full enable
|
|
85
|
+
# is still possible via the API.
|
|
86
|
+
#
|
|
87
|
+
# Examples:
|
|
88
|
+
#
|
|
89
|
+
# config.disable_fully_enable = true
|
|
90
|
+
# config.disable_fully_enable = "Use deploy pipeline instead."
|
|
91
|
+
#
|
|
92
|
+
attr_accessor :disable_fully_enable
|
|
93
|
+
|
|
80
94
|
VALID_BANNER_CLASS_VALUES = %w(
|
|
81
95
|
danger
|
|
82
96
|
dark
|
|
@@ -90,6 +104,7 @@ module Flipper
|
|
|
90
104
|
|
|
91
105
|
DEFAULT_DESCRIPTIONS_SOURCE = ->(_keys) { {} }
|
|
92
106
|
DEFAULT_ACTOR_NAMES_SOURCE = ->(_keys) { {} }
|
|
107
|
+
DEFAULT_DISABLE_FULLY_ENABLE_MESSAGE = "Fully enabling features via the UI is disabled."
|
|
93
108
|
|
|
94
109
|
def initialize
|
|
95
110
|
@delete = Option.new("Danger Zone", "Deleting a feature removes it from the list of features and disables it for everyone.")
|
|
@@ -106,6 +121,7 @@ module Flipper
|
|
|
106
121
|
@actors_separator = ','
|
|
107
122
|
@confirm_fully_enable = false
|
|
108
123
|
@confirm_disable = true
|
|
124
|
+
@disable_fully_enable = nil
|
|
109
125
|
@read_only = false
|
|
110
126
|
@nav_items = [
|
|
111
127
|
{ title: "Features", href: "features" },
|
|
@@ -121,6 +137,14 @@ module Flipper
|
|
|
121
137
|
using_descriptions? && @show_feature_description_in_list
|
|
122
138
|
end
|
|
123
139
|
|
|
140
|
+
def disable_fully_enable_message
|
|
141
|
+
if @disable_fully_enable.is_a?(String)
|
|
142
|
+
@disable_fully_enable
|
|
143
|
+
else
|
|
144
|
+
DEFAULT_DISABLE_FULLY_ENABLE_MESSAGE
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
124
148
|
def banner_class=(value)
|
|
125
149
|
unless VALID_BANNER_CLASS_VALUES.include?(value)
|
|
126
150
|
raise InvalidConfigurationValue, "The banner_class provided '#{value}' is " \
|
|
@@ -255,16 +255,24 @@
|
|
|
255
255
|
<div class="row">
|
|
256
256
|
<% unless @feature.boolean_value %>
|
|
257
257
|
<div class="col d-grid">
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
>
|
|
264
|
-
<span class="d-block" data-bs-toggle="tooltip" title="Enable for everyone">
|
|
265
|
-
Fully Enable
|
|
258
|
+
<% if Flipper::UI.configuration.disable_fully_enable %>
|
|
259
|
+
<span class="d-inline-block" tabindex="0" data-bs-toggle="tooltip" title="<%= Flipper::UI.configuration.disable_fully_enable_message %>">
|
|
260
|
+
<button type="submit" name="action" value="Enable" class="btn btn-outline-success w-100" disabled style="pointer-events: none;">
|
|
261
|
+
Fully Enable
|
|
262
|
+
</button>
|
|
266
263
|
</span>
|
|
267
|
-
|
|
264
|
+
<% else %>
|
|
265
|
+
<button type="submit" name="action" value="Enable" class="btn btn-outline-success"
|
|
266
|
+
<% if Flipper::UI.configuration.confirm_fully_enable %>
|
|
267
|
+
data-confirmation-prompt="Are you sure you want to fully enable this feature for everyone? Please enter the name of the feature to confirm it: <%= feature_name %>"
|
|
268
|
+
data-confirmation-text="<%= feature_name %>"
|
|
269
|
+
<% end %>
|
|
270
|
+
>
|
|
271
|
+
<span class="d-block" data-bs-toggle="tooltip" title="Enable for everyone">
|
|
272
|
+
Fully Enable
|
|
273
|
+
</span>
|
|
274
|
+
</button>
|
|
275
|
+
<% end %>
|
|
268
276
|
</div>
|
|
269
277
|
<% end %>
|
|
270
278
|
|
data/lib/flipper/version.rb
CHANGED
|
@@ -64,5 +64,76 @@ RSpec.describe Flipper::UI::Actions::BooleanGate do
|
|
|
64
64
|
expect(last_response.headers['location']).to eq('/features/search')
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
context 'when disable_fully_enable is false' do
|
|
69
|
+
before { Flipper::UI.configuration.disable_fully_enable = false }
|
|
70
|
+
after { Flipper::UI.configuration.disable_fully_enable = nil }
|
|
71
|
+
|
|
72
|
+
it 'allows enabling the feature' do
|
|
73
|
+
flipper.disable :search
|
|
74
|
+
post 'features/search/boolean',
|
|
75
|
+
{ 'action' => 'Enable', 'authenticity_token' => token },
|
|
76
|
+
'rack.session' => session
|
|
77
|
+
expect(flipper.enabled?(:search)).to be(true)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
context 'when disable_fully_enable is true' do
|
|
82
|
+
before { Flipper::UI.configuration.disable_fully_enable = true }
|
|
83
|
+
after { Flipper::UI.configuration.disable_fully_enable = nil }
|
|
84
|
+
|
|
85
|
+
context 'with enable' do
|
|
86
|
+
before do
|
|
87
|
+
flipper.disable :search
|
|
88
|
+
post 'features/search/boolean',
|
|
89
|
+
{ 'action' => 'Enable', 'authenticity_token' => token },
|
|
90
|
+
'rack.session' => session
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'does not enable the feature' do
|
|
94
|
+
expect(flipper.enabled?(:search)).to be(false)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'returns 403 status' do
|
|
98
|
+
expect(last_response.status).to be(403)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'renders the default disabled message' do
|
|
102
|
+
expect(last_response.body).to include('Fully enabling features via the UI is disabled.')
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
context 'with disable' do
|
|
107
|
+
before do
|
|
108
|
+
flipper.enable :search
|
|
109
|
+
post 'features/search/boolean',
|
|
110
|
+
{ 'action' => 'Disable', 'authenticity_token' => token },
|
|
111
|
+
'rack.session' => session
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'still allows disabling the feature' do
|
|
115
|
+
expect(flipper.enabled?(:search)).to be(false)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it 'redirects back to feature' do
|
|
119
|
+
expect(last_response.status).to be(302)
|
|
120
|
+
expect(last_response.headers['location']).to eq('/features/search')
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context 'when disable_fully_enable is a custom message' do
|
|
126
|
+
before { Flipper::UI.configuration.disable_fully_enable = "Use deploy pipeline instead." }
|
|
127
|
+
after { Flipper::UI.configuration.disable_fully_enable = nil }
|
|
128
|
+
|
|
129
|
+
it 'renders the custom message on 403' do
|
|
130
|
+
flipper.disable :search
|
|
131
|
+
post 'features/search/boolean',
|
|
132
|
+
{ 'action' => 'Enable', 'authenticity_token' => token },
|
|
133
|
+
'rack.session' => session
|
|
134
|
+
expect(last_response.status).to be(403)
|
|
135
|
+
expect(last_response.body).to include('Use deploy pipeline instead.')
|
|
136
|
+
end
|
|
137
|
+
end
|
|
67
138
|
end
|
|
68
139
|
end
|
|
@@ -125,6 +125,32 @@ RSpec.describe Flipper::UI::Actions::Feature do
|
|
|
125
125
|
end
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
+
context "when disable_fully_enable is true" do
|
|
129
|
+
before { Flipper::UI.configuration.disable_fully_enable = true }
|
|
130
|
+
after { Flipper::UI.configuration.disable_fully_enable = nil }
|
|
131
|
+
|
|
132
|
+
it 'renders the Fully Enable button as disabled' do
|
|
133
|
+
get '/features/search'
|
|
134
|
+
expect(last_response.body).to include('Fully Enable')
|
|
135
|
+
expect(last_response.body).to match(/disabled\s*>/)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'shows the default disabled tooltip' do
|
|
139
|
+
get '/features/search'
|
|
140
|
+
expect(last_response.body).to include('Fully enabling features via the UI is disabled.')
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
context "when disable_fully_enable is a custom message" do
|
|
145
|
+
before { Flipper::UI.configuration.disable_fully_enable = "Use deploy pipeline instead." }
|
|
146
|
+
after { Flipper::UI.configuration.disable_fully_enable = nil }
|
|
147
|
+
|
|
148
|
+
it 'shows custom disabled tooltip' do
|
|
149
|
+
get '/features/search'
|
|
150
|
+
expect(last_response.body).to include('Use deploy pipeline instead.')
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
128
154
|
context 'custom actor names' do
|
|
129
155
|
before do
|
|
130
156
|
actor = Flipper::Actor.new('some_actor_name')
|
|
@@ -146,6 +146,39 @@ RSpec.describe Flipper::UI::Configuration do
|
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
148
|
|
|
149
|
+
describe "#disable_fully_enable" do
|
|
150
|
+
it "defaults to nil" do
|
|
151
|
+
expect(configuration.disable_fully_enable).to be_nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
it "can be set to true" do
|
|
155
|
+
configuration.disable_fully_enable = true
|
|
156
|
+
expect(configuration.disable_fully_enable).to eq(true)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
it "can be set to false" do
|
|
160
|
+
configuration.disable_fully_enable = false
|
|
161
|
+
expect(configuration.disable_fully_enable).to eq(false)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it "can be set to a custom message" do
|
|
165
|
+
configuration.disable_fully_enable = "Use deploy pipeline instead."
|
|
166
|
+
expect(configuration.disable_fully_enable).to eq("Use deploy pipeline instead.")
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe "#disable_fully_enable_message" do
|
|
171
|
+
it "returns default message when set to true" do
|
|
172
|
+
configuration.disable_fully_enable = true
|
|
173
|
+
expect(configuration.disable_fully_enable_message).to eq("Fully enabling features via the UI is disabled.")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "returns custom message when set to a string" do
|
|
177
|
+
configuration.disable_fully_enable = "Use deploy pipeline instead."
|
|
178
|
+
expect(configuration.disable_fully_enable_message).to eq("Use deploy pipeline instead.")
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
149
182
|
describe "#show_feature_description_in_list" do
|
|
150
183
|
it "has default value" do
|
|
151
184
|
expect(configuration.show_feature_description_in_list).to eq(false)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flipper-ui
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.4.
|
|
4
|
+
version: 1.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Nunemaker
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -76,14 +76,14 @@ dependencies:
|
|
|
76
76
|
requirements:
|
|
77
77
|
- - "~>"
|
|
78
78
|
- !ruby/object:Gem::Version
|
|
79
|
-
version: 1.4.
|
|
79
|
+
version: 1.4.1
|
|
80
80
|
type: :runtime
|
|
81
81
|
prerelease: false
|
|
82
82
|
version_requirements: !ruby/object:Gem::Requirement
|
|
83
83
|
requirements:
|
|
84
84
|
- - "~>"
|
|
85
85
|
- !ruby/object:Gem::Version
|
|
86
|
-
version: 1.4.
|
|
86
|
+
version: 1.4.1
|
|
87
87
|
- !ruby/object:Gem::Dependency
|
|
88
88
|
name: erubi
|
|
89
89
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -166,6 +166,7 @@ files:
|
|
|
166
166
|
- lib/flipper/ui/views/add_actor.erb
|
|
167
167
|
- lib/flipper/ui/views/add_feature.erb
|
|
168
168
|
- lib/flipper/ui/views/add_group.erb
|
|
169
|
+
- lib/flipper/ui/views/disable_fully_enable.erb
|
|
169
170
|
- lib/flipper/ui/views/feature.erb
|
|
170
171
|
- lib/flipper/ui/views/feature_creation_disabled.erb
|
|
171
172
|
- lib/flipper/ui/views/feature_removal_disabled.erb
|
|
@@ -202,7 +203,7 @@ metadata:
|
|
|
202
203
|
homepage_uri: https://www.flippercloud.io
|
|
203
204
|
source_code_uri: https://github.com/flippercloud/flipper
|
|
204
205
|
bug_tracker_uri: https://github.com/flippercloud/flipper/issues
|
|
205
|
-
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.4.
|
|
206
|
+
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.4.1
|
|
206
207
|
funding_uri: https://github.com/sponsors/flippercloud
|
|
207
208
|
post_install_message:
|
|
208
209
|
rdoc_options: []
|