flipper-api 0.26.2 → 0.27.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8804cea61a705f8f6ce27bcf54a2f83fb46c09117f8e0104f8bcf296c1b4d623
4
- data.tar.gz: 59b804c299c5c15fd995e988d356f930044dfc9db967b42e7d0e749ec7712b13
3
+ metadata.gz: e6dcd6ea7ccf9b891553566c5b845688204c4c2a2add0a9607f9d6d35f599359
4
+ data.tar.gz: 7329555251cb9b5c5ada93ec6aa5224eb6dbf0a0a27707f9d361e7c5154fd9ab
5
5
  SHA512:
6
- metadata.gz: 9d140074d8035fc0214c85b7b1951528ace17a5fb9811ee050eefd95b985a0df46f4e622bebb93bc5ade0779f0f84b93d6c78fa9ba28a4d1d83983b785cc63fd
7
- data.tar.gz: 8c974f5501a3dbfc4f114a6c338f0ea1b86df3dc766da37761670539a2a1521569c86d16210f4d3b8bc57e015af07fe361845a1cecb06f0018d7b0c366f18cf8
6
+ metadata.gz: 0bc38a19b342daf3b31ca168cc08304230e83d468d5acf6658815661c002c94ad7bac947c6f9740d13c604c855c7982a1436382a9fc5fad0d04c9ab65779701b
7
+ data.tar.gz: 2cd588a44240ca5d8129d21f08d26593cb35039b3e46dd685cbbb97dff92b19340927e7d805bdc49423fbcd1c5cfa796af824fb2033f915a515987c37741190e
@@ -24,10 +24,10 @@ module Flipper
24
24
  ERRORS = {
25
25
  feature_not_found: Error.new(1, 'Feature not found.', 404),
26
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),
27
+ percentage_invalid: Error.new(3, 'Percentage must be a positive number less than or equal to 100.', 422),
29
28
  flipper_id_invalid: Error.new(4, 'Required parameter flipper_id is missing.', 422),
30
29
  name_invalid: Error.new(5, 'Required parameter name is missing.', 422),
30
+ import_invalid: Error.new(6, 'Import invalid.', 422),
31
31
  }.freeze
32
32
  end
33
33
  end
@@ -23,6 +23,7 @@ module Flipper
23
23
  @action_collection.add Api::V1::Actions::Actors
24
24
  @action_collection.add Api::V1::Actions::Feature
25
25
  @action_collection.add Api::V1::Actions::Features
26
+ @action_collection.add Api::V1::Actions::Import
26
27
  end
27
28
 
28
29
  def call(env)
@@ -11,6 +11,8 @@ module Flipper
11
11
  def get
12
12
  keys = params['keys']
13
13
  exclude_gates = params['exclude_gates']&.downcase == "true"
14
+ exclude_gate_names = params['exclude_gate_names']&.downcase == "true"
15
+
14
16
  features = if keys
15
17
  names = keys.split(',')
16
18
  if names.empty?
@@ -27,7 +29,10 @@ module Flipper
27
29
  end
28
30
 
29
31
  decorated_features = features.map do |feature|
30
- Decorators::Feature.new(feature).as_json(exclude_gates: exclude_gates)
32
+ Decorators::Feature.new(feature).as_json(
33
+ exclude_gates: exclude_gates,
34
+ exclude_gate_names: exclude_gate_names
35
+ )
31
36
  end
32
37
 
33
38
  json_response(features: decorated_features)
@@ -0,0 +1,25 @@
1
+ require 'flipper/exporters/json/export'
2
+ require 'flipper/api/action'
3
+ require 'flipper/api/v1/decorators/feature'
4
+
5
+ module Flipper
6
+ module Api
7
+ module V1
8
+ module Actions
9
+ class Import < Api::Action
10
+ route %r{\A/import/?\Z}
11
+
12
+ def post
13
+ body = request.body.read
14
+ request.body.rewind
15
+ export = Flipper::Exporters::Json::Export.new(contents: body)
16
+ flipper.import(export)
17
+ json_response({}, 204)
18
+ rescue Flipper::Exporters::Json::InvalidError
19
+ json_error_response(:import_invalid)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -10,7 +10,7 @@ module Flipper
10
10
  alias_method :feature, :__getobj__
11
11
 
12
12
  # Public: Returns instance as hash that is ready to be json dumped.
13
- def as_json(exclude_gates: false)
13
+ def as_json(exclude_gates: false, exclude_gate_names: false)
14
14
  result = {
15
15
  'key' => key,
16
16
  'state' => state.to_s,
@@ -19,7 +19,7 @@ module Flipper
19
19
  unless exclude_gates
20
20
  gate_values = feature.adapter.get(self)
21
21
  result['gates'] = gates.map do |gate|
22
- Decorators::Gate.new(gate, gate_values[gate.key]).as_json
22
+ Decorators::Gate.new(gate, gate_values[gate.key]).as_json(exclude_name: exclude_gate_names)
23
23
  end
24
24
  end
25
25
 
@@ -14,12 +14,13 @@ module Flipper
14
14
  @value = value
15
15
  end
16
16
 
17
- def as_json
18
- {
17
+ def as_json(exclude_name: false)
18
+ as_json = {
19
19
  'key' => gate.key.to_s,
20
- 'name' => gate.name.to_s,
21
20
  'value' => value_as_json,
22
21
  }
22
+ as_json['name'] = gate.name.to_s unless exclude_name
23
+ as_json
23
24
  end
24
25
 
25
26
  private
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.26.2'.freeze
2
+ VERSION = '0.27.1'.freeze
3
3
  end
@@ -8,10 +8,11 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
8
8
  before do
9
9
  flipper[:my_feature].enable
10
10
  flipper[:my_feature].enable(admin)
11
- get '/features'
12
11
  end
13
12
 
14
13
  it 'responds with correct attributes' do
14
+ get '/features'
15
+
15
16
  expected_response = {
16
17
  'features' => [
17
18
  {
@@ -50,6 +51,28 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
50
51
  expect(last_response.status).to eq(200)
51
52
  expect(json_response).to eq(expected_response)
52
53
  end
54
+
55
+ it 'responds without names when instructed by param' do
56
+ expected_response = {
57
+ 'features' => [
58
+ {
59
+ 'key' => 'my_feature',
60
+ 'state' => 'on',
61
+ 'gates' => [
62
+ { 'key' => 'boolean', 'value' => 'true'},
63
+ { 'key' => 'actors', 'value' => ['10']},
64
+ {'key' => 'percentage_of_actors', 'value' => nil},
65
+ { 'key' => 'percentage_of_time', 'value' => nil},
66
+ { 'key' => 'groups', 'value' => []},
67
+ ],
68
+ },
69
+ ],
70
+ }
71
+
72
+ get '/features', 'exclude_gate_names' => 'true'
73
+ expect(last_response.status).to eq(200)
74
+ expect(json_response).to eq(expected_response)
75
+ end
53
76
  end
54
77
 
55
78
  context 'with keys specified' do
@@ -103,13 +126,12 @@ RSpec.describe Flipper::Api::V1::Actions::Features do
103
126
  post '/features', name: 'my_feature'
104
127
  end
105
128
 
106
- it 'responds 200 ' do
129
+ it 'responds 200' do
107
130
  expect(last_response.status).to eq(200)
108
131
  end
109
132
 
110
133
  it 'returns decorated feature' do
111
134
  expected_response = {
112
-
113
135
  'key' => 'my_feature',
114
136
  'state' => 'off',
115
137
  'gates' => [
@@ -19,7 +19,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
19
19
  end
20
20
 
21
21
  it 'returns decorated feature with group enabled' do
22
- group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
22
+ group_gate = json_response['gates'].find { |m| m['key'] == 'groups' }
23
23
  expect(group_gate['value']).to eq(['admins'])
24
24
  end
25
25
  end
@@ -42,7 +42,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
42
42
  end
43
43
 
44
44
  it 'returns decorated feature with group enabled' do
45
- group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
45
+ group_gate = json_response['gates'].find { |m| m['key'] == 'groups' }
46
46
  expect(group_gate['value']).to eq(['admins'])
47
47
  end
48
48
  end
@@ -89,7 +89,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
89
89
  end
90
90
 
91
91
  it 'returns decorated feature with group disabled' do
92
- group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
92
+ group_gate = json_response['gates'].find { |m| m['key'] == 'groups' }
93
93
  expect(group_gate['value']).to eq([])
94
94
  end
95
95
  end
@@ -111,7 +111,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
111
111
  end
112
112
 
113
113
  it 'returns decorated feature with group disabled' do
114
- group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
114
+ group_gate = json_response['gates'].find { |m| m['key'] == 'groups' }
115
115
  expect(group_gate['value']).to eq([])
116
116
  end
117
117
  end
@@ -145,7 +145,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
145
145
  end
146
146
 
147
147
  it 'returns decorated feature with group in groups set' do
148
- group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
148
+ group_gate = json_response['gates'].find { |m| m['key'] == 'groups' }
149
149
  expect(group_gate['value']).to eq(['admins'])
150
150
  end
151
151
 
@@ -167,7 +167,7 @@ RSpec.describe Flipper::Api::V1::Actions::GroupsGate do
167
167
  end
168
168
 
169
169
  it 'returns decorated feature with group not in groups set' do
170
- group_gate = json_response['gates'].find { |m| m['name'] == 'group' }
170
+ group_gate = json_response['gates'].find { |m| m['key'] == 'groups' }
171
171
  expect(group_gate['value']).to eq([])
172
172
  end
173
173
 
@@ -0,0 +1,69 @@
1
+ RSpec.describe Flipper::Api::V1::Actions::Import do
2
+ let(:app) { build_api(flipper) }
3
+
4
+ describe 'post' do
5
+ context 'succesful request' do
6
+ before do
7
+ flipper.enable(:search)
8
+ flipper.disable(:adios)
9
+
10
+ source_flipper = build_flipper
11
+ source_flipper.disable(:search)
12
+ source_flipper.enable_actor(:google_analytics, Flipper::Actor.new("User;1"))
13
+
14
+ export = source_flipper.export
15
+
16
+ post '/import', export.contents, 'CONTENT_TYPE' => 'application/json'
17
+ end
18
+
19
+ it 'responds 204' do
20
+ expect(last_response.status).to eq(204)
21
+ end
22
+
23
+ it 'imports features' do
24
+ expect(flipper[:search].boolean_value).to be(false)
25
+ expect(flipper[:google_analytics].actors_value).to eq(Set["User;1"])
26
+ expect(flipper.features.map(&:key)).to eq(["search", "google_analytics"])
27
+ end
28
+ end
29
+
30
+ context 'empty request' do
31
+ before do
32
+ flipper.enable(:search)
33
+ flipper.disable(:adios)
34
+
35
+ source_flipper = build_flipper
36
+ export = source_flipper.export
37
+
38
+ post '/import', export.contents, 'CONTENT_TYPE' => 'application/json'
39
+ end
40
+
41
+ it 'responds 204' do
42
+ expect(last_response.status).to eq(204)
43
+ end
44
+
45
+ it 'removes all features' do
46
+ expect(flipper.features.map(&:key)).to eq([])
47
+ end
48
+ end
49
+
50
+ context 'bad request' do
51
+ before do
52
+ post '/import'
53
+ end
54
+
55
+ it 'returns correct status code' do
56
+ expect(last_response.status).to eq(422)
57
+ end
58
+
59
+ it 'returns formatted error' do
60
+ expected = {
61
+ 'code' => 6,
62
+ 'message' => 'Import invalid.',
63
+ 'more_info' => api_error_code_reference_url,
64
+ }
65
+ expect(json_response).to eq(expected)
66
+ end
67
+ end
68
+ end
69
+ end
@@ -8,7 +8,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
8
8
  end
9
9
 
10
10
  it 'returns decorated feature with gate enabled for a percent of actors' do
11
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
11
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_actors' }
12
12
  expect(gate['value']).to eq(percentage)
13
13
  end
14
14
  end
@@ -90,7 +90,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
90
90
  end
91
91
 
92
92
  it 'returns decorated feature with gate disabled' do
93
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
93
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_actors' }
94
94
  expect(gate['value']).to eq('0')
95
95
  end
96
96
  end
@@ -104,7 +104,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
104
104
  end
105
105
 
106
106
  it 'returns decorated feature with gate value set to 0 regardless of percentage requested' do
107
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
107
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_actors' }
108
108
  expect(gate['value']).to eq('0')
109
109
  end
110
110
  end
@@ -120,7 +120,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfActorsGate do
120
120
 
121
121
  it 'returns decorated feature with gate disabled' do
122
122
  expect(last_response.status).to eq(200)
123
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_actors' }
123
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_actors' }
124
124
  expect(gate['value']).to eq('0')
125
125
  end
126
126
  end
@@ -8,7 +8,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
8
8
  end
9
9
 
10
10
  it 'returns decorated feature with gate enabled for a percent of times' do
11
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
11
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_time' }
12
12
  expect(gate['value']).to eq(percentage)
13
13
  end
14
14
  end
@@ -90,7 +90,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
90
90
  end
91
91
 
92
92
  it 'returns decorated feature with gate disabled' do
93
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
93
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_time' }
94
94
  expect(gate['value']).to eq('0')
95
95
  end
96
96
  end
@@ -105,7 +105,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
105
105
 
106
106
  it 'returns decorated feature with gate value set to 0 regardless of percentage requested' do
107
107
  expect(last_response.status).to eq(200)
108
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
108
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_time' }
109
109
  expect(gate['value']).to eq('0')
110
110
  end
111
111
  end
@@ -120,7 +120,7 @@ RSpec.describe Flipper::Api::V1::Actions::PercentageOfTimeGate do
120
120
  end
121
121
 
122
122
  it 'returns decorated feature with gate disabled' do
123
- gate = json_response['gates'].find { |gate| gate['name'] == 'percentage_of_time' }
123
+ gate = json_response['gates'].find { |gate| gate['key'] == 'percentage_of_time' }
124
124
  expect(gate['value']).to eq('0')
125
125
  end
126
126
  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.26.2
4
+ version: 0.27.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: 2023-03-15 00:00:00.000000000 Z
11
+ date: 2023-03-20 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.26.2
39
+ version: 0.27.1
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.26.2
46
+ version: 0.27.1
47
47
  description:
48
48
  email:
49
49
  - nunemaker@gmail.com
@@ -67,6 +67,7 @@ files:
67
67
  - lib/flipper/api/v1/actions/feature.rb
68
68
  - lib/flipper/api/v1/actions/features.rb
69
69
  - lib/flipper/api/v1/actions/groups_gate.rb
70
+ - lib/flipper/api/v1/actions/import.rb
70
71
  - lib/flipper/api/v1/actions/percentage_of_actors_gate.rb
71
72
  - lib/flipper/api/v1/actions/percentage_of_time_gate.rb
72
73
  - lib/flipper/api/v1/decorators/actor.rb
@@ -82,6 +83,7 @@ files:
82
83
  - spec/flipper/api/v1/actions/feature_spec.rb
83
84
  - spec/flipper/api/v1/actions/features_spec.rb
84
85
  - spec/flipper/api/v1/actions/groups_gate_spec.rb
86
+ - spec/flipper/api/v1/actions/import_spec.rb
85
87
  - spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb
86
88
  - spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb
87
89
  - spec/flipper/api/v1/decorators/feature_spec.rb
@@ -120,6 +122,7 @@ test_files:
120
122
  - spec/flipper/api/v1/actions/feature_spec.rb
121
123
  - spec/flipper/api/v1/actions/features_spec.rb
122
124
  - spec/flipper/api/v1/actions/groups_gate_spec.rb
125
+ - spec/flipper/api/v1/actions/import_spec.rb
123
126
  - spec/flipper/api/v1/actions/percentage_of_actors_gate_spec.rb
124
127
  - spec/flipper/api/v1/actions/percentage_of_time_gate_spec.rb
125
128
  - spec/flipper/api/v1/decorators/feature_spec.rb