flipper-cloud 0.21.0.rc1 → 0.22.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: 9ba3c2db9aa7175de2301fb4fea82a25164e935b0f0fe5d7ce38911eee319cf9
4
- data.tar.gz: e97f8ec07dcc20057d6934e1cf933be9517d53549317539900630e43a7ffd23e
3
+ metadata.gz: c1cec6bcfa120d7adb3460621ddd532adad9059745e8c9268b53272230695b9a
4
+ data.tar.gz: f2532d2bcd7177d1aec8489429220ecdb44ace3d8995afb81ab30c0d9b8bdaee
5
5
  SHA512:
6
- metadata.gz: 62d4f0cc6817cfa654e1980156ca7e4d9ba81d1c0bb761f37651455b20f0b4732ceafe1adbf5a30ca09f8c1f8b80b8609c9d61ef969b82be48eedd183e4a5c15
7
- data.tar.gz: 4540506c2b1c4eabe0140f3757e0414977ef876454755106bedfffd8159a880ffea37b6bca24bde30e9e6b79259ad86e30afb415219f7a22104c91bcd4edfbe8
6
+ metadata.gz: '09b7f9f18671b1469dded918392df12314b2b82ce82ec5ce8b0390c11d49954b60ba16fbb7e7a52c2b1f93c5754d34d5d50f0b0e8f2ac2a4e0b2b96be0a9d3cc'
7
+ data.tar.gz: d3ea9b2abfe74837108b3434d48c93e291b076e9ac8e6537d5ff661231c9cba9b6e34468f32ddafd12b61a2c13877ee3d718d692e4ccecca38ecff72f25b9dfa
@@ -2,19 +2,18 @@
2
2
  # env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/basic.rb
3
3
  require 'bundler/setup'
4
4
  require 'flipper/cloud'
5
- flipper = Flipper::Cloud.new
6
5
 
7
- flipper[:stats].enable
6
+ Flipper[:stats].enable
8
7
 
9
- if flipper[:stats].enabled?
8
+ if Flipper[:stats].enabled?
10
9
  puts 'Enabled!'
11
10
  else
12
11
  puts 'Disabled!'
13
12
  end
14
13
 
15
- flipper[:stats].disable
14
+ Flipper[:stats].disable
16
15
 
17
- if flipper[:stats].enabled?
16
+ if Flipper[:stats].enabled?
18
17
  puts 'Enabled!'
19
18
  else
20
19
  puts 'Disabled!'
@@ -9,7 +9,7 @@ end
9
9
  Gem::Specification.new do |gem|
10
10
  gem.authors = ['John Nunemaker']
11
11
  gem.email = ['nunemaker@gmail.com']
12
- gem.summary = 'FeatureFlipper.com adapter for Flipper'
12
+ gem.summary = 'FlipperCloud.io adapter for Flipper'
13
13
  gem.license = 'MIT'
14
14
  gem.homepage = 'https://github.com/jnunemaker/flipper'
15
15
 
@@ -67,7 +67,7 @@ module Flipper
67
67
  @token = options.fetch(:token) { ENV["FLIPPER_CLOUD_TOKEN"] }
68
68
 
69
69
  if @token.nil?
70
- raise ArgumentError, "Flipper::Cloud token is missing. Please set FLIPPER_CLOUD_TOKEN or provide the token (e.g. Flipper::Cloud.new('token'))."
70
+ raise ArgumentError, "Flipper::Cloud token is missing. Please set FLIPPER_CLOUD_TOKEN or provide the token (e.g. Flipper::Cloud.new(token: 'token'))."
71
71
  end
72
72
 
73
73
  if ENV["FLIPPER_CLOUD_SYNC_METHOD"]
@@ -0,0 +1,29 @@
1
+ require "flipper/railtie"
2
+
3
+ module Flipper
4
+ module Cloud
5
+ class Engine < Rails::Engine
6
+ paths["config/routes.rb"] = ["lib/flipper/cloud/routes.rb"]
7
+
8
+ config.before_configuration do
9
+ config.flipper.cloud_path = "_flipper"
10
+ end
11
+
12
+ initializer "flipper.cloud.default", before: :load_config_initializers do |app|
13
+ Flipper.configure do |config|
14
+ config.default do
15
+ if ENV["FLIPPER_CLOUD_TOKEN"]
16
+ Flipper::Cloud.new(
17
+ local_adapter: config.adapter,
18
+ instrumenter: app.config.flipper.instrumenter
19
+ )
20
+ else
21
+ warn "Missing FLIPPER_CLOUD_TOKEN environment variable. Disabling Flipper::Cloud."
22
+ Flipper.new(config.adapter)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # Default routes loaded by Flipper::Cloud::Engine
2
+ Rails.application.routes.draw do
3
+ if ENV["FLIPPER_CLOUD_TOKEN"] && ENV["FLIPPER_CLOUD_SYNC_SECRET"]
4
+ config = Rails.application.config.flipper
5
+
6
+ cloud_app = Flipper::Cloud.app(nil,
7
+ env_key: config.env_key,
8
+ memoizer_options: { preload: config.preload }
9
+ )
10
+
11
+ mount cloud_app, at: config.cloud_path
12
+ end
13
+ end
data/lib/flipper/cloud.rb CHANGED
@@ -4,6 +4,7 @@ require "flipper/middleware/memoizer"
4
4
  require "flipper/cloud/configuration"
5
5
  require "flipper/cloud/dsl"
6
6
  require "flipper/cloud/middleware"
7
+ require "flipper/cloud/engine" if defined?(Rails::Engine)
7
8
 
8
9
  module Flipper
9
10
  module Cloud
@@ -14,8 +15,14 @@ module Flipper
14
15
  # options - The Hash of options. See Flipper::Cloud::Configuration.
15
16
  # block - The block that configuration will be yielded to allowing you to
16
17
  # customize this cloud instance and its adapter.
17
- def self.new(token = nil, options = {})
18
- options = options.merge(token: token) if token
18
+ def self.new(options = {}, deprecated_options = {})
19
+ if options.is_a?(String)
20
+ warn "`Flipper::Cloud.new(token)` is deprecated. Use `Flipper::Cloud.new(token: token)` " +
21
+ "or set the `FLIPPER_CLOUD_TOKEN` environment variable.\n" +
22
+ caller[0]
23
+ options = deprecated_options.merge(token: options)
24
+ end
25
+
19
26
  configuration = Configuration.new(options)
20
27
  yield configuration if block_given?
21
28
  DSL.new(configuration)
@@ -36,5 +43,21 @@ module Flipper
36
43
  builder.define_singleton_method(:inspect) { klass.inspect } # pretty rake routes output
37
44
  builder
38
45
  end
46
+
47
+ # Private: Configure Flipper to use Cloud by default
48
+ def self.set_default
49
+ Flipper.configure do |config|
50
+ config.default do
51
+ if ENV["FLIPPER_CLOUD_TOKEN"]
52
+ self.new(local_adapter: config.adapter)
53
+ else
54
+ warn "Missing FLIPPER_CLOUD_TOKEN environment variable. Disabling Flipper::Cloud."
55
+ Flipper.new(config.adapter)
56
+ end
57
+ end
58
+ end
59
+ end
39
60
  end
40
61
  end
62
+
63
+ Flipper::Cloud.set_default
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.21.0.rc1'.freeze
2
+ VERSION = '0.22.1'.freeze
3
3
  end
@@ -0,0 +1,98 @@
1
+ require 'helper'
2
+ require 'rails'
3
+ require 'flipper/cloud'
4
+
5
+ RSpec.describe Flipper::Cloud::Engine do
6
+ let(:env) do
7
+ { "FLIPPER_CLOUD_TOKEN" => "test-token" }
8
+ end
9
+
10
+ let(:application) do
11
+ Class.new(Rails::Application) do
12
+ config.eager_load = false
13
+ config.logger = ActiveSupport::Logger.new($stdout)
14
+ end
15
+ end
16
+
17
+ # App for Rack::Test
18
+ let(:app) { application.routes }
19
+
20
+ before do
21
+ Rails.application = nil
22
+
23
+ # Force loading of flipper to configure itself
24
+ load 'flipper/cloud.rb'
25
+ end
26
+
27
+ it "initializes cloud configuration" do
28
+ stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}")
29
+
30
+ with_modified_env env do
31
+ application.initialize!
32
+
33
+ expect(Flipper.instance).to be_a(Flipper::Cloud::DSL)
34
+ expect(Flipper.instance.instrumenter).to be(ActiveSupport::Notifications)
35
+ end
36
+ end
37
+
38
+ context "with CLOUD_SYNC_SECRET" do
39
+ before do
40
+ env.update "FLIPPER_CLOUD_SYNC_SECRET" => "test-secret"
41
+ end
42
+
43
+ let(:request_body) do
44
+ JSON.generate({
45
+ "environment_id" => 1,
46
+ "webhook_id" => 1,
47
+ "delivery_id" => SecureRandom.uuid,
48
+ "action" => "sync",
49
+ })
50
+ end
51
+ let(:timestamp) { Time.now }
52
+ let(:signature) {
53
+ Flipper::Cloud::MessageVerifier.new(secret: env["FLIPPER_CLOUD_SYNC_SECRET"]).generate(request_body, timestamp)
54
+ }
55
+ let(:signature_header_value) {
56
+ Flipper::Cloud::MessageVerifier.new(secret: "").header(signature, timestamp)
57
+ }
58
+
59
+ it "configures webhook app" do
60
+ with_modified_env env do
61
+ application.initialize!
62
+
63
+ stub = stub_request(:get, "https://www.flippercloud.io/adapter/features").with({
64
+ headers: { "Flipper-Cloud-Token" => ENV["FLIPPER_CLOUD_TOKEN"] },
65
+ }).to_return(status: 200, body: JSON.generate({ features: {} }), headers: {})
66
+
67
+ post "/_flipper", request_body, { "HTTP_FLIPPER_CLOUD_SIGNATURE" => signature_header_value }
68
+
69
+ expect(last_response.status).to eq(200)
70
+ expect(stub).to have_been_requested
71
+ end
72
+ end
73
+ end
74
+
75
+ context "without CLOUD_SYNC_SECRET" do
76
+ it "does not configure webhook app" do
77
+ with_modified_env env do
78
+ application.initialize!
79
+
80
+ post "/_flipper"
81
+ expect(last_response.status).to eq(404)
82
+ end
83
+ end
84
+ end
85
+
86
+ context "without FLIPPER_CLOUD_TOKEN" do
87
+ it "gracefully skips configuring webhook app" do
88
+ with_modified_env "FLIPPER_CLOUD_TOKEN" => nil do
89
+ application.initialize!
90
+ expect(silence { Flipper.instance }).to match(/Missing FLIPPER_CLOUD_TOKEN/)
91
+ expect(Flipper.instance).to be_a(Flipper::DSL)
92
+
93
+ post "/_flipper"
94
+ expect(last_response.status).to eq(404)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -6,14 +6,14 @@ require 'flipper/adapters/operation_logger'
6
6
 
7
7
  RSpec.describe Flipper::Cloud::Middleware do
8
8
  let(:flipper) {
9
- Flipper::Cloud.new("regular") do |config|
9
+ Flipper::Cloud.new(token: "regular") do |config|
10
10
  config.local_adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
11
11
  config.sync_secret = "regular_tasty"
12
12
  end
13
13
  }
14
14
 
15
15
  let(:env_flipper) {
16
- Flipper::Cloud.new("env") do |config|
16
+ Flipper::Cloud.new(token: "env") do |config|
17
17
  config.local_adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
18
18
  config.sync_secret = "env_tasty"
19
19
  end
@@ -12,7 +12,7 @@ RSpec.describe Flipper::Cloud do
12
12
  let(:token) { 'asdf' }
13
13
 
14
14
  before do
15
- @instance = described_class.new(token)
15
+ @instance = described_class.new(token: token)
16
16
  memoized_adapter = @instance.adapter
17
17
  sync_adapter = memoized_adapter.adapter
18
18
  @http_adapter = sync_adapter.instance_variable_get('@remote')
@@ -52,7 +52,7 @@ RSpec.describe Flipper::Cloud do
52
52
  before do
53
53
  stub_request(:get, /fakeflipper\.com/).to_return(status: 200, body: "{}")
54
54
 
55
- @instance = described_class.new('asdf', url: 'https://www.fakeflipper.com/sadpanda')
55
+ @instance = described_class.new(token: 'asdf', url: 'https://www.fakeflipper.com/sadpanda')
56
56
  memoized_adapter = @instance.adapter
57
57
  sync_adapter = memoized_adapter.adapter
58
58
  @http_adapter = sync_adapter.instance_variable_get('@remote')
@@ -75,12 +75,12 @@ RSpec.describe Flipper::Cloud do
75
75
 
76
76
  it 'can set instrumenter' do
77
77
  instrumenter = Flipper::Instrumenters::Memory.new
78
- instance = described_class.new('asdf', instrumenter: instrumenter)
78
+ instance = described_class.new(token: 'asdf', instrumenter: instrumenter)
79
79
  expect(instance.instrumenter).to be(instrumenter)
80
80
  end
81
81
 
82
82
  it 'allows wrapping adapter with another adapter like the instrumenter' do
83
- instance = described_class.new('asdf') do |config|
83
+ instance = described_class.new(token: 'asdf') do |config|
84
84
  config.adapter do |adapter|
85
85
  Flipper::Adapters::Instrumented.new(adapter)
86
86
  end
@@ -92,26 +92,26 @@ RSpec.describe Flipper::Cloud do
92
92
  it 'can set debug_output' do
93
93
  expect(Flipper::Adapters::Http::Client).to receive(:new)
94
94
  .with(hash_including(debug_output: STDOUT))
95
- described_class.new('asdf', debug_output: STDOUT)
95
+ described_class.new(token: 'asdf', debug_output: STDOUT)
96
96
  end
97
97
 
98
98
  it 'can set read_timeout' do
99
99
  expect(Flipper::Adapters::Http::Client).to receive(:new)
100
100
  .with(hash_including(read_timeout: 1))
101
- described_class.new('asdf', read_timeout: 1)
101
+ described_class.new(token: 'asdf', read_timeout: 1)
102
102
  end
103
103
 
104
104
  it 'can set open_timeout' do
105
105
  expect(Flipper::Adapters::Http::Client).to receive(:new)
106
106
  .with(hash_including(open_timeout: 1))
107
- described_class.new('asdf', open_timeout: 1)
107
+ described_class.new(token: 'asdf', open_timeout: 1)
108
108
  end
109
109
 
110
110
  if RUBY_VERSION >= '2.6.0'
111
111
  it 'can set write_timeout' do
112
112
  expect(Flipper::Adapters::Http::Client).to receive(:new)
113
113
  .with(hash_including(open_timeout: 1))
114
- described_class.new('asdf', open_timeout: 1)
114
+ described_class.new(token: 'asdf', open_timeout: 1)
115
115
  end
116
116
  end
117
117
 
@@ -129,7 +129,7 @@ RSpec.describe Flipper::Cloud do
129
129
  flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
130
130
  flipper.enable_percentage_of_time(:logging, 5)
131
131
 
132
- cloud_flipper = Flipper::Cloud.new("asdf")
132
+ cloud_flipper = Flipper::Cloud.new(token: "asdf")
133
133
 
134
134
  get_all = {
135
135
  "logging" => {actors: Set.new, boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: "5"},
@@ -158,7 +158,7 @@ RSpec.describe Flipper::Cloud do
158
158
  flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
159
159
  flipper.enable_percentage_of_time(:logging, 5)
160
160
 
161
- cloud_flipper = Flipper::Cloud.new("asdf")
161
+ cloud_flipper = Flipper::Cloud.new(token: "asdf")
162
162
 
163
163
  get_all = {
164
164
  "logging" => {actors: Set.new, boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: "5"},
@@ -186,7 +186,7 @@ RSpec.describe Flipper::Cloud do
186
186
  flipper.enable_actor(:stats, Flipper::Actor.new("jnunemaker"))
187
187
  flipper.enable_percentage_of_time(:logging, 5)
188
188
 
189
- cloud_flipper = Flipper::Cloud.new("asdf")
189
+ cloud_flipper = Flipper::Cloud.new(token: "asdf")
190
190
 
191
191
  get_all = {
192
192
  "logging" => {actors: Set.new, boolean: nil, groups: Set.new, percentage_of_actors: nil, percentage_of_time: "5"},
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0.rc1
4
+ version: 0.22.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: 2021-05-01 00:00:00.000000000 Z
11
+ date: 2021-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flipper
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.21.0.rc1
19
+ version: 0.22.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.21.0.rc1
26
+ version: 0.22.1
27
27
  description:
28
28
  email:
29
29
  - nunemaker@gmail.com
@@ -34,19 +34,20 @@ files:
34
34
  - docs/images/flipper_cloud.png
35
35
  - examples/cloud/app.ru
36
36
  - examples/cloud/basic.rb
37
- - examples/cloud/cached_in_memory.rb
38
37
  - examples/cloud/import.rb
39
- - examples/cloud/local_adapter.rb
40
38
  - flipper-cloud.gemspec
41
39
  - lib/flipper-cloud.rb
42
40
  - lib/flipper/cloud.rb
43
41
  - lib/flipper/cloud/configuration.rb
44
42
  - lib/flipper/cloud/dsl.rb
43
+ - lib/flipper/cloud/engine.rb
45
44
  - lib/flipper/cloud/message_verifier.rb
46
45
  - lib/flipper/cloud/middleware.rb
46
+ - lib/flipper/cloud/routes.rb
47
47
  - lib/flipper/version.rb
48
48
  - spec/flipper/cloud/configuration_spec.rb
49
49
  - spec/flipper/cloud/dsl_spec.rb
50
+ - spec/flipper/cloud/engine_spec.rb
50
51
  - spec/flipper/cloud/message_verifier_spec.rb
51
52
  - spec/flipper/cloud/middleware_spec.rb
52
53
  - spec/flipper/cloud_spec.rb
@@ -66,17 +67,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
66
67
  version: '0'
67
68
  required_rubygems_version: !ruby/object:Gem::Requirement
68
69
  requirements:
69
- - - ">"
70
+ - - ">="
70
71
  - !ruby/object:Gem::Version
71
- version: 1.3.1
72
+ version: '0'
72
73
  requirements: []
73
74
  rubygems_version: 3.0.3
74
75
  signing_key:
75
76
  specification_version: 4
76
- summary: FeatureFlipper.com adapter for Flipper
77
+ summary: FlipperCloud.io adapter for Flipper
77
78
  test_files:
78
79
  - spec/flipper/cloud/configuration_spec.rb
79
80
  - spec/flipper/cloud/dsl_spec.rb
81
+ - spec/flipper/cloud/engine_spec.rb
80
82
  - spec/flipper/cloud/message_verifier_spec.rb
81
83
  - spec/flipper/cloud/middleware_spec.rb
82
84
  - spec/flipper/cloud_spec.rb
@@ -1,28 +0,0 @@
1
- # env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/cached_in_memory.rb
2
- require 'bundler/setup'
3
- require 'flipper/cloud'
4
- require 'flipper/adapters/active_support_cache_store'
5
- require 'active_support/cache'
6
- require 'active_support/cache/memory_store'
7
-
8
- feature_name = ENV.fetch("FEATURE") { "testing" }.to_sym
9
-
10
- Flipper.configure do |config|
11
- config.default do
12
- Flipper::Cloud.new do |cloud|
13
- cloud.debug_output = STDOUT
14
- cloud.adapter do |adapter|
15
- Flipper::Adapters::ActiveSupportCacheStore.new(adapter,
16
- ActiveSupport::Cache::MemoryStore.new, {expires_in: 5.seconds})
17
- end
18
- end
19
- end
20
- end
21
-
22
- loop do
23
- # Should only print out http call every 5 seconds
24
- p Flipper.enabled?(feature_name)
25
- puts "\n\n"
26
-
27
- sleep 1
28
- end
@@ -1,35 +0,0 @@
1
- # This is an example of using cloud with a local adapter. All cloud feature
2
- # changes are synced to the local adapter on an interval. All feature reads are
3
- # directed to the local adapter, which means reads are fast and not dependent on
4
- # cloud being available. You can turn internet on/off and more and this should
5
- # never raise. You could get a slow request every now and then if cloud is
6
- # unavailable, but we are hoping to fix that soon by doing the cloud update in a
7
- # background thread.
8
- # env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/local_adapter.rb
9
- require 'bundler/setup'
10
- require 'logger'
11
- require 'flipper/cloud'
12
- require 'flipper/adapters/redis'
13
-
14
- feature_name = ENV.fetch("FEATURE") { "testing" }.to_sym
15
-
16
- redis = Redis.new(logger: Logger.new(STDOUT))
17
- redis.flushdb
18
-
19
- Flipper.configure do |config|
20
- config.default do
21
- Flipper::Cloud.new do |cloud|
22
- cloud.debug_output = STDOUT
23
- cloud.local_adapter = Flipper::Adapters::Redis.new(redis)
24
- cloud.sync_interval = 10
25
- end
26
- end
27
- end
28
-
29
- loop do
30
- # Should only print out http call every 10 seconds
31
- p Flipper.enabled?(feature_name)
32
- puts "\n\n"
33
-
34
- sleep 1
35
- end