flipper-api 0.10.2 → 0.11.0.beta1
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-api.gemspec +12 -12
- data/lib/flipper/api/action.rb +12 -10
- data/lib/flipper/api/action_collection.rb +2 -2
- data/lib/flipper/api/error_response.rb +9 -8
- data/lib/flipper/api/json_params.rb +45 -0
- data/lib/flipper/api/middleware.rb +3 -28
- data/lib/flipper/api/v1/actions/actors_gate.rb +1 -9
- data/lib/flipper/api/v1/actions/boolean_gate.rb +1 -1
- data/lib/flipper/api/v1/actions/clear_feature.rb +21 -0
- data/lib/flipper/api/v1/actions/feature.rb +8 -16
- data/lib/flipper/api/v1/actions/features.rb +26 -8
- data/lib/flipper/api/v1/actions/groups_gate.rb +18 -7
- data/lib/flipper/api/v1/actions/percentage_of_actors_gate.rb +6 -24
- data/lib/flipper/api/v1/actions/percentage_of_time_gate.rb +6 -23
- data/lib/flipper/api/v1/decorators/feature.rb +3 -4
- data/lib/flipper/api.rb +11 -27
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/api/action_spec.rb +30 -36
- data/spec/flipper/api/json_params_spec.rb +81 -0
- data/spec/flipper/api/v1/actions/actors_gate_spec.rb +33 -22
- data/spec/flipper/api/v1/actions/boolean_gate_spec.rb +2 -2
- data/spec/flipper/api/v1/actions/clear_feature_spec.rb +27 -0
- data/spec/flipper/api/v1/actions/feature_spec.rb +28 -30
- data/spec/flipper/api/v1/actions/features_spec.rb +79 -44
- data/spec/flipper/api/v1/actions/groups_gate_spec.rb +92 -10
- data/spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb +62 -24
- data/spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb +34 -15
- data/spec/flipper/api_spec.rb +55 -0
- metadata +15 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c14f137259ca4f58e82fa3ed35b32dd1f0375cb3
|
4
|
+
data.tar.gz: 6ed6d5173f602c198cc7b41e7cc2ac72c9fac096
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffaf1fefc28654784568f600a17f0506a4d0833e4de153aedf89ced7b06613d6a9b7468d400afbdeabf762fd26ca50128140301abeb0e06df1c25c5ed3ef1b0e
|
7
|
+
data.tar.gz: 419c2e26875d8973e7c37d8e1258f1fed26809b1d8798cb692d468f79b5de51599ef57205b4309401198e8bd211d891ddaeb6ed244ecdeae6997883c1d4880e0
|
data/flipper-api.gemspec
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require File.expand_path('../lib/flipper/version', __FILE__)
|
3
3
|
|
4
|
-
flipper_api_files = lambda
|
5
|
-
file =~
|
6
|
-
|
4
|
+
flipper_api_files = lambda do |file|
|
5
|
+
file =~ %r{(flipper)[\/-]api}
|
6
|
+
end
|
7
7
|
|
8
8
|
Gem::Specification.new do |gem|
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.summary =
|
12
|
-
gem.description =
|
13
|
-
gem.license =
|
14
|
-
gem.homepage =
|
15
|
-
gem.files = `git ls-files`.split("\n").select(&flipper_api_files) + [
|
9
|
+
gem.authors = ['John Nunemaker']
|
10
|
+
gem.email = ['nunemaker@gmail.com']
|
11
|
+
gem.summary = 'API for the Flipper gem'
|
12
|
+
gem.description = 'Rack middleware that provides an API for the flipper gem.'
|
13
|
+
gem.license = 'MIT'
|
14
|
+
gem.homepage = 'https://github.com/jnunemaker/flipper'
|
15
|
+
gem.files = `git ls-files`.split("\n").select(&flipper_api_files) + ['lib/flipper/version.rb']
|
16
16
|
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_api_files)
|
17
|
-
gem.name =
|
18
|
-
gem.require_paths = [
|
17
|
+
gem.name = 'flipper-api'
|
18
|
+
gem.require_paths = ['lib']
|
19
19
|
gem.version = Flipper::VERSION
|
20
20
|
|
21
21
|
gem.add_dependency 'rack', '>= 1.4', '< 3'
|
data/lib/flipper/api/action.rb
CHANGED
@@ -9,11 +9,11 @@ module Flipper
|
|
9
9
|
extend Forwardable
|
10
10
|
|
11
11
|
VALID_REQUEST_METHOD_NAMES = Set.new([
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
'get'.freeze,
|
13
|
+
'post'.freeze,
|
14
|
+
'put'.freeze,
|
15
|
+
'delete'.freeze,
|
16
|
+
]).freeze
|
17
17
|
|
18
18
|
# Public: Call this in subclasses so the action knows its route.
|
19
19
|
#
|
@@ -50,9 +50,10 @@ module Flipper
|
|
50
50
|
def_delegator :@request, :params
|
51
51
|
|
52
52
|
def initialize(flipper, request)
|
53
|
-
@flipper
|
53
|
+
@flipper = flipper
|
54
|
+
@request = request
|
54
55
|
@code = 200
|
55
|
-
@headers = {
|
56
|
+
@headers = { 'Content-Type' => Api::CONTENT_TYPE }
|
56
57
|
end
|
57
58
|
|
58
59
|
# Public: Runs the request method for the provided request.
|
@@ -62,7 +63,8 @@ module Flipper
|
|
62
63
|
if valid_request_method? && respond_to?(request_method_name)
|
63
64
|
catch(:halt) { send(request_method_name) }
|
64
65
|
else
|
65
|
-
raise Api::RequestMethodNotSupported,
|
66
|
+
raise Api::RequestMethodNotSupported,
|
67
|
+
"#{self.class} does not support request method #{request_method_name.inspect}"
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
@@ -135,9 +137,9 @@ module Flipper
|
|
135
137
|
end
|
136
138
|
|
137
139
|
# Private: split request path by "/"
|
138
|
-
# Example: "
|
140
|
+
# Example: "features/feature_name" => ['features', 'feature_name']
|
139
141
|
def path_parts
|
140
|
-
@request.path.split(
|
142
|
+
@request.path.split('/')
|
141
143
|
end
|
142
144
|
|
143
145
|
def valid_request_method?
|
@@ -7,7 +7,8 @@ module Flipper
|
|
7
7
|
def initialize(code, message, http_status)
|
8
8
|
@code = code
|
9
9
|
@message = message
|
10
|
-
@more_info =
|
10
|
+
@more_info =
|
11
|
+
'https://github.com/jnunemaker/flipper/tree/master/docs/api#error-code-reference'
|
11
12
|
@http_status = http_status
|
12
13
|
end
|
13
14
|
|
@@ -21,13 +22,13 @@ module Flipper
|
|
21
22
|
end
|
22
23
|
|
23
24
|
ERRORS = {
|
24
|
-
feature_not_found: Error.new(1,
|
25
|
-
group_not_registered: Error.new(2,
|
26
|
-
percentage_invalid:
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
feature_not_found: Error.new(1, 'Feature not found.', 404),
|
26
|
+
group_not_registered: Error.new(2, 'Group not registered.', 404),
|
27
|
+
percentage_invalid:
|
28
|
+
Error.new(3, 'Percentage must be a positive number less than or equal to 100.', 422),
|
29
|
+
flipper_id_invalid: Error.new(4, 'Required parameter flipper_id is missing.', 422),
|
30
|
+
name_invalid: Error.new(5, 'Required parameter name is missing.', 422),
|
31
|
+
}.freeze
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Flipper
|
4
|
+
module Api
|
5
|
+
class JsonParams
|
6
|
+
include Rack::Utils
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
13
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
14
|
+
REQUEST_BODY = 'rack.input'.freeze
|
15
|
+
|
16
|
+
# Public: Merge request body params with query string params
|
17
|
+
# This way can access all params with Rack::Request#params
|
18
|
+
# Rack does not add application/json params to Rack::Request#params
|
19
|
+
# Allows app to handle x-www-url-form-encoded / application/json request
|
20
|
+
# parameters the same way
|
21
|
+
def call(env)
|
22
|
+
if env[CONTENT_TYPE] == 'application/json'
|
23
|
+
body = env[REQUEST_BODY].read
|
24
|
+
env[REQUEST_BODY].rewind
|
25
|
+
update_params(env, body)
|
26
|
+
end
|
27
|
+
@app.call(env)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Rails 3.2.2.1 Rack version does not have Rack::Request#update_param
|
33
|
+
# Rack 1.5.0 adds update_param
|
34
|
+
# This method accomplishes similar functionality
|
35
|
+
def update_params(env, data)
|
36
|
+
return if data.empty?
|
37
|
+
parsed_request_body = JSON.parse(data)
|
38
|
+
parsed_query_string = parse_query(env[QUERY_STRING])
|
39
|
+
parsed_query_string.merge!(parsed_request_body)
|
40
|
+
parameters = build_query(parsed_query_string)
|
41
|
+
env[QUERY_STRING] = parameters
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -9,45 +9,20 @@ end
|
|
9
9
|
module Flipper
|
10
10
|
module Api
|
11
11
|
class Middleware
|
12
|
-
|
13
|
-
#
|
14
|
-
# app - The app this middleware is included in.
|
15
|
-
# flipper_or_block - The Flipper::DSL instance or a block that yields a
|
16
|
-
# Flipper::DSL instance to use for all operations.
|
17
|
-
#
|
18
|
-
# Examples
|
19
|
-
#
|
20
|
-
# flipper = Flipper.new(...)
|
21
|
-
#
|
22
|
-
# # using with a normal flipper instance
|
23
|
-
# use Flipper::Api::Middleware, flipper
|
24
|
-
#
|
25
|
-
# # using with a block that yields a flipper instance
|
26
|
-
# use Flipper::Api::Middleware, lambda { Flipper.new(...) }
|
27
|
-
#
|
28
|
-
def initialize(app, flipper_or_block)
|
12
|
+
def initialize(app)
|
29
13
|
@app = app
|
30
14
|
|
31
|
-
if flipper_or_block.respond_to?(:call)
|
32
|
-
@flipper_block = flipper_or_block
|
33
|
-
else
|
34
|
-
@flipper = flipper_or_block
|
35
|
-
end
|
36
|
-
|
37
15
|
@action_collection = ActionCollection.new
|
38
16
|
@action_collection.add Api::V1::Actions::PercentageOfTimeGate
|
39
17
|
@action_collection.add Api::V1::Actions::PercentageOfActorsGate
|
40
18
|
@action_collection.add Api::V1::Actions::ActorsGate
|
41
19
|
@action_collection.add Api::V1::Actions::GroupsGate
|
42
20
|
@action_collection.add Api::V1::Actions::BooleanGate
|
21
|
+
@action_collection.add Api::V1::Actions::ClearFeature
|
43
22
|
@action_collection.add Api::V1::Actions::Feature
|
44
23
|
@action_collection.add Api::V1::Actions::Features
|
45
24
|
end
|
46
25
|
|
47
|
-
def flipper
|
48
|
-
@flipper ||= @flipper_block.call
|
49
|
-
end
|
50
|
-
|
51
26
|
def call(env)
|
52
27
|
dup.call!(env)
|
53
28
|
end
|
@@ -56,9 +31,9 @@ module Flipper
|
|
56
31
|
request = Rack::Request.new(env)
|
57
32
|
action_class = @action_collection.action_for_request(request)
|
58
33
|
if action_class.nil?
|
59
|
-
@app.status = 404
|
60
34
|
@app.call(env)
|
61
35
|
else
|
36
|
+
flipper = env.fetch('flipper')
|
62
37
|
action_class.run(flipper, request)
|
63
38
|
end
|
64
39
|
end
|
@@ -6,7 +6,7 @@ module Flipper
|
|
6
6
|
module V1
|
7
7
|
module Actions
|
8
8
|
class ActorsGate < Api::Action
|
9
|
-
route %r{
|
9
|
+
route %r{features/[^/]*/actors/?\Z}
|
10
10
|
|
11
11
|
def post
|
12
12
|
ensure_valid_params
|
@@ -29,10 +29,6 @@ module Flipper
|
|
29
29
|
private
|
30
30
|
|
31
31
|
def ensure_valid_params
|
32
|
-
unless feature_names.include?(feature_name)
|
33
|
-
json_error_response(:feature_not_found)
|
34
|
-
end
|
35
|
-
|
36
32
|
json_error_response(:flipper_id_invalid) if flipper_id.nil?
|
37
33
|
end
|
38
34
|
|
@@ -43,10 +39,6 @@ module Flipper
|
|
43
39
|
def flipper_id
|
44
40
|
@flipper_id ||= params['flipper_id']
|
45
41
|
end
|
46
|
-
|
47
|
-
def feature_names
|
48
|
-
@feature_names ||= flipper.adapter.features
|
49
|
-
end
|
50
42
|
end
|
51
43
|
end
|
52
44
|
end
|
@@ -0,0 +1,21 @@
|
|
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 ClearFeature < Api::Action
|
9
|
+
route %r{features/[^/]*/clear/?\Z}
|
10
|
+
|
11
|
+
def delete
|
12
|
+
feature_name = Rack::Utils.unescape(path_parts[-2])
|
13
|
+
feature = flipper[feature_name]
|
14
|
+
flipper.adapter.clear(feature)
|
15
|
+
json_response({}, 204)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -6,25 +6,17 @@ module Flipper
|
|
6
6
|
module V1
|
7
7
|
module Actions
|
8
8
|
class Feature < Api::Action
|
9
|
-
|
10
|
-
route %r{api/v1/features/[^/]*/?\Z}
|
9
|
+
route %r{features/[^/]*/?\Z}
|
11
10
|
|
12
11
|
def get
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
json_error_response(:feature_not_found)
|
18
|
-
end
|
12
|
+
return json_error_response(:feature_not_found) unless feature_exists?(feature_name)
|
13
|
+
feature = Decorators::Feature.new(flipper[feature_name])
|
14
|
+
json_response(feature.as_json)
|
19
15
|
end
|
20
16
|
|
21
17
|
def delete
|
22
|
-
|
23
|
-
|
24
|
-
json_response({}, 204)
|
25
|
-
else
|
26
|
-
json_error_response(:feature_not_found)
|
27
|
-
end
|
18
|
+
flipper.remove(feature_name)
|
19
|
+
json_response({}, 204)
|
28
20
|
end
|
29
21
|
|
30
22
|
private
|
@@ -33,8 +25,8 @@ module Flipper
|
|
33
25
|
@feature_name ||= Rack::Utils.unescape(path_parts.last)
|
34
26
|
end
|
35
27
|
|
36
|
-
def
|
37
|
-
|
28
|
+
def feature_exists?(feature_name)
|
29
|
+
flipper.features.map(&:key).include?(feature_name)
|
38
30
|
end
|
39
31
|
end
|
40
32
|
end
|
@@ -1,23 +1,35 @@
|
|
1
1
|
require 'flipper/api/action'
|
2
2
|
require 'flipper/api/v1/decorators/feature'
|
3
|
-
require 'json'
|
4
3
|
|
5
4
|
module Flipper
|
6
5
|
module Api
|
7
6
|
module V1
|
8
7
|
module Actions
|
9
8
|
class Features < Api::Action
|
10
|
-
|
11
|
-
route %r{api/v1/features\Z}
|
9
|
+
route(/features\Z/)
|
12
10
|
|
13
11
|
def get
|
14
|
-
|
12
|
+
keys = params['keys']
|
13
|
+
features = if keys
|
14
|
+
names = keys.split(',')
|
15
|
+
if names.empty?
|
16
|
+
[]
|
17
|
+
else
|
18
|
+
existing_feature_names = names.keep_if do |feature_name|
|
19
|
+
feature_exists?(feature_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
flipper.preload(existing_feature_names)
|
23
|
+
end
|
24
|
+
else
|
25
|
+
flipper.features
|
26
|
+
end
|
27
|
+
|
28
|
+
decorated_features = features.map do |feature|
|
15
29
|
Decorators::Feature.new(feature).as_json
|
16
|
-
|
30
|
+
end
|
17
31
|
|
18
|
-
json_response(
|
19
|
-
features: features
|
20
|
-
})
|
32
|
+
json_response(features: decorated_features)
|
21
33
|
end
|
22
34
|
|
23
35
|
def post
|
@@ -27,6 +39,12 @@ module Flipper
|
|
27
39
|
decorated_feature = Decorators::Feature.new(feature)
|
28
40
|
json_response(decorated_feature.as_json, 200)
|
29
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def feature_exists?(feature_name)
|
46
|
+
flipper.features.map(&:key).include?(feature_name)
|
47
|
+
end
|
30
48
|
end
|
31
49
|
end
|
32
50
|
end
|
@@ -6,7 +6,7 @@ module Flipper
|
|
6
6
|
module V1
|
7
7
|
module Actions
|
8
8
|
class GroupsGate < Api::Action
|
9
|
-
route %r{
|
9
|
+
route %r{features/[^/]*/groups/?\Z}
|
10
10
|
|
11
11
|
def post
|
12
12
|
ensure_valid_params
|
@@ -27,8 +27,23 @@ module Flipper
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def ensure_valid_params
|
30
|
-
|
31
|
-
|
30
|
+
if group_name.nil? || group_name.empty?
|
31
|
+
json_error_response(:name_invalid)
|
32
|
+
end
|
33
|
+
|
34
|
+
return if allow_unregistered_groups?
|
35
|
+
return if Flipper.group_exists?(group_name)
|
36
|
+
|
37
|
+
json_error_response(:group_not_registered)
|
38
|
+
end
|
39
|
+
|
40
|
+
def allow_unregistered_groups?
|
41
|
+
allow_unregistered_groups = params['allow_unregistered_groups']
|
42
|
+
allow_unregistered_groups && allow_unregistered_groups == 'true'
|
43
|
+
end
|
44
|
+
|
45
|
+
def disallow_unregistered_groups?
|
46
|
+
!allow_unregistered_groups?
|
32
47
|
end
|
33
48
|
|
34
49
|
def feature_name
|
@@ -38,10 +53,6 @@ module Flipper
|
|
38
53
|
def group_name
|
39
54
|
@group_name ||= params['name']
|
40
55
|
end
|
41
|
-
|
42
|
-
def feature_names
|
43
|
-
@feature_names ||= flipper.adapter.features
|
44
|
-
end
|
45
56
|
end
|
46
57
|
end
|
47
58
|
end
|
@@ -6,10 +6,13 @@ module Flipper
|
|
6
6
|
module V1
|
7
7
|
module Actions
|
8
8
|
class PercentageOfActorsGate < Api::Action
|
9
|
-
route %r{
|
9
|
+
route %r{features/[^/]*/percentage_of_actors/?\Z}
|
10
10
|
|
11
11
|
def post
|
12
|
-
|
12
|
+
if percentage < 0 || percentage > 100
|
13
|
+
json_error_response(:percentage_invalid)
|
14
|
+
end
|
15
|
+
|
13
16
|
feature = flipper[feature_name]
|
14
17
|
feature.enable_percentage_of_actors(percentage)
|
15
18
|
decorated_feature = Decorators::Feature.new(feature)
|
@@ -17,7 +20,6 @@ module Flipper
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def delete
|
20
|
-
ensure_valid_disable_params
|
21
23
|
feature = flipper[feature_name]
|
22
24
|
feature.disable_percentage_of_actors
|
23
25
|
decorated_feature = Decorators::Feature.new(feature)
|
@@ -26,22 +28,6 @@ module Flipper
|
|
26
28
|
|
27
29
|
private
|
28
30
|
|
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
31
|
def feature_name
|
46
32
|
@feature_name ||= Rack::Utils.unescape(path_parts[-2])
|
47
33
|
end
|
@@ -53,12 +39,8 @@ module Flipper
|
|
53
39
|
-1
|
54
40
|
end
|
55
41
|
end
|
56
|
-
|
57
|
-
def feature_names
|
58
|
-
@feature_names ||= flipper.adapter.features
|
59
|
-
end
|
60
|
-
end
|
61
42
|
end
|
62
43
|
end
|
63
44
|
end
|
64
45
|
end
|
46
|
+
end
|
@@ -6,10 +6,13 @@ module Flipper
|
|
6
6
|
module V1
|
7
7
|
module Actions
|
8
8
|
class PercentageOfTimeGate < Api::Action
|
9
|
-
route %r{
|
9
|
+
route %r{features/[^/]*/percentage_of_time/?\Z}
|
10
10
|
|
11
11
|
def post
|
12
|
-
|
12
|
+
if percentage < 0 || percentage > 100
|
13
|
+
json_error_response(:percentage_invalid)
|
14
|
+
end
|
15
|
+
|
13
16
|
feature = flipper[feature_name]
|
14
17
|
feature.enable_percentage_of_time(percentage)
|
15
18
|
decorated_feature = Decorators::Feature.new(feature)
|
@@ -17,31 +20,15 @@ module Flipper
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def delete
|
20
|
-
ensure_valid_disable_params
|
21
23
|
feature = flipper[feature_name]
|
22
24
|
feature.disable_percentage_of_time
|
25
|
+
|
23
26
|
decorated_feature = Decorators::Feature.new(feature)
|
24
27
|
json_response(decorated_feature.as_json, 200)
|
25
28
|
end
|
26
29
|
|
27
30
|
private
|
28
31
|
|
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
32
|
def feature_name
|
46
33
|
@feature_name ||= Rack::Utils.unescape(path_parts[-2])
|
47
34
|
end
|
@@ -53,10 +40,6 @@ module Flipper
|
|
53
40
|
-1
|
54
41
|
end
|
55
42
|
end
|
56
|
-
|
57
|
-
def feature_names
|
58
|
-
@feature_names ||= flipper.adapter.features
|
59
|
-
end
|
60
43
|
end
|
61
44
|
end
|
62
45
|
end
|
@@ -6,19 +6,18 @@ module Flipper
|
|
6
6
|
module V1
|
7
7
|
module Decorators
|
8
8
|
class Feature < SimpleDelegator
|
9
|
-
|
10
9
|
# Public: The feature being decorated.
|
11
10
|
alias_method :feature, :__getobj__
|
12
11
|
|
13
12
|
# Public: Returns instance as hash that is ready to be json dumped.
|
14
13
|
def as_json
|
15
|
-
gate_values = feature.
|
14
|
+
gate_values = feature.adapter.get(self)
|
16
15
|
{
|
17
16
|
'key' => key,
|
18
17
|
'state' => state.to_s,
|
19
|
-
'gates' => gates.map
|
18
|
+
'gates' => gates.map do |gate|
|
20
19
|
Decorators::Gate.new(gate, gate_values[gate.key]).as_json
|
21
|
-
|
20
|
+
end,
|
22
21
|
}
|
23
22
|
end
|
24
23
|
end
|
data/lib/flipper/api.rb
CHANGED
@@ -1,43 +1,27 @@
|
|
1
1
|
require 'rack'
|
2
2
|
require 'flipper'
|
3
|
+
require 'flipper/middleware/setup_env'
|
4
|
+
require 'flipper/middleware/memoizer'
|
3
5
|
require 'flipper/api/middleware'
|
6
|
+
require 'flipper/api/json_params'
|
4
7
|
require 'flipper/api/actor'
|
5
8
|
|
6
9
|
module Flipper
|
7
10
|
module Api
|
8
11
|
CONTENT_TYPE = 'application/json'.freeze
|
9
12
|
|
10
|
-
def self.app(flipper)
|
11
|
-
app =
|
13
|
+
def self.app(flipper = nil)
|
14
|
+
app = ->(_) { [404, { 'Content-Type'.freeze => CONTENT_TYPE }, ['{}'.freeze]] }
|
12
15
|
builder = Rack::Builder.new
|
13
16
|
yield builder if block_given?
|
14
|
-
builder.use Flipper::
|
17
|
+
builder.use Flipper::Middleware::SetupEnv, flipper
|
18
|
+
builder.use Flipper::Middleware::Memoizer
|
19
|
+
builder.use Flipper::Api::JsonParams
|
20
|
+
builder.use Flipper::Api::Middleware
|
15
21
|
builder.run app
|
22
|
+
klass = self
|
23
|
+
builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
|
16
24
|
builder
|
17
25
|
end
|
18
|
-
|
19
|
-
class App
|
20
|
-
# Public: HTTP response code
|
21
|
-
# Use this method to update status code before responding
|
22
|
-
attr_writer :status
|
23
|
-
|
24
|
-
def initialize(status, headers, body)
|
25
|
-
@status = status
|
26
|
-
@headers = headers
|
27
|
-
@body = body
|
28
|
-
end
|
29
|
-
|
30
|
-
# Public : Rack expects object that responds to call
|
31
|
-
# env - environment hash
|
32
|
-
def call(env)
|
33
|
-
response
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def response
|
39
|
-
[@status, @headers, @body]
|
40
|
-
end
|
41
|
-
end
|
42
26
|
end
|
43
27
|
end
|
data/lib/flipper/version.rb
CHANGED