flipper 1.2.1 → 1.3.0.pre
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 +2 -1
- data/.github/workflows/examples.yml +1 -1
- data/Gemfile +0 -1
- data/README.md +1 -0
- data/lib/flipper/adapters/cache_base.rb +143 -0
- data/lib/flipper/adapters/operation_logger.rb +18 -88
- data/lib/flipper/adapters/read_only.rb +6 -39
- data/lib/flipper/adapters/strict.rb +5 -10
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +38 -15
- data/lib/flipper/cloud/configuration.rb +2 -3
- data/lib/flipper/cloud/telemetry/instrumenter.rb +4 -8
- data/lib/flipper/cloud/telemetry.rb +10 -2
- data/lib/flipper/poller.rb +6 -5
- data/lib/flipper/serializers/gzip.rb +3 -5
- data/lib/flipper/serializers/json.rb +3 -5
- data/lib/flipper/spec/shared_adapter_specs.rb +17 -16
- data/lib/flipper/test/shared_adapter_test.rb +17 -17
- data/lib/flipper/test_help.rb +15 -10
- data/lib/flipper/typecast.rb +3 -3
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +1 -0
- data/package-lock.json +41 -0
- data/package.json +10 -0
- data/spec/flipper/adapters/http_spec.rb +11 -2
- data/spec/flipper/cli_spec.rb +23 -23
- data/spec/flipper/cloud/configuration_spec.rb +29 -37
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +8 -9
- data/spec/flipper/cloud/telemetry_spec.rb +52 -0
- data/spec/flipper/cloud_spec.rb +6 -5
- data/spec/flipper/engine_spec.rb +39 -49
- data/spec/flipper/middleware/memoizer_spec.rb +7 -4
- data/spec/support/fail_on_output.rb +8 -0
- data/spec/support/spec_helpers.rb +2 -1
- data/test_rails/system/test_help_test.rb +8 -3
- metadata +9 -5
- data/spec/support/climate_control.rb +0 -7
@@ -2,6 +2,12 @@ require 'flipper/cloud/telemetry'
|
|
2
2
|
require 'flipper/cloud/configuration'
|
3
3
|
|
4
4
|
RSpec.describe Flipper::Cloud::Telemetry do
|
5
|
+
before do
|
6
|
+
# Stub polling for features.
|
7
|
+
stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
|
8
|
+
to_return(status: 200, body: "{}")
|
9
|
+
end
|
10
|
+
|
5
11
|
it "phones home and does not update telemetry interval if missing" do
|
6
12
|
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
7
13
|
to_return(status: 200, body: "{}")
|
@@ -42,6 +48,52 @@ RSpec.describe Flipper::Cloud::Telemetry do
|
|
42
48
|
expect(stub).to have_been_requested
|
43
49
|
end
|
44
50
|
|
51
|
+
it "phones home and requests shutdown if telemetry-shutdown header is true" do
|
52
|
+
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
53
|
+
to_return(status: 404, body: "{}", headers: {"telemetry-shutdown" => "true"})
|
54
|
+
|
55
|
+
output = StringIO.new
|
56
|
+
cloud_configuration = Flipper::Cloud::Configuration.new(
|
57
|
+
token: "test",
|
58
|
+
logger: Logger.new(output),
|
59
|
+
logging_enabled: true,
|
60
|
+
)
|
61
|
+
|
62
|
+
# Record some telemetry and stop the threads so we submit a response.
|
63
|
+
telemetry = described_class.new(cloud_configuration)
|
64
|
+
telemetry.record(Flipper::Feature::InstrumentationName, {
|
65
|
+
operation: :enabled?,
|
66
|
+
feature_name: :foo,
|
67
|
+
result: true,
|
68
|
+
})
|
69
|
+
telemetry.stop
|
70
|
+
expect(stub).to have_been_requested
|
71
|
+
expect(output.string).to match(/action=telemetry_shutdown message=The server has requested that telemetry be shut down./)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "phones home and does not shutdown if telemetry shutdown header is missing" do
|
75
|
+
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
76
|
+
to_return(status: 404, body: "{}", headers: {})
|
77
|
+
|
78
|
+
output = StringIO.new
|
79
|
+
cloud_configuration = Flipper::Cloud::Configuration.new(
|
80
|
+
token: "test",
|
81
|
+
logger: Logger.new(output),
|
82
|
+
logging_enabled: true,
|
83
|
+
)
|
84
|
+
|
85
|
+
# Record some telemetry and stop the threads so we submit a response.
|
86
|
+
telemetry = described_class.new(cloud_configuration)
|
87
|
+
telemetry.record(Flipper::Feature::InstrumentationName, {
|
88
|
+
operation: :enabled?,
|
89
|
+
feature_name: :foo,
|
90
|
+
result: true,
|
91
|
+
})
|
92
|
+
telemetry.stop
|
93
|
+
expect(stub).to have_been_requested
|
94
|
+
expect(output.string).not_to match(/action=telemetry_shutdown message=The server has requested that telemetry be shut down./)
|
95
|
+
end
|
96
|
+
|
45
97
|
it "can update telemetry interval from error" do
|
46
98
|
stub = stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
47
99
|
to_return(status: 500, body: "{}", headers: {"telemetry-interval" => "120"})
|
data/spec/flipper/cloud_spec.rb
CHANGED
@@ -36,7 +36,8 @@ RSpec.describe Flipper::Cloud do
|
|
36
36
|
expect(client.uri.host).to eq('www.flippercloud.io')
|
37
37
|
expect(client.uri.path).to eq('/adapter')
|
38
38
|
expect(client.headers["flipper-cloud-token"]).to eq(token)
|
39
|
-
expect(@instance.instrumenter).to
|
39
|
+
expect(@instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
|
40
|
+
expect(@instance.instrumenter.instrumenter).to be(Flipper::Instrumenters::Noop)
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
@@ -55,15 +56,15 @@ RSpec.describe Flipper::Cloud do
|
|
55
56
|
end
|
56
57
|
|
57
58
|
it 'can initialize with no token explicitly provided' do
|
58
|
-
|
59
|
-
|
60
|
-
end
|
59
|
+
ENV['FLIPPER_CLOUD_TOKEN'] = 'asdf'
|
60
|
+
expect(described_class.new).to be_instance_of(Flipper::Cloud::DSL)
|
61
61
|
end
|
62
62
|
|
63
63
|
it 'can set instrumenter' do
|
64
64
|
instrumenter = Flipper::Instrumenters::Memory.new
|
65
65
|
instance = described_class.new(token: 'asdf', instrumenter: instrumenter)
|
66
|
-
expect(instance.instrumenter).to
|
66
|
+
expect(instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
|
67
|
+
expect(instance.instrumenter.instrumenter).to be(instrumenter)
|
67
68
|
end
|
68
69
|
|
69
70
|
it 'allows wrapping adapter with another adapter like the instrumenter' do
|
data/spec/flipper/engine_spec.rb
CHANGED
@@ -4,8 +4,10 @@ require 'flipper/engine'
|
|
4
4
|
RSpec.describe Flipper::Engine do
|
5
5
|
let(:application) do
|
6
6
|
Class.new(Rails::Application) do
|
7
|
+
config.load_defaults Rails::VERSION::STRING.to_f
|
7
8
|
config.eager_load = false
|
8
9
|
config.logger = ActiveSupport::Logger.new($stdout)
|
10
|
+
config.active_support.remove_deprecated_time_with_zone_name = false
|
9
11
|
end.instance
|
10
12
|
end
|
11
13
|
|
@@ -33,28 +35,25 @@ RSpec.describe Flipper::Engine do
|
|
33
35
|
let(:adapter) { Flipper.adapter.adapter }
|
34
36
|
|
35
37
|
it 'can set strict=true from ENV' do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
end
|
38
|
+
ENV['FLIPPER_STRICT'] = 'true'
|
39
|
+
subject
|
40
|
+
expect(config.strict).to eq(:raise)
|
41
|
+
expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
|
41
42
|
end
|
42
43
|
|
43
44
|
it 'can set strict=warn from ENV' do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
45
|
+
ENV['FLIPPER_STRICT'] = 'warn'
|
46
|
+
subject
|
47
|
+
expect(config.strict).to eq(:warn)
|
48
|
+
expect(adapter).to be_instance_of(Flipper::Adapters::Strict)
|
49
|
+
expect(adapter.handler).to be(:warn)
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'can set strict=false from ENV' do
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
53
|
+
ENV['FLIPPER_STRICT'] = 'false'
|
54
|
+
subject
|
55
|
+
expect(config.strict).to eq(false)
|
56
|
+
expect(adapter).to be_instance_of(Flipper::Adapters::Memory)
|
58
57
|
end
|
59
58
|
|
60
59
|
[true, :raise, :warn].each do |value|
|
@@ -104,39 +103,34 @@ RSpec.describe Flipper::Engine do
|
|
104
103
|
it_behaves_like 'config.strict'
|
105
104
|
|
106
105
|
it 'can set env_key from ENV' do
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
106
|
+
ENV['FLIPPER_ENV_KEY'] = 'flopper'
|
107
|
+
subject
|
108
|
+
expect(config.env_key).to eq('flopper')
|
111
109
|
end
|
112
110
|
|
113
111
|
it 'can set memoize from ENV' do
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
112
|
+
ENV['FLIPPER_MEMOIZE'] = 'false'
|
113
|
+
subject
|
114
|
+
expect(config.memoize).to eq(false)
|
118
115
|
end
|
119
116
|
|
120
117
|
it 'can set preload from ENV' do
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
118
|
+
ENV['FLIPPER_PRELOAD'] = 'false'
|
119
|
+
subject
|
120
|
+
expect(config.preload).to eq(false)
|
125
121
|
end
|
126
122
|
|
127
123
|
it 'can set instrumenter from ENV' do
|
128
124
|
stub_const('My::Cool::Instrumenter', Class.new)
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
end
|
125
|
+
ENV['FLIPPER_INSTRUMENTER'] = 'My::Cool::Instrumenter'
|
126
|
+
subject
|
127
|
+
expect(config.instrumenter).to eq(My::Cool::Instrumenter)
|
133
128
|
end
|
134
129
|
|
135
130
|
it 'can set log from ENV' do
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
131
|
+
ENV['FLIPPER_LOG'] = 'false'
|
132
|
+
subject
|
133
|
+
expect(config.log).to eq(false)
|
140
134
|
end
|
141
135
|
|
142
136
|
it 'sets defaults' do
|
@@ -220,10 +214,8 @@ RSpec.describe Flipper::Engine do
|
|
220
214
|
end
|
221
215
|
|
222
216
|
context 'with cloud' do
|
223
|
-
|
224
|
-
|
225
|
-
example.run
|
226
|
-
end
|
217
|
+
before do
|
218
|
+
ENV["FLIPPER_CLOUD_TOKEN"] = "test-token"
|
227
219
|
end
|
228
220
|
|
229
221
|
# App for Rack::Test
|
@@ -244,14 +236,13 @@ RSpec.describe Flipper::Engine do
|
|
244
236
|
application.initialize!
|
245
237
|
|
246
238
|
expect(Flipper.instance).to be_a(Flipper::Cloud::DSL)
|
247
|
-
expect(Flipper.instance.instrumenter).to
|
239
|
+
expect(Flipper.instance.instrumenter).to be_a(Flipper::Cloud::Telemetry::Instrumenter)
|
240
|
+
expect(Flipper.instance.instrumenter.instrumenter).to be(ActiveSupport::Notifications)
|
248
241
|
end
|
249
242
|
|
250
243
|
context "with CLOUD_SYNC_SECRET" do
|
251
|
-
|
252
|
-
|
253
|
-
example.run
|
254
|
-
end
|
244
|
+
before do
|
245
|
+
ENV["FLIPPER_CLOUD_SYNC_SECRET"] = "test-secret"
|
255
246
|
end
|
256
247
|
|
257
248
|
let(:request_body) do
|
@@ -296,10 +287,9 @@ RSpec.describe Flipper::Engine do
|
|
296
287
|
|
297
288
|
context "without FLIPPER_CLOUD_TOKEN" do
|
298
289
|
it "gracefully skips configuring webhook app" do
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
end
|
290
|
+
ENV["FLIPPER_CLOUD_TOKEN"] = nil
|
291
|
+
application.initialize!
|
292
|
+
expect(Flipper.instance).to be_a(Flipper::DSL)
|
303
293
|
|
304
294
|
post "/_flipper"
|
305
295
|
expect(last_response.status).to eq(404)
|
@@ -458,7 +458,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
458
458
|
logged_memory = Flipper::Adapters::OperationLogger.new(memory)
|
459
459
|
cache = ActiveSupport::Cache::MemoryStore.new
|
460
460
|
cache.clear
|
461
|
-
cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache
|
461
|
+
cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache)
|
462
462
|
logged_cached = Flipper::Adapters::OperationLogger.new(cached)
|
463
463
|
memo = {}
|
464
464
|
flipper = Flipper.new(logged_cached)
|
@@ -471,15 +471,18 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
471
471
|
|
472
472
|
get '/', {}, 'flipper' => flipper
|
473
473
|
expect(logged_cached.count(:get_all)).to be(1)
|
474
|
-
expect(logged_memory.count(:
|
474
|
+
expect(logged_memory.count(:features)).to be(1)
|
475
|
+
expect(logged_memory.count(:get_multi)).to be(1)
|
475
476
|
|
476
477
|
get '/', {}, 'flipper' => flipper
|
477
478
|
expect(logged_cached.count(:get_all)).to be(2)
|
478
|
-
expect(logged_memory.count(:
|
479
|
+
expect(logged_memory.count(:features)).to be(1)
|
480
|
+
expect(logged_memory.count(:get_multi)).to be(1)
|
479
481
|
|
480
482
|
get '/', {}, 'flipper' => flipper
|
481
483
|
expect(logged_cached.count(:get_all)).to be(3)
|
482
|
-
expect(logged_memory.count(:
|
484
|
+
expect(logged_memory.count(:features)).to be(1)
|
485
|
+
expect(logged_memory.count(:get_multi)).to be(1)
|
483
486
|
end
|
484
487
|
end
|
485
488
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'ice_age'
|
2
2
|
require 'json'
|
3
3
|
require 'rack/test'
|
4
|
+
require 'rack/session'
|
4
5
|
|
5
6
|
module SpecHelpers
|
6
7
|
extend self
|
@@ -12,7 +13,7 @@ module SpecHelpers
|
|
12
13
|
|
13
14
|
def build_app(flipper, options = {})
|
14
15
|
Flipper::UI.app(flipper, options) do |builder|
|
15
|
-
builder.use Rack::Session::Cookie, secret: 'test'
|
16
|
+
builder.use Rack::Session::Cookie, secret: 'test' * 16 # Rack 3+ wants a 64-character secret
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -29,10 +29,15 @@ end
|
|
29
29
|
|
30
30
|
class TestHelpTest < ActionDispatch::SystemTestCase
|
31
31
|
# Any driver that runs the app in a separate thread will test what we want here.
|
32
|
-
driven_by :cuprite
|
32
|
+
driven_by :cuprite, options: { process_timeout: 60 }
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
setup do
|
35
|
+
# Reconfigure Flipper since other tests change the adapter.
|
36
|
+
flipper_configure
|
37
|
+
|
38
|
+
# Ensure this test uses this app instance
|
39
|
+
Rails.application = TestApp.instance
|
40
|
+
end
|
36
41
|
|
37
42
|
test "configures a shared adapter between tests and app" do
|
38
43
|
Flipper.disable(:test)
|
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: 1.
|
4
|
+
version: 1.3.0.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- lib/flipper/actor.rb
|
91
91
|
- lib/flipper/adapter.rb
|
92
92
|
- lib/flipper/adapter_builder.rb
|
93
|
+
- lib/flipper/adapters/cache_base.rb
|
93
94
|
- lib/flipper/adapters/dual_write.rb
|
94
95
|
- lib/flipper/adapters/failover.rb
|
95
96
|
- lib/flipper/adapters/failsafe.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- lib/flipper/adapters/sync/feature_synchronizer.rb
|
110
111
|
- lib/flipper/adapters/sync/interval_synchronizer.rb
|
111
112
|
- lib/flipper/adapters/sync/synchronizer.rb
|
113
|
+
- lib/flipper/adapters/wrapper.rb
|
112
114
|
- lib/flipper/cli.rb
|
113
115
|
- lib/flipper/cloud.rb
|
114
116
|
- lib/flipper/cloud/configuration.rb
|
@@ -193,6 +195,8 @@ files:
|
|
193
195
|
- lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb
|
194
196
|
- lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb
|
195
197
|
- lib/generators/flipper/update_generator.rb
|
198
|
+
- package-lock.json
|
199
|
+
- package.json
|
196
200
|
- spec/fixtures/environment.rb
|
197
201
|
- spec/fixtures/feature.json
|
198
202
|
- spec/fixtures/flipper_pstore_1679087600.json
|
@@ -286,8 +290,8 @@ files:
|
|
286
290
|
- spec/flipper_spec.rb
|
287
291
|
- spec/spec_helper.rb
|
288
292
|
- spec/support/actor_names.yml
|
289
|
-
- spec/support/climate_control.rb
|
290
293
|
- spec/support/descriptions.yml
|
294
|
+
- spec/support/fail_on_output.rb
|
291
295
|
- spec/support/fake_backoff_policy.rb
|
292
296
|
- spec/support/fake_udp_socket.rb
|
293
297
|
- spec/support/skippable.rb
|
@@ -307,7 +311,7 @@ metadata:
|
|
307
311
|
homepage_uri: https://www.flippercloud.io
|
308
312
|
source_code_uri: https://github.com/flippercloud/flipper
|
309
313
|
bug_tracker_uri: https://github.com/flippercloud/flipper/issues
|
310
|
-
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.
|
314
|
+
changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.0.pre
|
311
315
|
post_install_message:
|
312
316
|
rdoc_options: []
|
313
317
|
require_paths:
|
@@ -421,8 +425,8 @@ test_files:
|
|
421
425
|
- spec/flipper_spec.rb
|
422
426
|
- spec/spec_helper.rb
|
423
427
|
- spec/support/actor_names.yml
|
424
|
-
- spec/support/climate_control.rb
|
425
428
|
- spec/support/descriptions.yml
|
429
|
+
- spec/support/fail_on_output.rb
|
426
430
|
- spec/support/fake_backoff_policy.rb
|
427
431
|
- spec/support/fake_udp_socket.rb
|
428
432
|
- spec/support/skippable.rb
|