flipper-api 0.20.3 → 0.22.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: cddd925172819bb8de72eb02d85ee014bd170fb0bfd0b4e071eabeedb162911f
4
- data.tar.gz: 6be5a9daf2461963d9b14b5c2f3edab6102e4c69f6952ca13168d53690701131
3
+ metadata.gz: c9e87d4217900f7ce0ff46ea9fa6c145ec1d01e226395529e7ec8ab782bf8119
4
+ data.tar.gz: 8473cf20629a95691441912a1a8b0ed623b59f84865c2f45410d600fb9a342c1
5
5
  SHA512:
6
- metadata.gz: e749a826aae5fa8c9e9942e828f77eb738d62788a646637bb5c46723a85f8444cf760b8630bbcf562bed90121ca43f488d1b45f9575f67db3cac995ea96b3427
7
- data.tar.gz: 978a81bbab7f4d5bb2dc1cb5004c0328e7d379f23f6dddd7a60762f5a7ee69b54c860aa70539271a2e66e219a16de55ac1173d0ba593a632d16570627b775a19
6
+ metadata.gz: d16c137e2541de6fa6d53f7e48296be6a17b98bed5c8393274af9766c041c1189bfa823934e694d46b3d041d34df4e132e3158f6a3f7cdba8a9ebc9e9b1b8b0a
7
+ data.tar.gz: 5979c36e18707e094941a0e12fae47a2203a6bd5b53218c574b63d100f9323109646bd7a429d8c89c4611e86e9a152642feefc74bdb8deea19ac07b6c9a306c2
data/lib/flipper/api.rb CHANGED
@@ -9,14 +9,11 @@ module Flipper
9
9
 
10
10
  def self.app(flipper = nil, options = {})
11
11
  env_key = options.fetch(:env_key, 'flipper')
12
- memoizer_options = options.fetch(:memoizer_options, {})
13
-
14
12
  app = ->(_) { [404, { 'Content-Type'.freeze => CONTENT_TYPE }, ['{}'.freeze]] }
15
13
  builder = Rack::Builder.new
16
14
  yield builder if block_given?
17
15
  builder.use Flipper::Api::JsonParams
18
16
  builder.use Flipper::Middleware::SetupEnv, flipper, env_key: env_key
19
- builder.use Flipper::Middleware::Memoizer, memoizer_options.merge(env_key: env_key)
20
17
  builder.use Flipper::Api::Middleware, env_key: env_key
21
18
  builder.run app
22
19
  klass = self
@@ -32,10 +32,11 @@ module Flipper
32
32
  def call!(env)
33
33
  request = Rack::Request.new(env)
34
34
  action_class = @action_collection.action_for_request(request)
35
+
35
36
  if action_class.nil?
36
37
  @app.call(env)
37
38
  else
38
- flipper = env.fetch(@env_key)
39
+ flipper = env.fetch(@env_key) { Flipper }
39
40
  action_class.run(flipper, request)
40
41
  end
41
42
  end
@@ -30,9 +30,17 @@ module Flipper
30
30
 
31
31
  private
32
32
 
33
+ def percentage_param
34
+ @percentage_param ||= params['percentage'].to_s
35
+ end
36
+
33
37
  def percentage
34
38
  @percentage ||= begin
35
- Integer(params['percentage'])
39
+ unless percentage_param.match(/\d/)
40
+ raise ArgumentError, "invalid numeric value: #{percentage_param}"
41
+ end
42
+
43
+ Flipper::Types::Percentage.new(percentage_param).value
36
44
  rescue ArgumentError, TypeError
37
45
  -1
38
46
  end
@@ -31,9 +31,17 @@ module Flipper
31
31
 
32
32
  private
33
33
 
34
+ def percentage_param
35
+ @percentage_param ||= params['percentage'].to_s
36
+ end
37
+
34
38
  def percentage
35
39
  @percentage ||= begin
36
- Integer(params['percentage'])
40
+ unless percentage_param.match(/\d/)
41
+ raise ArgumentError, "invalid numeric value: #{percentage_param}"
42
+ end
43
+
44
+ Flipper::Types::Percentage.new(percentage_param).value
37
45
  rescue ArgumentError, TypeError
38
46
  -1
39
47
  end
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.20.3'.freeze
2
+ VERSION = '0.22.0'.freeze
3
3
  end
@@ -44,7 +44,7 @@ RSpec.describe Flipper::Api::V1::Actions::ActorsGate do
44
44
 
45
45
  describe 'enable feature with slash in name' do
46
46
  before do
47
- flipper[:my_feature].disable_actor(actor)
47
+ flipper["my/feature"].disable_actor(actor)
48
48
  post '/features/my/feature/actors', flipper_id: actor.flipper_id
49
49
  end
50
50
 
@@ -60,6 +60,24 @@ RSpec.describe Flipper::Api::V1::Actions::ActorsGate do
60
60
  end
61
61
  end
62
62
 
63
+ describe 'enable feature with space in name' do
64
+ before do
65
+ flipper["sp ace"].disable_actor(actor)
66
+ post '/features/sp%20ace/actors', flipper_id: actor.flipper_id
67
+ end
68
+
69
+ it 'enables feature for actor' do
70
+ expect(last_response.status).to eq(200)
71
+ expect(flipper["sp ace"].enabled?(actor)).to be_truthy
72
+ expect(flipper["sp ace"].enabled_gate_names).to eq([:actor])
73
+ end
74
+
75
+ it 'returns decorated feature with actor enabled' do
76
+ gate = json_response['gates'].find { |gate| gate['key'] == 'actors' }
77
+ expect(gate['value']).to eq(['1'])
78
+ end
79
+ end
80
+
63
81
  describe 'enable missing flipper_id parameter' do
64
82
  before do
65
83
  flipper[:my_feature].enable
@@ -4,53 +4,79 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
4
4
  let(:app) { build_api(flipper) }
5
5
 
6
6
  describe 'enable' do
7
+ shared_examples 'gates with percentage' do
8
+ it 'enables gate for feature' do
9
+ expect(flipper[path].enabled_gate_names).to include(:percentage_of_actors)
10
+ end
11
+
12
+ it 'returns decorated feature with gate enabled for a percent of actors' do
13
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
14
+ expect(gate['value']).to eq(percentage)
15
+ end
16
+ end
17
+
7
18
  context 'for feature with slash in name' do
19
+ let(:path) { 'my/feature' }
20
+
8
21
  before do
9
- flipper["my/feature"].disable
10
- post '/features/my/feature/percentage_of_actors', percentage: '10'
22
+ flipper[path].disable
23
+ post "/features/#{path}/percentage_of_actors", percentage: percentage
11
24
  end
12
25
 
13
- it 'enables gate for feature' do
14
- expect(flipper["my/feature"].enabled_gate_names).to include(:percentage_of_actors)
26
+ context 'with integer percentage' do
27
+ let(:percentage) { '10' }
28
+
29
+ it_behaves_like 'gates with percentage'
15
30
  end
16
31
 
17
- it 'returns decorated feature with gate enabled for 10 percent of actors' do
18
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
19
- expect(gate['value']).to eq('10')
32
+ context 'with decimal percentage' do
33
+ let(:percentage) { '0.05' }
34
+
35
+ it_behaves_like 'gates with percentage'
20
36
  end
21
37
  end
22
38
 
23
39
  context 'url-encoded request' do
40
+ let(:path) { :my_feature }
41
+
24
42
  before do
25
43
  flipper[:my_feature].disable
26
- post '/features/my_feature/percentage_of_actors', percentage: '10'
44
+ post "/features/#{path}/percentage_of_actors", percentage: percentage
27
45
  end
28
46
 
29
- it 'enables gate for feature' do
30
- expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_actors)
47
+ context 'with integer percentage' do
48
+ let(:percentage) { '10' }
49
+
50
+ it_behaves_like 'gates with percentage'
31
51
  end
32
52
 
33
- it 'returns decorated feature with gate enabled for 10 percent of actors' do
34
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
35
- expect(gate['value']).to eq('10')
53
+ context 'with decimal percentage' do
54
+ let(:percentage) { '0.05' }
55
+
56
+ it_behaves_like 'gates with percentage'
36
57
  end
37
58
  end
38
59
 
39
60
  context 'json request' do
61
+ let(:path) { :my_feature }
62
+
40
63
  before do
41
64
  flipper[:my_feature].disable
42
- post '/features/my_feature/percentage_of_actors',
43
- JSON.generate(percentage: '10'),
65
+ post "/features/#{path}/percentage_of_actors",
66
+ JSON.generate(percentage: percentage),
44
67
  'CONTENT_TYPE' => 'application/json'
45
68
  end
46
69
 
47
- it 'enables gate for feature' do
48
- expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_actors)
70
+ context 'with integer percentage' do
71
+ let(:percentage) { '10' }
72
+
73
+ it_behaves_like 'gates with percentage'
49
74
  end
50
75
 
51
- it 'returns decorated feature with gate enabled for 10 percent of actors' do
52
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
53
- expect(gate['value']).to eq('10')
76
+ context 'with decimal percentage' do
77
+ let(:percentage) { '0.05' }
78
+
79
+ it_behaves_like 'gates with percentage'
54
80
  end
55
81
  end
56
82
  end
@@ -113,7 +139,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
113
139
  end
114
140
  end
115
141
 
116
- describe 'percentage parameter not an integer' do
142
+ describe 'percentage parameter not a number' do
117
143
  before do
118
144
  flipper[:my_feature].disable
119
145
  post '/features/my_feature/percentage_of_actors', percentage: 'foo'
@@ -4,34 +4,80 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
4
4
  let(:app) { build_api(flipper) }
5
5
 
6
6
  describe 'enable' do
7
- before do
8
- flipper[:my_feature].disable
9
- post '/features/my_feature/percentage_of_time', percentage: '10'
10
- end
7
+ shared_examples 'gates with percentage' do
8
+ it 'enables gate for feature' do
9
+ expect(flipper[path].enabled_gate_names).to include(:percentage_of_time)
10
+ end
11
11
 
12
- it 'enables gate for feature' do
13
- expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_time)
12
+ it 'returns decorated feature with gate enabled for a percent of times' do
13
+ gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
14
+ expect(gate['value']).to eq(percentage)
15
+ end
14
16
  end
15
17
 
16
- it 'returns decorated feature with gate enabled for 5% of time' do
17
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
18
- expect(gate['value']).to eq('10')
19
- end
20
- end
18
+ context 'for feature with slash in name' do
19
+ let(:path) { 'my/feature' }
21
20
 
22
- describe 'enable for feature with slash in name' do
23
- before do
24
- flipper["my/feature"].disable
25
- post '/features/my/feature/percentage_of_time', percentage: '10'
21
+ before do
22
+ flipper[path].disable
23
+ post "/features/#{path}/percentage_of_time", percentage: percentage
24
+ end
25
+
26
+ context 'with integer percentage' do
27
+ let(:percentage) { '10' }
28
+
29
+ it_behaves_like 'gates with percentage'
30
+ end
31
+
32
+ context 'with decimal percentage' do
33
+ let(:percentage) { '0.05' }
34
+
35
+ it_behaves_like 'gates with percentage'
36
+ end
26
37
  end
27
38
 
28
- it 'enables gate for feature' do
29
- expect(flipper["my/feature"].enabled_gate_names).to include(:percentage_of_time)
39
+ context 'url-encoded request' do
40
+ let(:path) { :my_feature }
41
+
42
+ before do
43
+ flipper[:my_feature].disable
44
+ post "/features/#{path}/percentage_of_time", percentage: percentage
45
+ end
46
+
47
+ context 'with integer percentage' do
48
+ let(:percentage) { '10' }
49
+
50
+ it_behaves_like 'gates with percentage'
51
+ end
52
+
53
+ context 'with decimal percentage' do
54
+ let(:percentage) { '0.05' }
55
+
56
+ it_behaves_like 'gates with percentage'
57
+ end
30
58
  end
31
59
 
32
- it 'returns decorated feature with gate enabled for 5% of time' do
33
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
34
- expect(gate['value']).to eq('10')
60
+ context 'json request' do
61
+ let(:path) { :my_feature }
62
+
63
+ before do
64
+ flipper[:my_feature].disable
65
+ post "/features/#{path}/percentage_of_time",
66
+ JSON.generate(percentage: percentage),
67
+ 'CONTENT_TYPE' => 'application/json'
68
+ end
69
+
70
+ context 'with integer percentage' do
71
+ let(:percentage) { '10' }
72
+
73
+ it_behaves_like 'gates with percentage'
74
+ end
75
+
76
+ context 'with decimal percentage' do
77
+ let(:percentage) { '0.05' }
78
+
79
+ it_behaves_like 'gates with percentage'
80
+ end
35
81
  end
36
82
  end
37
83
 
@@ -93,7 +139,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
93
139
  end
94
140
  end
95
141
 
96
- describe 'percentage parameter not an integer' do
142
+ describe 'percentage parameter not an number' do
97
143
  before do
98
144
  flipper[:my_feature].disable
99
145
  post '/features/my_feature/percentage_of_time', percentage: 'foo'
@@ -1,6 +1,36 @@
1
1
  require 'helper'
2
2
 
3
3
  RSpec.describe Flipper::Api do
4
+ describe 'Initializing middleware with flipper instance' do
5
+ let(:app) { build_api(flipper) }
6
+
7
+ it 'works' do
8
+ flipper.enable :a
9
+ flipper.disable :b
10
+
11
+ get '/features'
12
+
13
+ expect(last_response.status).to be(200)
14
+ feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') }
15
+ expect(feature_names).to eq(%w(a b))
16
+ end
17
+ end
18
+
19
+ describe 'Initializing middleware lazily with a block' do
20
+ let(:app) { build_api(-> { flipper }) }
21
+
22
+ it 'works' do
23
+ flipper.enable :a
24
+ flipper.disable :b
25
+
26
+ get '/features'
27
+
28
+ expect(last_response.status).to be(200)
29
+ feature_names = json_response.fetch('features').map { |feature| feature.fetch('key') }
30
+ expect(feature_names).to eq(%w(a b))
31
+ end
32
+ end
33
+
4
34
  context 'when initialized with flipper instance and flipper instance in env' do
5
35
  let(:app) { build_api(flipper) }
6
36
 
@@ -76,4 +106,10 @@ RSpec.describe Flipper::Api do
76
106
  expect(json_response).to eq({})
77
107
  end
78
108
  end
109
+
110
+ describe 'Inspecting the built Rack app' do
111
+ it 'returns a String' do
112
+ expect(build_api(flipper).inspect).to be_a(String)
113
+ end
114
+ end
79
115
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.3
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-10 00:00:00.000000000 Z
11
+ date: 2021-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.20.3
39
+ version: 0.22.0
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.20.3
46
+ version: 0.22.0
47
47
  description:
48
48
  email:
49
49
  - nunemaker@gmail.com