flipper-api 0.20.3 → 0.22.0

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 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