flipper 0.20.1 → 0.20.2
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/.github/workflows/ci.yml +49 -0
- data/Changelog.md +7 -0
- data/lib/flipper/adapters/http.rb +32 -28
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/http_spec.rb +74 -8
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df90e63870767caa4d32363658e4515d0822a1453cc3a5325d7dbf932c326b59
|
4
|
+
data.tar.gz: 125256e9cdee5bd0e2bfe065203140eb7fa015cac56043711c7eb45bd5421aa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9598a0a2f62b5c160a8a7c81b50ed9d2bd06f6e70898037c8321f9060780875347552b66919c62b5deb40647f734efa6bef1382b0e9fb1caefe2c8f4b5eaec01
|
7
|
+
data.tar.gz: 8bb413f54398cf90e9aafc7feba144a2e15343e8a75e28ca197d52505ae4771659b3fc7bfafec899388e71c76832859396d86fa52438ba22327f6b6522ade3a5
|
@@ -0,0 +1,49 @@
|
|
1
|
+
name: CI
|
2
|
+
on: [push, pull_request]
|
3
|
+
jobs:
|
4
|
+
build:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
services:
|
7
|
+
redis:
|
8
|
+
image: redis
|
9
|
+
ports: ['6379:6379']
|
10
|
+
options: >-
|
11
|
+
--health-cmd "redis-cli ping"
|
12
|
+
--health-interval 10s
|
13
|
+
--health-timeout 5s
|
14
|
+
--health-retries 5
|
15
|
+
strategy:
|
16
|
+
matrix:
|
17
|
+
ruby: ['2.5', '2.6', '2.7']
|
18
|
+
env:
|
19
|
+
RAILS_VERSION: 6.0.0
|
20
|
+
SQLITE3_VERSION: 1.4.1
|
21
|
+
REDIS_URL: redis://localhost:6379/0
|
22
|
+
steps:
|
23
|
+
- name: Setup memcached
|
24
|
+
uses: KeisukeYamashita/memcached-actions@v1
|
25
|
+
- name: Start MongoDB
|
26
|
+
uses: supercharge/mongodb-github-action@1.3.0
|
27
|
+
with:
|
28
|
+
mongodb-version: 4.0
|
29
|
+
- name: Check out repository code
|
30
|
+
uses: actions/checkout@v2
|
31
|
+
- name: Do some action caching
|
32
|
+
uses: actions/cache@v1
|
33
|
+
with:
|
34
|
+
path: vendor/bundle
|
35
|
+
key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
|
36
|
+
restore-keys: |
|
37
|
+
${{ runner.os }}-gem-
|
38
|
+
- name: Set up Ruby
|
39
|
+
uses: actions/setup-ruby@v1
|
40
|
+
with:
|
41
|
+
ruby-version: ${{ matrix.ruby }}
|
42
|
+
- name: Install libpq-dev
|
43
|
+
run: sudo apt-get -yqq install libpq-dev
|
44
|
+
- name: Install bundler
|
45
|
+
run: gem install bundler
|
46
|
+
- name: Run bundler
|
47
|
+
run: bundle install --jobs 4 --retry 3
|
48
|
+
- name: Run Rake
|
49
|
+
run: bundle exec rake
|
data/Changelog.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.20.2
|
2
|
+
|
3
|
+
### Additions/Changes
|
4
|
+
|
5
|
+
* Http adapter now raises error when enable/disable/add/remove/clear fail.
|
6
|
+
* Cloud adapter sends some extra info like hostname, ruby version, etc. for debugging and decision making.
|
7
|
+
|
1
8
|
## 0.20.1
|
2
9
|
|
3
10
|
### Additions/Changes
|
@@ -35,12 +35,6 @@ module Flipper
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
def add(feature)
|
39
|
-
body = JSON.generate(name: feature.key)
|
40
|
-
response = @client.post('/features', body)
|
41
|
-
response.is_a?(Net::HTTPOK)
|
42
|
-
end
|
43
|
-
|
44
38
|
def get_multi(features)
|
45
39
|
csv_keys = features.map(&:key).join(',')
|
46
40
|
response = @client.get("/features?keys=#{csv_keys}")
|
@@ -87,51 +81,61 @@ module Flipper
|
|
87
81
|
parsed_response['features'].map { |feature| feature['key'] }.to_set
|
88
82
|
end
|
89
83
|
|
84
|
+
def add(feature)
|
85
|
+
body = JSON.generate(name: feature.key)
|
86
|
+
response = @client.post('/features', body)
|
87
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
88
|
+
true
|
89
|
+
end
|
90
|
+
|
90
91
|
def remove(feature)
|
91
92
|
response = @client.delete("/features/#{feature.key}")
|
92
|
-
response.is_a?(Net::HTTPNoContent)
|
93
|
+
raise Error, response unless response.is_a?(Net::HTTPNoContent)
|
94
|
+
true
|
93
95
|
end
|
94
96
|
|
95
97
|
def enable(feature, gate, thing)
|
96
98
|
body = request_body_for_gate(gate, thing.value.to_s)
|
97
99
|
query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : ""
|
98
100
|
response = @client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
|
99
|
-
response.is_a?(Net::HTTPOK)
|
101
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
102
|
+
true
|
100
103
|
end
|
101
104
|
|
102
105
|
def disable(feature, gate, thing)
|
103
106
|
body = request_body_for_gate(gate, thing.value.to_s)
|
104
107
|
query_string = gate.key == :groups ? "?allow_unregistered_groups=true" : ""
|
105
|
-
response =
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
108
|
+
response = case gate.key
|
109
|
+
when :percentage_of_actors, :percentage_of_time
|
110
|
+
@client.post("/features/#{feature.key}/#{gate.key}#{query_string}", body)
|
111
|
+
else
|
112
|
+
@client.delete("/features/#{feature.key}/#{gate.key}#{query_string}", body)
|
113
|
+
end
|
114
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
115
|
+
true
|
113
116
|
end
|
114
117
|
|
115
118
|
def clear(feature)
|
116
119
|
response = @client.delete("/features/#{feature.key}/clear")
|
117
|
-
response.is_a?(Net::HTTPNoContent)
|
120
|
+
raise Error, response unless response.is_a?(Net::HTTPNoContent)
|
121
|
+
true
|
118
122
|
end
|
119
123
|
|
120
124
|
private
|
121
125
|
|
122
126
|
def request_body_for_gate(gate, value)
|
123
127
|
data = case gate.key
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
128
|
+
when :boolean
|
129
|
+
{}
|
130
|
+
when :groups
|
131
|
+
{ name: value }
|
132
|
+
when :actors
|
133
|
+
{ flipper_id: value }
|
134
|
+
when :percentage_of_actors, :percentage_of_time
|
135
|
+
{ percentage: value }
|
136
|
+
else
|
137
|
+
raise "#{gate.key} is not a valid flipper gate key"
|
138
|
+
end
|
135
139
|
JSON.generate(data)
|
136
140
|
end
|
137
141
|
|
@@ -19,7 +19,7 @@ module Flipper
|
|
19
19
|
# Public: Initializes a new interval synchronizer.
|
20
20
|
#
|
21
21
|
# synchronizer - The Synchronizer to call when the interval has passed.
|
22
|
-
# interval - The Integer number of
|
22
|
+
# interval - The Integer number of seconds between invocations of
|
23
23
|
# the wrapped synchronizer.
|
24
24
|
def initialize(synchronizer, interval: nil)
|
25
25
|
@synchronizer = synchronizer
|
data/lib/flipper/version.rb
CHANGED
@@ -76,9 +76,9 @@ RSpec.describe Flipper::Adapters::Http do
|
|
76
76
|
.to_return(status: 503, body: "", headers: {})
|
77
77
|
|
78
78
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
79
|
-
expect
|
79
|
+
expect {
|
80
80
|
adapter.get(flipper[:feature_panel])
|
81
|
-
|
81
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -88,9 +88,9 @@ RSpec.describe Flipper::Adapters::Http do
|
|
88
88
|
.to_return(status: 503, body: "", headers: {})
|
89
89
|
|
90
90
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
91
|
-
expect
|
91
|
+
expect {
|
92
92
|
adapter.get_multi([flipper[:feature_panel]])
|
93
|
-
|
93
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -100,9 +100,9 @@ RSpec.describe Flipper::Adapters::Http do
|
|
100
100
|
.to_return(status: 503, body: "", headers: {})
|
101
101
|
|
102
102
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
103
|
-
expect
|
103
|
+
expect {
|
104
104
|
adapter.get_all
|
105
|
-
|
105
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
@@ -112,9 +112,75 @@ RSpec.describe Flipper::Adapters::Http do
|
|
112
112
|
.to_return(status: 503, body: "", headers: {})
|
113
113
|
|
114
114
|
adapter = described_class.new(url: 'http://app.com/flipper')
|
115
|
-
expect
|
115
|
+
expect {
|
116
116
|
adapter.features
|
117
|
-
|
117
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#add" do
|
122
|
+
it "raises error when not successful" do
|
123
|
+
stub_request(:post, /app.com/)
|
124
|
+
.to_return(status: 503, body: "{}", headers: {})
|
125
|
+
|
126
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
127
|
+
expect {
|
128
|
+
adapter.add(Flipper::Feature.new(:search, adapter))
|
129
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "#remove" do
|
134
|
+
it "raises error when not successful" do
|
135
|
+
stub_request(:delete, /app.com/)
|
136
|
+
.to_return(status: 503, body: "{}", headers: {})
|
137
|
+
|
138
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
139
|
+
expect {
|
140
|
+
adapter.remove(Flipper::Feature.new(:search, adapter))
|
141
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "#clear" do
|
146
|
+
it "raises error when not successful" do
|
147
|
+
stub_request(:delete, /app.com/)
|
148
|
+
.to_return(status: 503, body: "{}", headers: {})
|
149
|
+
|
150
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
151
|
+
expect {
|
152
|
+
adapter.clear(Flipper::Feature.new(:search, adapter))
|
153
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#enable" do
|
158
|
+
it "raises error when not successful" do
|
159
|
+
stub_request(:post, /app.com/)
|
160
|
+
.to_return(status: 503, body: "{}", headers: {})
|
161
|
+
|
162
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
163
|
+
feature = Flipper::Feature.new(:search, adapter)
|
164
|
+
gate = feature.gate(:boolean)
|
165
|
+
thing = gate.wrap(true)
|
166
|
+
expect {
|
167
|
+
adapter.enable(feature, gate, thing)
|
168
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "#disable" do
|
173
|
+
it "raises error when not successful" do
|
174
|
+
stub_request(:delete, /app.com/)
|
175
|
+
.to_return(status: 503, body: "{}", headers: {})
|
176
|
+
|
177
|
+
adapter = described_class.new(url: 'http://app.com/flipper')
|
178
|
+
feature = Flipper::Feature.new(:search, adapter)
|
179
|
+
gate = feature.gate(:boolean)
|
180
|
+
thing = gate.wrap(false)
|
181
|
+
expect {
|
182
|
+
adapter.disable(feature, gate, thing)
|
183
|
+
}.to raise_error(Flipper::Adapters::Http::Error)
|
118
184
|
end
|
119
185
|
end
|
120
186
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.20.
|
4
|
+
version: 0.20.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -18,6 +18,7 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- ".codeclimate.yml"
|
21
|
+
- ".github/workflows/ci.yml"
|
21
22
|
- CODE_OF_CONDUCT.md
|
22
23
|
- Changelog.md
|
23
24
|
- Dockerfile
|