flipper-api 0.9.2 → 0.10.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 +4 -4
- data/lib/flipper/api.rb +1 -0
- data/lib/flipper/api/action.rb +17 -0
- data/lib/flipper/api/actor.rb +13 -0
- data/lib/flipper/api/error_response.rb +32 -0
- data/lib/flipper/api/middleware.rb +4 -0
- data/lib/flipper/api/v1/actions/actors_gate.rb +54 -0
- data/lib/flipper/api/v1/actions/boolean_gate.rb +15 -6
- data/lib/flipper/api/v1/actions/feature.rb +2 -3
- data/lib/flipper/api/v1/actions/features.rb +5 -10
- data/lib/flipper/api/v1/actions/groups_gate.rb +49 -0
- data/lib/flipper/api/v1/actions/percentage_of_actors_gate.rb +64 -0
- data/lib/flipper/api/v1/actions/percentage_of_time_gate.rb +64 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/api/action_spec.rb +74 -25
- data/spec/flipper/api/v1/actions/actors_gate_spec.rb +115 -0
- data/spec/flipper/api/v1/actions/boolean_gate_spec.rb +12 -12
- data/spec/flipper/api/v1/actions/feature_spec.rb +26 -6
- data/spec/flipper/api/v1/actions/features_spec.rb +47 -12
- data/spec/flipper/api/v1/actions/groups_gate_spec.rb +75 -0
- data/spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb +85 -0
- data/spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb +84 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd696bdc261c62486cab5a607d1ef98e21eaf9d1
|
4
|
+
data.tar.gz: 3bec1050517cd29ac18233562644ea0d859f00b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 08e7142b00964657bf6e21afc3fb192c900305b8825e67c500bffc1e51bfe271a80be3f2ac6b0288cf9fb44eb3476bddc0220013e01f6b8339454d0a42159deb
|
7
|
+
data.tar.gz: f6c94f090cc3e2745348a06edaf4aed37449df84879888f697e55b14046a7cb1e9ff4f0e9a9b657afd39db3df6a5d743e4640fcd8514999c8c3280a4c94df364
|
data/lib/flipper/api.rb
CHANGED
data/lib/flipper/api/action.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'forwardable'
|
2
2
|
require 'flipper/api/error'
|
3
|
+
require 'flipper/api/error_response'
|
3
4
|
require 'json'
|
4
5
|
|
5
6
|
module Flipper
|
@@ -88,6 +89,12 @@ module Flipper
|
|
88
89
|
throw :halt, response
|
89
90
|
end
|
90
91
|
|
92
|
+
# Public: Call this with a json serializable object (i.e. Hash)
|
93
|
+
# to serialize object and respond to request
|
94
|
+
#
|
95
|
+
# object - json serializable object
|
96
|
+
# status - http status code
|
97
|
+
|
91
98
|
def json_response(object, status = 200)
|
92
99
|
header 'Content-Type', Api::CONTENT_TYPE
|
93
100
|
status(status)
|
@@ -95,6 +102,16 @@ module Flipper
|
|
95
102
|
halt [@code, @headers, [body]]
|
96
103
|
end
|
97
104
|
|
105
|
+
# Public: Call this with an ErrorResponse::ERRORS key to respond
|
106
|
+
# with the serialized error object as response body
|
107
|
+
#
|
108
|
+
# error_key - key to lookup error object
|
109
|
+
|
110
|
+
def json_error_response(error_key)
|
111
|
+
error = ErrorResponse::ERRORS.fetch(error_key.to_sym)
|
112
|
+
json_response(error.as_json, error.http_status)
|
113
|
+
end
|
114
|
+
|
98
115
|
# Public: Set the status code for the response.
|
99
116
|
#
|
100
117
|
# code - The Integer code you would like the response to return.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Flipper
|
2
|
+
module Api
|
3
|
+
# Internal: Shim for turning a string flipper id into something that responds to
|
4
|
+
# flipper_id for Flipper::Types::Actor.
|
5
|
+
class Actor
|
6
|
+
attr_reader :flipper_id
|
7
|
+
|
8
|
+
def initialize(flipper_id)
|
9
|
+
@flipper_id = flipper_id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Flipper
|
2
|
+
module Api
|
3
|
+
module ErrorResponse
|
4
|
+
class Error
|
5
|
+
attr_reader :http_status
|
6
|
+
|
7
|
+
def initialize(code, message, info, http_status)
|
8
|
+
@code = code
|
9
|
+
@message = message
|
10
|
+
@more_info = info
|
11
|
+
@http_status = http_status
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json
|
15
|
+
{
|
16
|
+
code: @code,
|
17
|
+
message: @message,
|
18
|
+
more_info: @more_info,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
ERRORS = {
|
24
|
+
feature_not_found: Error.new(1, "Feature not found.", "", 404),
|
25
|
+
group_not_registered: Error.new(2, "Group not registered.", "", 404),
|
26
|
+
percentage_invalid: Error.new(3, "Percentage must be a positive number less than or equal to 100.", "", 422),
|
27
|
+
flipper_id_invalid: Error.new(4, "Required parameter flipper_id is missing.", "", 422),
|
28
|
+
name_invalid: Error.new(5, "Required parameter name is missing.", "", 422),
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -35,6 +35,10 @@ module Flipper
|
|
35
35
|
end
|
36
36
|
|
37
37
|
@action_collection = ActionCollection.new
|
38
|
+
@action_collection.add Api::V1::Actions::PercentageOfTimeGate
|
39
|
+
@action_collection.add Api::V1::Actions::PercentageOfActorsGate
|
40
|
+
@action_collection.add Api::V1::Actions::ActorsGate
|
41
|
+
@action_collection.add Api::V1::Actions::GroupsGate
|
38
42
|
@action_collection.add Api::V1::Actions::BooleanGate
|
39
43
|
@action_collection.add Api::V1::Actions::Feature
|
40
44
|
@action_collection.add Api::V1::Actions::Features
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'flipper/api/action'
|
2
|
+
require 'flipper/api/v1/decorators/feature'
|
3
|
+
|
4
|
+
module Flipper
|
5
|
+
module Api
|
6
|
+
module V1
|
7
|
+
module Actions
|
8
|
+
class ActorsGate < Api::Action
|
9
|
+
route %r{api/v1/features/[^/]*/actors/?\Z}
|
10
|
+
|
11
|
+
def post
|
12
|
+
ensure_valid_params
|
13
|
+
feature = flipper[feature_name]
|
14
|
+
actor = Actor.new(flipper_id)
|
15
|
+
feature.enable_actor(actor)
|
16
|
+
decorated_feature = Decorators::Feature.new(feature)
|
17
|
+
json_response(decorated_feature.as_json, 200)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete
|
21
|
+
ensure_valid_params
|
22
|
+
feature = flipper[feature_name]
|
23
|
+
actor = Actor.new(flipper_id)
|
24
|
+
feature.disable_actor(actor)
|
25
|
+
decorated_feature = Decorators::Feature.new(feature)
|
26
|
+
json_response(decorated_feature.as_json, 200)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def ensure_valid_params
|
32
|
+
unless feature_names.include?(feature_name)
|
33
|
+
json_error_response(:feature_not_found)
|
34
|
+
end
|
35
|
+
|
36
|
+
json_error_response(:flipper_id_invalid) if flipper_id.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
def feature_name
|
40
|
+
@feature_name ||= Rack::Utils.unescape(path_parts[-2])
|
41
|
+
end
|
42
|
+
|
43
|
+
def flipper_id
|
44
|
+
@flipper_id ||= params['flipper_id']
|
45
|
+
end
|
46
|
+
|
47
|
+
def feature_names
|
48
|
+
@feature_names ||= flipper.adapter.features
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -1,18 +1,27 @@
|
|
1
1
|
require 'flipper/api/action'
|
2
|
+
require 'flipper/api/v1/decorators/feature'
|
2
3
|
|
3
4
|
module Flipper
|
4
5
|
module Api
|
5
6
|
module V1
|
6
7
|
module Actions
|
7
8
|
class BooleanGate < Api::Action
|
8
|
-
route %r{api/v1/features/[^/]*/
|
9
|
-
|
10
|
-
def
|
9
|
+
route %r{api/v1/features/[^/]*/boolean/?\Z}
|
10
|
+
|
11
|
+
def post
|
12
|
+
feature_name = Rack::Utils.unescape(path_parts[-2])
|
13
|
+
feature = flipper[feature_name]
|
14
|
+
feature.enable
|
15
|
+
decorated_feature = Decorators::Feature.new(feature)
|
16
|
+
json_response(decorated_feature.as_json, 200)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete
|
11
20
|
feature_name = Rack::Utils.unescape(path_parts[-2])
|
12
21
|
feature = flipper[feature_name.to_sym]
|
13
|
-
|
14
|
-
|
15
|
-
json_response(
|
22
|
+
feature.disable
|
23
|
+
decorated_feature = Decorators::Feature.new(feature)
|
24
|
+
json_response(decorated_feature.as_json, 200)
|
16
25
|
end
|
17
26
|
end
|
18
27
|
end
|
@@ -14,17 +14,16 @@ module Flipper
|
|
14
14
|
feature = Decorators::Feature.new(flipper[feature_name])
|
15
15
|
json_response(feature.as_json)
|
16
16
|
else
|
17
|
-
|
17
|
+
json_error_response(:feature_not_found)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
def delete
|
22
22
|
if feature_names.include?(feature_name)
|
23
23
|
flipper.remove(feature_name)
|
24
|
-
|
25
24
|
json_response({}, 204)
|
26
25
|
else
|
27
|
-
|
26
|
+
json_error_response(:feature_not_found)
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
@@ -21,16 +21,11 @@ module Flipper
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def post
|
24
|
-
feature_name = params.fetch('name')
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}, 422)
|
30
|
-
end
|
31
|
-
|
32
|
-
flipper.adapter.add(flipper[feature_name])
|
33
|
-
json_response({}, 200)
|
24
|
+
feature_name = params.fetch('name') { json_error_response(:name_invalid) }
|
25
|
+
feature = flipper[feature_name]
|
26
|
+
flipper.adapter.add(feature)
|
27
|
+
decorated_feature = Decorators::Feature.new(feature)
|
28
|
+
json_response(decorated_feature.as_json, 200)
|
34
29
|
end
|
35
30
|
end
|
36
31
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'flipper/api/action'
|
2
|
+
require 'flipper/api/v1/decorators/feature'
|
3
|
+
|
4
|
+
module Flipper
|
5
|
+
module Api
|
6
|
+
module V1
|
7
|
+
module Actions
|
8
|
+
class GroupsGate < Api::Action
|
9
|
+
route %r{api/v1/features/[^/]*/groups/?\Z}
|
10
|
+
|
11
|
+
def post
|
12
|
+
ensure_valid_params
|
13
|
+
feature = flipper[feature_name]
|
14
|
+
feature.enable_group(group_name)
|
15
|
+
decorated_feature = Decorators::Feature.new(feature)
|
16
|
+
json_response(decorated_feature.as_json, 200)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete
|
20
|
+
ensure_valid_params
|
21
|
+
feature = flipper[feature_name]
|
22
|
+
feature.disable_group(group_name)
|
23
|
+
decorated_feature = Decorators::Feature.new(feature)
|
24
|
+
json_response(decorated_feature.as_json, 200)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def ensure_valid_params
|
30
|
+
json_error_response(:feature_not_found) unless feature_names.include?(feature_name)
|
31
|
+
json_error_response(:group_not_registered) unless Flipper.group_exists?(group_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def feature_name
|
35
|
+
@feature_name ||= Rack::Utils.unescape(path_parts[-2])
|
36
|
+
end
|
37
|
+
|
38
|
+
def group_name
|
39
|
+
@group_name ||= params['name']
|
40
|
+
end
|
41
|
+
|
42
|
+
def feature_names
|
43
|
+
@feature_names ||= flipper.adapter.features
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'flipper/api/action'
|
2
|
+
require 'flipper/api/v1/decorators/feature'
|
3
|
+
|
4
|
+
module Flipper
|
5
|
+
module Api
|
6
|
+
module V1
|
7
|
+
module Actions
|
8
|
+
class PercentageOfActorsGate < Api::Action
|
9
|
+
route %r{api/v1/features/[^/]*/percentage_of_actors/?\Z}
|
10
|
+
|
11
|
+
def post
|
12
|
+
ensure_valid_enable_params
|
13
|
+
feature = flipper[feature_name]
|
14
|
+
feature.enable_percentage_of_actors(percentage)
|
15
|
+
decorated_feature = Decorators::Feature.new(feature)
|
16
|
+
json_response(decorated_feature.as_json, 200)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete
|
20
|
+
ensure_valid_disable_params
|
21
|
+
feature = flipper[feature_name]
|
22
|
+
feature.disable_percentage_of_actors
|
23
|
+
decorated_feature = Decorators::Feature.new(feature)
|
24
|
+
json_response(decorated_feature.as_json, 200)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def ensure_valid_enable_params
|
30
|
+
unless feature_names.include?(feature_name)
|
31
|
+
json_error_response(:feature_not_found)
|
32
|
+
end
|
33
|
+
|
34
|
+
if percentage < 0 || percentage > 100
|
35
|
+
json_error_response(:percentage_invalid)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_valid_disable_params
|
40
|
+
unless feature_names.include?(feature_name)
|
41
|
+
json_error_response(:feature_not_found)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def feature_name
|
46
|
+
@feature_name ||= Rack::Utils.unescape(path_parts[-2])
|
47
|
+
end
|
48
|
+
|
49
|
+
def percentage
|
50
|
+
@percentage ||= begin
|
51
|
+
Integer(params['percentage'])
|
52
|
+
rescue ArgumentError, TypeError
|
53
|
+
-1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def feature_names
|
58
|
+
@feature_names ||= flipper.adapter.features
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'flipper/api/action'
|
2
|
+
require 'flipper/api/v1/decorators/feature'
|
3
|
+
|
4
|
+
module Flipper
|
5
|
+
module Api
|
6
|
+
module V1
|
7
|
+
module Actions
|
8
|
+
class PercentageOfTimeGate < Api::Action
|
9
|
+
route %r{api/v1/features/[^/]*/percentage_of_time/?\Z}
|
10
|
+
|
11
|
+
def post
|
12
|
+
ensure_valid_enable_params
|
13
|
+
feature = flipper[feature_name]
|
14
|
+
feature.enable_percentage_of_time(percentage)
|
15
|
+
decorated_feature = Decorators::Feature.new(feature)
|
16
|
+
json_response(decorated_feature.as_json, 200)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete
|
20
|
+
ensure_valid_disable_params
|
21
|
+
feature = flipper[feature_name]
|
22
|
+
feature.disable_percentage_of_time
|
23
|
+
decorated_feature = Decorators::Feature.new(feature)
|
24
|
+
json_response(decorated_feature.as_json, 200)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def ensure_valid_enable_params
|
30
|
+
unless feature_names.include?(feature_name)
|
31
|
+
json_error_response(:feature_not_found)
|
32
|
+
end
|
33
|
+
|
34
|
+
if percentage < 0 || percentage > 100
|
35
|
+
json_error_response(:percentage_invalid)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def ensure_valid_disable_params
|
40
|
+
unless feature_names.include?(feature_name)
|
41
|
+
json_error_response(:feature_not_found)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def feature_name
|
46
|
+
@feature_name ||= Rack::Utils.unescape(path_parts[-2])
|
47
|
+
end
|
48
|
+
|
49
|
+
def percentage
|
50
|
+
@percentage ||= begin
|
51
|
+
Integer(params['percentage'])
|
52
|
+
rescue ArgumentError, TypeError
|
53
|
+
-1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def feature_names
|
58
|
+
@feature_names ||= flipper.adapter.features
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/flipper/version.rb
CHANGED
@@ -25,35 +25,84 @@ RSpec.describe Flipper::Api::Action do
|
|
25
25
|
end
|
26
26
|
}
|
27
27
|
|
28
|
-
|
29
|
-
fake_request = Struct.new(:request_method, :env, :session).new("NOOOOPE", {}, {})
|
30
|
-
action = action_subclass.new(flipper, fake_request)
|
31
|
-
expect {
|
32
|
-
action.run
|
33
|
-
}.to raise_error(Flipper::Api::RequestMethodNotSupported)
|
34
|
-
end
|
28
|
+
describe 'https verbs' do
|
35
29
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
30
|
+
it "won't run method that isn't whitelisted" do
|
31
|
+
fake_request = Struct.new(:request_method, :env, :session).new("NOOOOPE", {}, {})
|
32
|
+
action = action_subclass.new(flipper, fake_request)
|
33
|
+
expect {
|
34
|
+
action.run
|
35
|
+
}.to raise_error(Flipper::Api::RequestMethodNotSupported)
|
36
|
+
end
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
38
|
+
it "will run get" do
|
39
|
+
fake_request = Struct.new(:request_method, :env, :session).new("GET", {}, {})
|
40
|
+
action = action_subclass.new(flipper, fake_request)
|
41
|
+
expect(action.run).to eq([200, {}, "get"])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "will run post" do
|
45
|
+
fake_request = Struct.new(:request_method, :env, :session).new("POST", {}, {})
|
46
|
+
action = action_subclass.new(flipper, fake_request)
|
47
|
+
expect(action.run).to eq([200, {}, "post"])
|
48
|
+
end
|
49
|
+
|
50
|
+
it "will run put" do
|
51
|
+
fake_request = Struct.new(:request_method, :env, :session).new("PUT", {}, {})
|
52
|
+
action = action_subclass.new(flipper, fake_request)
|
53
|
+
expect(action.run).to eq([200, {}, "put"])
|
54
|
+
end
|
47
55
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
56
|
+
it "will run delete" do
|
57
|
+
fake_request = Struct.new(:request_method, :env, :session).new("DELETE", {}, {})
|
58
|
+
action = action_subclass.new(flipper, fake_request)
|
59
|
+
expect(action.run).to eq([200, {}, "delete"])
|
60
|
+
end
|
52
61
|
end
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
63
|
+
describe '#json_error_response' do
|
64
|
+
describe ":feature_not_found" do
|
65
|
+
|
66
|
+
it 'locates and serializes error correctly' do
|
67
|
+
action = action_subclass.new({}, {})
|
68
|
+
response = catch(:halt) do
|
69
|
+
action.json_error_response(:feature_not_found)
|
70
|
+
end
|
71
|
+
status, headers, body = response
|
72
|
+
parsed_body = JSON.parse(body[0])
|
73
|
+
|
74
|
+
expect(headers["Content-Type"]).to eq("application/json")
|
75
|
+
expect(parsed_body["code"]).to eq(1)
|
76
|
+
expect(parsed_body["message"]).to eq("Feature not found.")
|
77
|
+
expect(parsed_body["more_info"]).to eq("")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe ':group_not_registered' do
|
82
|
+
|
83
|
+
it 'locates and serializes error correctly' do
|
84
|
+
action = action_subclass.new({}, {})
|
85
|
+
response = catch(:halt) do
|
86
|
+
action.json_error_response(:group_not_registered)
|
87
|
+
end
|
88
|
+
status, headers, body = response
|
89
|
+
parsed_body = JSON.parse(body[0])
|
90
|
+
|
91
|
+
expect(headers["Content-Type"]).to eq("application/json")
|
92
|
+
expect(parsed_body["code"]).to eq(2)
|
93
|
+
expect(parsed_body["message"]).to eq("Group not registered.")
|
94
|
+
expect(parsed_body["more_info"]).to eq("")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'invalid error key' do
|
99
|
+
|
100
|
+
it 'raises descriptive error' do
|
101
|
+
action = action_subclass.new({}, {})
|
102
|
+
catch(:halt) do
|
103
|
+
expect{ action.json_error_response(:invalid_error_key) }.to raise_error(KeyError)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
58
107
|
end
|
59
108
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Api::V1::Actions::ActorsGate do
|
4
|
+
let(:app) { build_api(flipper) }
|
5
|
+
|
6
|
+
describe 'enable' do
|
7
|
+
let(:actor) { Flipper::Api::Actor.new("1") }
|
8
|
+
|
9
|
+
before do
|
10
|
+
flipper[:my_feature].disable_actor(actor)
|
11
|
+
post '/api/v1/features/my_feature/actors', { flipper_id: actor.flipper_id }
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'enables feature for actor' do
|
15
|
+
expect(last_response.status).to eq(200)
|
16
|
+
expect(flipper[:my_feature].enabled?(actor)).to be_truthy
|
17
|
+
expect(flipper[:my_feature].enabled_gate_names).to eq([:actor])
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'returns decorated feature with actor enabled' do
|
21
|
+
gate = json_response['gates'].find { |gate| gate['key'] == 'actors' }
|
22
|
+
expect(gate['value']).to eq(["1"])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'disable' do
|
27
|
+
let(:actor) { Flipper::Api::Actor.new("1") }
|
28
|
+
|
29
|
+
before do
|
30
|
+
flipper[:my_feature].enable_actor(actor)
|
31
|
+
delete '/api/v1/features/my_feature/actors', { flipper_id: actor.flipper_id }
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'disables feature' do
|
35
|
+
expect(last_response.status).to eq(200)
|
36
|
+
expect(flipper[:my_feature].enabled?(actor)).to be_falsy
|
37
|
+
expect(flipper[:my_feature].enabled_gate_names).to be_empty
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns decorated feature with boolean gate disabled' do
|
41
|
+
gate = json_response['gates'].find { |gate| gate['key'] == 'actors' }
|
42
|
+
expect(gate['value']).to be_empty
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'enable missing flipper_id parameter' do
|
47
|
+
before do
|
48
|
+
flipper[:my_feature].enable
|
49
|
+
post '/api/v1/features/my_feature/actors'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'returns correct error response' do
|
53
|
+
expect(last_response.status).to eq(422)
|
54
|
+
expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => '' })
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'disable missing flipper_id parameter' do
|
59
|
+
before do
|
60
|
+
flipper[:my_feature].enable
|
61
|
+
delete '/api/v1/features/my_feature/actors'
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns correct error response' do
|
65
|
+
expect(last_response.status).to eq(422)
|
66
|
+
expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => '' })
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe 'enable nil flipper_id parameter' do
|
71
|
+
before do
|
72
|
+
flipper[:my_feature].enable
|
73
|
+
post '/api/v1/features/my_feature/actors', { flipper_id: nil }
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns correct error response' do
|
77
|
+
expect(last_response.status).to eq(422)
|
78
|
+
expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => '' })
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'disable nil flipper_id parameter' do
|
83
|
+
before do
|
84
|
+
flipper[:my_feature].enable
|
85
|
+
delete '/api/v1/features/my_feature/actors', { flipper_id: nil }
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'returns correct error response' do
|
89
|
+
expect(last_response.status).to eq(422)
|
90
|
+
expect(json_response).to eq({ 'code' => 4, 'message' => 'Required parameter flipper_id is missing.', 'more_info' => '' })
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'enable missing feature' do
|
95
|
+
before do
|
96
|
+
post '/api/v1/features/my_feature/actors'
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns correct error response' do
|
100
|
+
expect(last_response.status).to eq(404)
|
101
|
+
expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => '' })
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'disable missing feature' do
|
106
|
+
before do
|
107
|
+
delete '/api/v1/features/my_feature/actors'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns correct error response' do
|
111
|
+
expect(last_response.status).to eq(404)
|
112
|
+
expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => '' })
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -6,34 +6,34 @@ RSpec.describe Flipper::Api::V1::Actions::BooleanGate do
|
|
6
6
|
describe 'enable' do
|
7
7
|
before do
|
8
8
|
flipper[:my_feature].disable
|
9
|
-
|
9
|
+
post '/api/v1/features/my_feature/boolean'
|
10
10
|
end
|
11
11
|
|
12
12
|
it 'enables feature' do
|
13
|
-
expect(last_response.status).to eq(
|
13
|
+
expect(last_response.status).to eq(200)
|
14
14
|
expect(flipper[:my_feature].on?).to be_truthy
|
15
15
|
end
|
16
|
+
|
17
|
+
it 'returns decorated feature with boolean gate enabled' do
|
18
|
+
boolean_gate = json_response['gates'].find { |gate| gate['key'] == 'boolean' }
|
19
|
+
expect(boolean_gate['value']).to be_truthy
|
20
|
+
end
|
16
21
|
end
|
17
22
|
|
18
23
|
describe 'disable' do
|
19
24
|
before do
|
20
25
|
flipper[:my_feature].enable
|
21
|
-
|
26
|
+
delete '/api/v1/features/my_feature/boolean'
|
22
27
|
end
|
23
28
|
|
24
29
|
it 'disables feature' do
|
25
|
-
expect(last_response.status).to eq(
|
30
|
+
expect(last_response.status).to eq(200)
|
26
31
|
expect(flipper[:my_feature].off?).to be_truthy
|
27
32
|
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe 'invalid paremeter' do
|
31
|
-
before do
|
32
|
-
put '/api/v1/features/my_feature/invalid_param'
|
33
|
-
end
|
34
33
|
|
35
|
-
it '
|
36
|
-
|
34
|
+
it 'returns decorated feature with boolean gate disabled' do
|
35
|
+
boolean_gate = json_response['gates'].find { |gate| gate['key'] == 'boolean' }
|
36
|
+
expect(boolean_gate['value']).to be_falsy
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -103,16 +103,36 @@ RSpec.describe Flipper::Api::V1::Actions::Feature do
|
|
103
103
|
it 'returns 404' do
|
104
104
|
expect(last_response.status).to eq(404)
|
105
105
|
end
|
106
|
+
|
107
|
+
it 'returns formatted error response body' do
|
108
|
+
expect(json_response).to eq({ "code" => 1, "message" => "Feature not found.", "more_info" => "" })
|
109
|
+
end
|
106
110
|
end
|
107
111
|
end
|
108
112
|
|
109
113
|
describe 'delete' do
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
114
|
+
context 'succesful request' do
|
115
|
+
it 'deletes feature' do
|
116
|
+
flipper[:my_feature].enable
|
117
|
+
expect(flipper.features.map(&:key)).to include('my_feature')
|
118
|
+
delete 'api/v1/features/my_feature'
|
119
|
+
expect(last_response.status).to eq(204)
|
120
|
+
expect(flipper.features.map(&:key)).not_to include('my_feature')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'feature not found' do
|
125
|
+
before do
|
126
|
+
delete 'api/v1/features/my_feature'
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'returns 404' do
|
130
|
+
expect(last_response.status).to eq(404)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'returns formatted error response body' do
|
134
|
+
expect(json_response).to eq({ "code" => 1, "message" => "Feature not found.", "more_info" => "" })
|
135
|
+
end
|
116
136
|
end
|
117
137
|
end
|
118
138
|
end
|
@@ -17,22 +17,23 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
|
|
17
17
|
expected_response = {
|
18
18
|
"features" => [
|
19
19
|
{
|
20
|
-
"key" =>"my_feature",
|
20
|
+
"key" => "my_feature",
|
21
21
|
"state" => "on",
|
22
22
|
"gates" => [
|
23
23
|
{
|
24
24
|
"key"=> "boolean",
|
25
25
|
"name"=> "boolean",
|
26
|
-
"value" => true
|
27
|
-
|
28
|
-
|
26
|
+
"value" => true
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"key" => "groups",
|
29
30
|
"name" => "group",
|
30
|
-
"value" =>[],
|
31
|
+
"value" => [],
|
31
32
|
},
|
32
33
|
{
|
33
34
|
"key" => "actors",
|
34
|
-
"name"=>"actor",
|
35
|
-
"value"=>["10"],
|
35
|
+
"name" => "actor",
|
36
|
+
"value" => ["10"],
|
36
37
|
},
|
37
38
|
{
|
38
39
|
"key" => "percentage_of_actors",
|
@@ -44,7 +45,7 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
|
|
44
45
|
"name"=> "percentage_of_time",
|
45
46
|
"value"=> 0,
|
46
47
|
},
|
47
|
-
|
48
|
+
],
|
48
49
|
},
|
49
50
|
]
|
50
51
|
}
|
@@ -74,9 +75,44 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
|
|
74
75
|
post 'api/v1/features', { name: 'my_feature' }
|
75
76
|
end
|
76
77
|
|
77
|
-
it 'responds 200
|
78
|
+
it 'responds 200 ' do
|
78
79
|
expect(last_response.status).to eq(200)
|
79
|
-
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns decorated feature' do
|
83
|
+
expected_response = {
|
84
|
+
|
85
|
+
"key" => "my_feature",
|
86
|
+
"state" => "off",
|
87
|
+
"gates" => [
|
88
|
+
{
|
89
|
+
"key"=> "boolean",
|
90
|
+
"name"=> "boolean",
|
91
|
+
"value" => false,
|
92
|
+
},
|
93
|
+
{
|
94
|
+
"key" => "groups",
|
95
|
+
"name" => "group",
|
96
|
+
"value" => [],
|
97
|
+
},
|
98
|
+
{
|
99
|
+
"key" => "actors",
|
100
|
+
"name" => "actor",
|
101
|
+
"value" => [],
|
102
|
+
},
|
103
|
+
{
|
104
|
+
"key" => "percentage_of_actors",
|
105
|
+
"name" => "percentage_of_actors",
|
106
|
+
"value" => 0,
|
107
|
+
},
|
108
|
+
{
|
109
|
+
"key"=> "percentage_of_time",
|
110
|
+
"name"=> "percentage_of_time",
|
111
|
+
"value"=> 0,
|
112
|
+
},
|
113
|
+
],
|
114
|
+
}
|
115
|
+
expect(json_response).to eq(expected_response)
|
80
116
|
end
|
81
117
|
|
82
118
|
it 'adds feature' do
|
@@ -98,8 +134,7 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
|
|
98
134
|
end
|
99
135
|
|
100
136
|
it 'returns formatted error' do
|
101
|
-
|
102
|
-
expect(errors.first['message']).to eq('Missing post parameter: name')
|
137
|
+
expect(json_response).to eq({ 'code' => 5, 'message' => 'Required parameter name is missing.', 'more_info' => '' })
|
103
138
|
end
|
104
139
|
end
|
105
140
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
|
4
|
+
let(:app) { build_api(flipper) }
|
5
|
+
|
6
|
+
describe 'enable' do
|
7
|
+
before do
|
8
|
+
flipper[:my_feature].disable
|
9
|
+
Flipper.register(:admins) do |actor|
|
10
|
+
actor.respond_to?(:admin?) && actor.admin?
|
11
|
+
end
|
12
|
+
post '/api/v1/features/my_feature/groups', { name: 'admins' }
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'enables feature for group' do
|
16
|
+
person = double
|
17
|
+
allow(person).to receive(:flipper_id).and_return(1)
|
18
|
+
allow(person).to receive(:admin?).and_return(true)
|
19
|
+
expect(last_response.status).to eq(200)
|
20
|
+
expect(flipper[:my_feature].enabled?(person)).to be_truthy
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns decorated feature with group enabled' do
|
24
|
+
group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
|
25
|
+
expect(group_gate['value']).to eq(['admins'])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'disable' do
|
30
|
+
before do
|
31
|
+
flipper[:my_feature].disable
|
32
|
+
Flipper.register(:admins) do |actor|
|
33
|
+
actor.respond_to?(:admin?) && actor.admin?
|
34
|
+
end
|
35
|
+
flipper[:my_feature].enable_group(:admins)
|
36
|
+
delete '/api/v1/features/my_feature/groups', { name: 'admins' }
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'disables feature for group' do
|
40
|
+
person = double
|
41
|
+
allow(person).to receive(:flipper_id).and_return(1)
|
42
|
+
allow(person).to receive(:admin?).and_return(true)
|
43
|
+
expect(last_response.status).to eq(200)
|
44
|
+
expect(flipper[:my_feature].enabled?(person)).to be_falsey
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns decorated feature with group disabled' do
|
48
|
+
group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
|
49
|
+
expect(group_gate['value']).to eq([])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'non-existent feature' do
|
54
|
+
before do
|
55
|
+
delete '/api/v1/features/my_feature/groups', { name: 'admins' }
|
56
|
+
end
|
57
|
+
|
58
|
+
it '404s with correct error response when feature does not exist' do
|
59
|
+
expect(last_response.status).to eq(404)
|
60
|
+
expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => '' })
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'group not registered' do
|
65
|
+
before do
|
66
|
+
flipper[:my_feature].disable
|
67
|
+
delete '/api/v1/features/my_feature/groups', { name: 'admins' }
|
68
|
+
end
|
69
|
+
|
70
|
+
it '404s with correct error response when group not registered' do
|
71
|
+
expect(last_response.status).to eq(404)
|
72
|
+
expect(json_response).to eq({ 'code' => 2, 'message' => 'Group not registered.', 'more_info' => '' })
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
|
4
|
+
let(:app) { build_api(flipper) }
|
5
|
+
|
6
|
+
describe 'enable' do
|
7
|
+
before do
|
8
|
+
flipper[:my_feature].disable
|
9
|
+
post '/api/v1/features/my_feature/percentage_of_actors', { percentage: '10' }
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'enables gate for feature' do
|
13
|
+
expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_actors)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns decorated feature with gate enabled for 10 percent of actors' do
|
17
|
+
gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
|
18
|
+
expect(gate['value']).to eq(10)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'disable' do
|
24
|
+
before do
|
25
|
+
flipper[:my_feature].enable_percentage_of_actors(10)
|
26
|
+
delete '/api/v1/features/my_feature/percentage_of_actors'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'disables gate for feature' do
|
30
|
+
expect(flipper[:my_feature].enabled_gates).to be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns decorated feature with gate disabled' do
|
34
|
+
gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
|
35
|
+
expect(gate['value']).to eq(0)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'non-existent feature' do
|
40
|
+
before do
|
41
|
+
delete '/api/v1/features/my_feature/percentage_of_actors'
|
42
|
+
end
|
43
|
+
|
44
|
+
it '404s with correct error response when feature does not exist' do
|
45
|
+
expect(last_response.status).to eq(404)
|
46
|
+
expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => '' })
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'out of range parameter percentage parameter' do
|
51
|
+
before do
|
52
|
+
flipper[:my_feature].disable
|
53
|
+
post '/api/v1/features/my_feature/percentage_of_actors', { percentage: '300' }
|
54
|
+
end
|
55
|
+
|
56
|
+
it '422s with correct error response when percentage parameter is invalid' do
|
57
|
+
expect(last_response.status).to eq(422)
|
58
|
+
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'percentage parameter not an integer' do
|
63
|
+
before do
|
64
|
+
flipper[:my_feature].disable
|
65
|
+
post '/api/v1/features/my_feature/percentage_of_actors', { percentage: 'foo' }
|
66
|
+
end
|
67
|
+
|
68
|
+
it '422s with correct error response when percentage parameter is invalid' do
|
69
|
+
expect(last_response.status).to eq(422)
|
70
|
+
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'missing percentage parameter' do
|
75
|
+
before do
|
76
|
+
flipper[:my_feature].disable
|
77
|
+
post '/api/v1/features/my_feature/percentage_of_actors'
|
78
|
+
end
|
79
|
+
|
80
|
+
it '422s with correct error response when percentage parameter is missing' do
|
81
|
+
expect(last_response.status).to eq(422)
|
82
|
+
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
|
4
|
+
let(:app) { build_api(flipper) }
|
5
|
+
|
6
|
+
describe 'enable' do
|
7
|
+
before do
|
8
|
+
flipper[:my_feature].disable
|
9
|
+
post '/api/v1/features/my_feature/percentage_of_time', { percentage: '10' }
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'enables gate for feature' do
|
13
|
+
expect(flipper[:my_feature].enabled_gate_names).to include(:percentage_of_time)
|
14
|
+
end
|
15
|
+
|
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
|
21
|
+
|
22
|
+
describe 'disable' do
|
23
|
+
before do
|
24
|
+
flipper[:my_feature].enable_percentage_of_time(10)
|
25
|
+
delete '/api/v1/features/my_feature/percentage_of_time'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'disables gate for feature' do
|
29
|
+
expect(flipper[:my_feature].enabled_gates).to be_empty
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns decorated feature with gate disabled' do
|
33
|
+
gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
|
34
|
+
expect(gate['value']).to eq(0)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe 'non-existent feature' do
|
39
|
+
before do
|
40
|
+
delete '/api/v1/features/my_feature/percentage_of_time'
|
41
|
+
end
|
42
|
+
|
43
|
+
it '404s with correct error response when feature does not exist' do
|
44
|
+
expect(last_response.status).to eq(404)
|
45
|
+
expect(json_response).to eq({ 'code' => 1, 'message' => 'Feature not found.', 'more_info' => '' })
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'out of range parameter percentage parameter' do
|
50
|
+
before do
|
51
|
+
flipper[:my_feature].disable
|
52
|
+
post '/api/v1/features/my_feature/percentage_of_time', { percentage: '300' }
|
53
|
+
end
|
54
|
+
|
55
|
+
it '422s with correct error response when percentage parameter is invalid' do
|
56
|
+
expect(last_response.status).to eq(422)
|
57
|
+
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'percentage parameter not an integer' do
|
62
|
+
before do
|
63
|
+
flipper[:my_feature].disable
|
64
|
+
post '/api/v1/features/my_feature/percentage_of_time', { percentage: 'foo' }
|
65
|
+
end
|
66
|
+
|
67
|
+
it '422s with correct error response when percentage parameter is invalid' do
|
68
|
+
expect(last_response.status).to eq(422)
|
69
|
+
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'missing percentage parameter' do
|
74
|
+
before do
|
75
|
+
flipper[:my_feature].disable
|
76
|
+
post '/api/v1/features/my_feature/percentage_of_time'
|
77
|
+
end
|
78
|
+
|
79
|
+
it '422s with correct error response when percentage parameter is missing' do
|
80
|
+
expect(last_response.status).to eq(422)
|
81
|
+
expect(json_response).to eq({ 'code' => 3, 'message' => 'Percentage must be a positive number less than or equal to 100.', 'more_info' => '' })
|
82
|
+
end
|
83
|
+
end
|
84
|
+
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.
|
4
|
+
version: 0.10.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: 2016-
|
11
|
+
date: 2016-11-17 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.
|
39
|
+
version: 0.10.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.
|
46
|
+
version: 0.10.0
|
47
47
|
description: Rack middleware that provides an API for the flipper gem.
|
48
48
|
email:
|
49
49
|
- nunemaker@gmail.com
|
@@ -56,18 +56,28 @@ files:
|
|
56
56
|
- lib/flipper/api.rb
|
57
57
|
- lib/flipper/api/action.rb
|
58
58
|
- lib/flipper/api/action_collection.rb
|
59
|
+
- lib/flipper/api/actor.rb
|
59
60
|
- lib/flipper/api/error.rb
|
61
|
+
- lib/flipper/api/error_response.rb
|
60
62
|
- lib/flipper/api/middleware.rb
|
63
|
+
- lib/flipper/api/v1/actions/actors_gate.rb
|
61
64
|
- lib/flipper/api/v1/actions/boolean_gate.rb
|
62
65
|
- lib/flipper/api/v1/actions/feature.rb
|
63
66
|
- lib/flipper/api/v1/actions/features.rb
|
67
|
+
- lib/flipper/api/v1/actions/groups_gate.rb
|
68
|
+
- lib/flipper/api/v1/actions/percentage_of_actors_gate.rb
|
69
|
+
- lib/flipper/api/v1/actions/percentage_of_time_gate.rb
|
64
70
|
- lib/flipper/api/v1/decorators/feature.rb
|
65
71
|
- lib/flipper/api/v1/decorators/gate.rb
|
66
72
|
- lib/flipper/version.rb
|
67
73
|
- spec/flipper/api/action_spec.rb
|
74
|
+
- spec/flipper/api/v1/actions/actors_gate_spec.rb
|
68
75
|
- spec/flipper/api/v1/actions/boolean_gate_spec.rb
|
69
76
|
- spec/flipper/api/v1/actions/feature_spec.rb
|
70
77
|
- spec/flipper/api/v1/actions/features_spec.rb
|
78
|
+
- spec/flipper/api/v1/actions/groups_gate_spec.rb
|
79
|
+
- spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb
|
80
|
+
- spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb
|
71
81
|
homepage: https://github.com/jnunemaker/flipper
|
72
82
|
licenses:
|
73
83
|
- MIT
|
@@ -94,6 +104,10 @@ specification_version: 4
|
|
94
104
|
summary: API for the Flipper gem
|
95
105
|
test_files:
|
96
106
|
- spec/flipper/api/action_spec.rb
|
107
|
+
- spec/flipper/api/v1/actions/actors_gate_spec.rb
|
97
108
|
- spec/flipper/api/v1/actions/boolean_gate_spec.rb
|
98
109
|
- spec/flipper/api/v1/actions/feature_spec.rb
|
99
110
|
- spec/flipper/api/v1/actions/features_spec.rb
|
111
|
+
- spec/flipper/api/v1/actions/groups_gate_spec.rb
|
112
|
+
- spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb
|
113
|
+
- spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb
|