bandiera-client 2.2.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.hound.yml +3 -0
- data/.rubocop.yml +3 -0
- data/.travis.yml +20 -0
- data/README.md +57 -47
- data/bandiera-client.gemspec +2 -2
- data/lib/bandiera/client/version.rb +1 -1
- data/lib/bandiera/client.rb +55 -53
- data/lib/bandiera/middleware.rb +66 -0
- data/spec/lib/bandiera/client_spec.rb +2 -185
- data/spec/lib/bandiera/middleware_spec.rb +184 -0
- data/spec/spec_helper.rb +1 -0
- metadata +12 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 170478dc6075aeda3c245c9ea8ef1f4fd357a556
|
4
|
+
data.tar.gz: a5bda6c22f5c52fceb82d665009a9c3323940948
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f274fff7ed02c4bcbc18324c7c1c0ea7eea83213b051f8c5ba859f89a1c21dbe25e6b310a95bd6448cd8b4aee0165dd0650b6a6a31d2e05fb4fa755866075034
|
7
|
+
data.tar.gz: 85585452a8af8119f8d12fd3d2a610ae0d4ecb10eaae61ccac7fd7782ac17a24744d641b0339e725053f03280a867e000d3d5567812cab8b48431655288ed72e
|
data/.hound.yml
CHANGED
data/.rubocop.yml
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
sudo: false
|
2
|
+
cache: bundler
|
3
|
+
language: ruby
|
4
|
+
|
5
|
+
# Ruby build matrix
|
6
|
+
rvm:
|
7
|
+
- 2.0
|
8
|
+
- 2.1
|
9
|
+
- 2.2
|
10
|
+
- ruby
|
11
|
+
- ruby-head
|
12
|
+
- jruby
|
13
|
+
|
14
|
+
# Ensure we don't build for *every* commit (doesn't apply to PR builds)
|
15
|
+
branches:
|
16
|
+
only:
|
17
|
+
- master
|
18
|
+
|
19
|
+
script:
|
20
|
+
- bundle exec rspec --format documentation
|
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# Bandiera::Client (Ruby)
|
2
2
|
|
3
|
-
This is a client for talking to the [Bandiera][bandiera] feature flagging
|
4
|
-
service from a Ruby application.
|
3
|
+
This is a client for talking to the [Bandiera][bandiera] feature flagging service from a Ruby application.
|
5
4
|
|
6
5
|
This client is compatible with the [v2 Bandiera API][bandiera-api].
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
[![Gem version][shield-gem]][info-gem]
|
8
|
+
[![Build status][shield-build]][info-build]
|
9
|
+
[![Dependencies][shield-dependencies]][info-dependencies]
|
10
|
+
[![MIT licensed][shield-license]][info-license]
|
11
11
|
|
12
12
|
## Ruby Support:
|
13
13
|
|
@@ -26,72 +26,76 @@ Then interact with a Bandiera server like so:
|
|
26
26
|
```ruby
|
27
27
|
require 'bandiera/client'
|
28
28
|
|
29
|
-
|
29
|
+
client = Bandiera::Client.new('http://bandiera-demo.herokuapp.com')
|
30
|
+
params = {}
|
30
31
|
|
31
|
-
if
|
32
|
+
if client.enabled?('pubserv', 'show-new-search', params)
|
32
33
|
# show the new experimental search function
|
33
34
|
end
|
34
35
|
```
|
35
36
|
|
36
|
-
The
|
37
|
-
and the 'feature name'. This is because in Bandiera, features are organised
|
38
|
-
in groups as it is intented as a service for multiple applications to use at
|
39
|
-
the same time - this organisation allows separation of feature flags that are
|
40
|
-
intended for different audiences.
|
37
|
+
The `client.enabled?` command takes two main arguments - the 'feature group', and the 'feature name'. This is because in Bandiera, features are organised into groups as it is intented as a service for multiple applications to use at the same time - this organisation allows separation of feature flags that are intended for different audiences.
|
41
38
|
|
42
|
-
|
39
|
+
`client.enabled?` also takes an optional `params` hash, this is for use with some of the more advanced features in Bandiera - user group and percentage based flags. It is in this params hash you pass in your `user_group` and `user_id`, i.e.:
|
43
40
|
|
44
|
-
|
41
|
+
```ruby
|
42
|
+
client.enabled?('pubserv', 'show-new-search',
|
43
|
+
{ user_id: '1234567', user_group: 'Administrators' })
|
44
|
+
```
|
45
45
|
|
46
|
-
|
47
|
-
2. Make things faster
|
46
|
+
For more information on these advanced features, please see the Bandiera wiki:
|
48
47
|
|
49
|
-
|
48
|
+
https://github.com/nature/bandiera/wiki/How-Feature-Flags-Work#feature-flags-in-bandiera
|
50
49
|
|
51
|
-
|
50
|
+
# Performance
|
52
51
|
|
53
|
-
|
52
|
+
Using the `client.enabled?` method all over your codebase isn't the most efficient way of working with Bandiera as every time you call `enabled?` you will make a HTTP request to the Bandiera server.
|
54
53
|
|
55
|
-
|
56
|
-
the response in it's local cache. Subsequent `.enabled?` requests (using the
|
57
|
-
same arguments) will read from the cache until it has expired. This is the
|
58
|
-
**least** efficient strategy in terms of reducing HTTP requests and speed.
|
54
|
+
One way of working more efficiently is using the [Direct API Access](#direct-api-access) methods, to fetch all the feature flags for a given group (or even **all** of the feature flags in the Bandiera server) in one request. You can then hold these as you please in your application and call on the values when needed.
|
59
55
|
|
60
|
-
|
56
|
+
Another approach is to use the Bandiera::Middleware class supplied in this gem. This can be used in conjunction with other middlewares for identifying your currently logged in user and assigning them a UUID to enable all of the most advanced features in Bandiera very simply.
|
61
57
|
|
62
|
-
|
63
|
-
requested, and then stores the resulting feature flag values in the cache.
|
64
|
-
This means that all subsequent calls to `.enabled?` for the same group will not
|
65
|
-
perform a HTTP request and instead read from the cache until it has expired.
|
66
|
-
This is a good compromise in terms of speed and number of HTTP requests, **and
|
67
|
-
is the default caching strategy**.
|
58
|
+
[See the blog post on cruft.io for more information on how Bandiera::Client is used at Nature.](http://cruft.io)
|
68
59
|
|
69
|
-
|
60
|
+
<a name="direct-api-access"></a># Direct API Access
|
70
61
|
|
71
|
-
|
72
|
-
flag values locally. All subsequent calls to `.enabled?` read from the cache
|
73
|
-
until it has expired. This strategy is obviously the most efficient in terms of
|
74
|
-
the number of HTTP requests if you are requesting flags from across multiple
|
75
|
-
groups, but might not be the fastest if there are **lots** of flags in your
|
76
|
-
Bandiera instance.
|
62
|
+
If you'd prefer not to use the `enabled?` method for featching feature flag values, the following methods are available...
|
77
63
|
|
78
|
-
|
64
|
+
Get features for all groups:
|
79
65
|
|
80
66
|
```ruby
|
81
|
-
|
82
|
-
|
67
|
+
client.get_all(params)
|
68
|
+
# gives:
|
69
|
+
# {
|
70
|
+
# 'group1' => {
|
71
|
+
# 'feature1' => true,
|
72
|
+
# 'feature2' => false
|
73
|
+
# },
|
74
|
+
# 'group2' => {
|
75
|
+
# 'feature1' => false
|
76
|
+
# },
|
77
|
+
# }
|
83
78
|
```
|
84
79
|
|
85
|
-
|
80
|
+
Get features for a group:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
client.get_features_for_group('pubserv', params)
|
84
|
+
# gives:
|
85
|
+
# {
|
86
|
+
# 'feature1' => true,
|
87
|
+
# 'feature2' => false
|
88
|
+
# }
|
89
|
+
```
|
86
90
|
|
87
|
-
|
88
|
-
can do so as follows:
|
91
|
+
Get an individual feature:
|
89
92
|
|
90
93
|
```ruby
|
91
|
-
|
92
|
-
|
94
|
+
client.get_feature('pubserv', 'show-article-metrics', params)
|
95
|
+
# gives: true/false
|
93
96
|
```
|
94
97
|
|
98
|
+
As with the `enabled?` method the `params` hash is for passing in your `user_group` and `user_id` values if you are using some of the more advanced features in Bandiera.
|
95
99
|
|
96
100
|
# Development
|
97
101
|
|
@@ -107,5 +111,11 @@ Bandiera::Client (Ruby) is licensed under the [MIT License][mit].
|
|
107
111
|
[mit]: http://opensource.org/licenses/mit-license.php
|
108
112
|
[bandiera]: https://github.com/nature/bandiera
|
109
113
|
[bandiera-api]: https://github.com/nature/bandiera/wiki/API-Documentation
|
110
|
-
[
|
111
|
-
[
|
114
|
+
[info-dependencies]: https://gemnasium.com/nature/bandiera-client-ruby
|
115
|
+
[info-license]: LICENSE
|
116
|
+
[info-gem]: https://rubygems.org/gems/bandiera-client
|
117
|
+
[info-build]: https://travis-ci.org/nature/bandiera-client-ruby
|
118
|
+
[shield-dependencies]: https://img.shields.io/gemnasium/nature/bandiera-client-ruby.svg
|
119
|
+
[shield-license]: https://img.shields.io/badge/license-MIT-blue.svg
|
120
|
+
[shield-gem]: https://img.shields.io/gem/v/bandiera-client.svg
|
121
|
+
[shield-build]: https://img.shields.io/travis/nature/bandiera-client-ruby/master.svg
|
data/bandiera-client.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.description = 'Bandiera is a simple, stand-alone feature flagging service that is not tied to any existing web framework or language. This is a client for talking to the web service.'
|
14
14
|
spec.summary = 'Simple feature flagging API client.'
|
15
15
|
spec.homepage = 'https://github.com/nature/bandiera-client-ruby'
|
16
|
-
spec.license = '
|
16
|
+
spec.license = 'MIT'
|
17
17
|
|
18
18
|
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
19
|
spec.test_files = Dir.glob('spec/*_spec.rb')
|
@@ -22,10 +22,10 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency 'bundler'
|
23
23
|
spec.add_development_dependency 'rspec'
|
24
24
|
spec.add_development_dependency 'webmock'
|
25
|
+
spec.add_development_dependency 'rack-test'
|
25
26
|
spec.add_development_dependency 'rake'
|
26
27
|
spec.add_development_dependency 'pry'
|
27
28
|
|
28
29
|
spec.add_dependency 'rest-client'
|
29
|
-
spec.add_dependency 'moneta'
|
30
30
|
spec.add_dependency 'macmillan-utils'
|
31
31
|
end
|
data/lib/bandiera/client.rb
CHANGED
@@ -1,49 +1,54 @@
|
|
1
1
|
require 'rest_client'
|
2
2
|
require 'json'
|
3
3
|
require 'logger'
|
4
|
-
require 'moneta'
|
5
4
|
|
6
5
|
module Bandiera
|
6
|
+
##
|
7
|
+
# Client class for communicating with a Bandiera server.
|
8
|
+
#
|
9
|
+
# @since 1.0.0
|
10
|
+
# @attr [Float] timeout The HTTP timeout value (seconds) for requests
|
11
|
+
# @attr [String] client_name The client name passed along with HTTP requests
|
12
|
+
# @attr_reader [Logger] logger The logger object in use
|
13
|
+
#
|
7
14
|
class Client
|
8
15
|
autoload :VERSION, 'bandiera/client/version'
|
9
16
|
|
10
|
-
|
11
|
-
|
12
|
-
attr_accessor :timeout, :client_name, :cache_ttl
|
13
|
-
attr_reader :logger, :cache, :cache_strategy
|
17
|
+
attr_accessor :timeout, :client_name
|
18
|
+
attr_reader :logger
|
14
19
|
|
20
|
+
##
|
21
|
+
# Builds a new instance of Bandiera::Client
|
22
|
+
#
|
23
|
+
# @param [String] base_uri The URI of the Bandiera server
|
24
|
+
# @param [Logger] logger A logger object
|
25
|
+
# @param [String] client_name A client name to pass through along with the HTTP requests
|
26
|
+
#
|
15
27
|
def initialize(base_uri = 'http://localhost', logger = Logger.new($stdout), client_name = nil)
|
16
28
|
@base_uri = base_uri
|
17
29
|
@base_uri << '/api' unless @base_uri.match(/\/api$/)
|
18
30
|
@logger = logger
|
19
31
|
@timeout = 0.2 # 0.4s (0.2 + 0.2) default timeout
|
20
32
|
@client_name = client_name
|
21
|
-
@cache = Moneta.new(:LRUHash, expires: true)
|
22
|
-
@cache_ttl = 5 # 5 seconds
|
23
|
-
@cache_strategy = :group
|
24
33
|
end
|
25
34
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
@cache_strategy = strategy
|
31
|
-
end
|
32
|
-
|
33
|
-
def enabled?(group, feature, params = {}, http_opts = {})
|
34
|
-
cache_key = build_cache_key(group, feature, params)
|
35
|
-
|
36
|
-
unless cache.key?(cache_key)
|
37
|
-
case cache_strategy
|
38
|
-
when :single_feature then get_feature(group, feature, params, http_opts)
|
39
|
-
when :group then get_features_for_group(group, params, http_opts)
|
40
|
-
when :all then get_all
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
cache.fetch(cache_key)
|
35
|
+
# @deprecated This functionality was deprecated/removed in 3.0.0
|
36
|
+
def cache_strategy=(_)
|
37
|
+
warn 'The caching features in Bandiera::Client have been removed as of v3.0.0, please consider using using ' \
|
38
|
+
'the Bandiera::Middleware class shipped as part of the "bandiera-client" gem.'
|
45
39
|
end
|
46
40
|
|
41
|
+
##
|
42
|
+
# Get the active/inactive state for a single feature flag
|
43
|
+
#
|
44
|
+
# @param [String] group The group of feature flags we're interested in
|
45
|
+
# @param [String] feature The feature flag we want to retrieve
|
46
|
+
# @param [Hash] params Additional parameters to pass through to the Bandiera request
|
47
|
+
# @option params [String] :user_id A unique user identifier, or UUID (for use with percentage based feature flags)
|
48
|
+
# @option params [String] :user_group A group to assign the identify the user with (for use with group based feature flags)
|
49
|
+
#
|
50
|
+
# @return [Boolean] True/False - depending on if the feature is on or off
|
51
|
+
#
|
47
52
|
def get_feature(group, feature, params = {}, http_opts = {})
|
48
53
|
path = "/v2/groups/#{group}/features/#{feature}"
|
49
54
|
default_response = false
|
@@ -51,11 +56,21 @@ module Bandiera
|
|
51
56
|
|
52
57
|
logger.debug "[Bandiera::Client#get_feature] calling #{path} with params: #{params}"
|
53
58
|
|
54
|
-
get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
|
55
|
-
store_value_in_cache(group, feature, params, value)
|
56
|
-
end
|
59
|
+
get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
|
57
60
|
end
|
58
61
|
|
62
|
+
alias_method :enabled?, :get_feature
|
63
|
+
|
64
|
+
##
|
65
|
+
# Get the active/inactive state for all feature flags in a group
|
66
|
+
#
|
67
|
+
# @param [String] group The group of feature flags we're interested in
|
68
|
+
# @param [Hash] params Additional parameters to pass through to the Bandiera request
|
69
|
+
# @option params [String] :user_id A unique user identifier, or UUID (for use with percentage based feature flags)
|
70
|
+
# @option params [String] :user_group A group to assign the identify the user with (for use with group based feature flags)
|
71
|
+
#
|
72
|
+
# @return [Hash] A hash of feature flag pairs. Keys are the feature flag names, values are the active/inactive states.
|
73
|
+
#
|
59
74
|
def get_features_for_group(group, params = {}, http_opts = {})
|
60
75
|
path = "/v2/groups/#{group}/features"
|
61
76
|
default_response = {}
|
@@ -63,11 +78,17 @@ module Bandiera
|
|
63
78
|
|
64
79
|
logger.debug "[Bandiera::Client#get_features_for_group] calling #{path} with params: #{params}"
|
65
80
|
|
66
|
-
get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
|
67
|
-
store_feature_hash_in_cache(group, params, feature_hash)
|
68
|
-
end
|
81
|
+
get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
|
69
82
|
end
|
70
83
|
|
84
|
+
##
|
85
|
+
# Get the active/inactive state for all feature flags known on the Bandiera server
|
86
|
+
#
|
87
|
+
# @param [Hash] params Additional parameters to pass through to the Bandiera request
|
88
|
+
# @option params [String] :user_id A unique user identifier, or UUID (for use with percentage based feature flags)
|
89
|
+
# @option params [String] :user_group A group to assign the identify the user with (for use with group based feature flags)
|
90
|
+
#
|
91
|
+
# @return [Hash] A hash of hashes containing the feature flag active/inactive states grouped by 'group'
|
71
92
|
def get_all(params = {}, http_opts = {})
|
72
93
|
path = '/v2/all'
|
73
94
|
default_response = {}
|
@@ -75,30 +96,11 @@ module Bandiera
|
|
75
96
|
|
76
97
|
logger.debug "[Bandiera::Client#get_all] calling #{path} with params: #{params}"
|
77
98
|
|
78
|
-
get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
|
79
|
-
group_hash.each do |group, feature_hash|
|
80
|
-
store_feature_hash_in_cache(group, params, feature_hash)
|
81
|
-
end
|
82
|
-
end
|
99
|
+
get_and_handle_exceptions(path, params, http_opts, default_response, error_msg_prefix)
|
83
100
|
end
|
84
101
|
|
85
102
|
private
|
86
103
|
|
87
|
-
def store_feature_hash_in_cache(group, params, feature_hash)
|
88
|
-
feature_hash.each do |feature, value|
|
89
|
-
store_value_in_cache(group, feature, params, value)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
def store_value_in_cache(group, feature, params, value)
|
94
|
-
cache_key = build_cache_key(group, feature, params)
|
95
|
-
cache.store(cache_key, value, expires: cache_ttl)
|
96
|
-
end
|
97
|
-
|
98
|
-
def build_cache_key(group, feature, params)
|
99
|
-
"#{group} / #{feature} / #{params}"
|
100
|
-
end
|
101
|
-
|
102
104
|
def headers
|
103
105
|
headers = { 'User-Agent' => "Bandiera Ruby Client / #{Bandiera::Client::VERSION}" }
|
104
106
|
headers.merge!('Bandiera-Client' => client_name) unless client_name.nil?
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'macmillan/utils'
|
2
|
+
|
3
|
+
module Bandiera
|
4
|
+
##
|
5
|
+
# Rack middleware for talking to a Bandiera feature flagging service.
|
6
|
+
#
|
7
|
+
# The benefit of using this middleware is two-fold when working with a Bandiera server:
|
8
|
+
#
|
9
|
+
# 1. Performance - Using this middleware, you will only call the Bandiera server once per-request (in contrast to using the `enabled?` method where you will make one request per-use).
|
10
|
+
# 2. Better Quality Code - If you use the more advanced features in Bandiera (user groups and percentages) you will no longer need to pass around user objects and UUIDs in your code. This does assume the use of other middlewares to supply user objects and UUIDs though.
|
11
|
+
#
|
12
|
+
# This middleware can be used in conjunction with the {Macmillan::Utils::Middleware::Uuid} (or one of your own design) to automatically generate UUIDs for your users. See https://github.com/nature/bandiera/wiki for more information on this approach.
|
13
|
+
#
|
14
|
+
# @since 3.0.0
|
15
|
+
#
|
16
|
+
class Middleware
|
17
|
+
##
|
18
|
+
# Builds a new instance of Bandiera::Middleware
|
19
|
+
#
|
20
|
+
# @param app The rack application
|
21
|
+
# @param [Hash] opts Additional options for the middleware
|
22
|
+
# @option opts [Bandiera::Client] :client The Bandiera::Client class to use
|
23
|
+
# @option opts [Array] :groups ([]) The feature flag groups to request and store in the rack env
|
24
|
+
# @option opts [String] :uuid_env_key (Macmillan::Utils::Middleware::Uuid.env_key) The rack env key containing a UUID for your current user (to pass to `user_id` in Bandiera)
|
25
|
+
# @option opts [String] :user_env_key ('current_user') The rack env key containing your currently logged in user
|
26
|
+
# @option opts [Symbol] :user_group_method (:email) The method to call on the current user to pass to `user_group` in Bandiera
|
27
|
+
#
|
28
|
+
# @see Bandiera::Client
|
29
|
+
# @see Macmillan::Utils::Middleware::Uuid
|
30
|
+
#
|
31
|
+
def initialize(app, opts = {})
|
32
|
+
@app = app
|
33
|
+
@client = opts[:client]
|
34
|
+
@groups = opts[:groups] || []
|
35
|
+
@uuid_env_key = opts[:uuid_env_key] || Macmillan::Utils::Middleware::Uuid.env_key
|
36
|
+
@user_env_key = opts[:user_env_key] || 'current_user'
|
37
|
+
@user_group_method = opts[:user_group_method] || :email
|
38
|
+
|
39
|
+
fail ArgumentError, 'You must supply a Bandiera::Client' unless @client
|
40
|
+
end
|
41
|
+
|
42
|
+
def call(env)
|
43
|
+
dup.process(env)
|
44
|
+
end
|
45
|
+
|
46
|
+
def process(env)
|
47
|
+
request = Rack::Request.new(env)
|
48
|
+
|
49
|
+
user = request.env[@user_env_key]
|
50
|
+
user_group = user ? user.public_send(@user_group_method) : nil
|
51
|
+
uuid = request.env[@uuid_env_key]
|
52
|
+
|
53
|
+
if @groups.empty?
|
54
|
+
@client.get_all(user_group: user_group, user_id: uuid).each do |group, flags|
|
55
|
+
request.env["bandiera.#{group}"] = flags
|
56
|
+
end
|
57
|
+
else
|
58
|
+
@groups.each do |group|
|
59
|
+
request.env["bandiera.#{group}"] = @client.get_features_for_group(group, user_group: user_group, user_id: uuid)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@app.call(request.env)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -9,10 +9,10 @@ describe Bandiera::Client do
|
|
9
9
|
context 'when a client name is provided' do
|
10
10
|
let(:group) { 'pubserv' }
|
11
11
|
let(:feature) { 'log-stats' }
|
12
|
-
let(:url) { "#{api_uri}/v2/groups/#{group}/features" }
|
12
|
+
let(:url) { "#{api_uri}/v2/groups/#{group}/features/#{feature}" }
|
13
13
|
|
14
14
|
it 'sends it as part of the headers' do
|
15
|
-
stub
|
15
|
+
stub = stub_api_request(url, { 'response' => {} }, { 'Bandiera-Client' => 'asdf' })
|
16
16
|
client = Bandiera::Client.new(api_uri, logger, 'asdf')
|
17
17
|
client.enabled?(group, feature)
|
18
18
|
expect(stub).to have_been_requested
|
@@ -36,83 +36,6 @@ describe Bandiera::Client do
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
describe '#enabled?' do
|
40
|
-
context 'with cache strategy :single_feature' do
|
41
|
-
before do
|
42
|
-
subject.cache_strategy = :single_feature
|
43
|
-
end
|
44
|
-
|
45
|
-
context 'and an empty cache' do
|
46
|
-
it 'calls #get_feature' do
|
47
|
-
expect(subject).to receive(:get_feature).once
|
48
|
-
subject.enabled?('foo', 'bar')
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'and a primed cache' do
|
53
|
-
before do
|
54
|
-
cache_key = subject.send(:build_cache_key, 'foo', 'bar', {})
|
55
|
-
subject.cache.store(cache_key, false)
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'does not call #get_feature' do
|
59
|
-
expect(subject).to_not receive(:get_feature)
|
60
|
-
subject.enabled?('foo', 'bar')
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context 'with cache strategy :group' do
|
66
|
-
before do
|
67
|
-
subject.cache_strategy = :group
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'and an empty cache' do
|
71
|
-
it 'calls #get_features_for_group' do
|
72
|
-
expect(subject).to receive(:get_features_for_group).once
|
73
|
-
subject.enabled?('foo', 'bar')
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context 'and a primed cache' do
|
78
|
-
before do
|
79
|
-
cache_key = subject.send(:build_cache_key, 'foo', 'bar', {})
|
80
|
-
subject.cache.store(cache_key, false)
|
81
|
-
end
|
82
|
-
|
83
|
-
it 'does not call #get_features_for_group' do
|
84
|
-
expect(subject).to_not receive(:get_features_for_group)
|
85
|
-
subject.enabled?('foo', 'bar')
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'with cache strategy :all' do
|
91
|
-
before do
|
92
|
-
subject.cache_strategy = :all
|
93
|
-
end
|
94
|
-
|
95
|
-
context 'and an empty cache' do
|
96
|
-
it 'calls #get_all' do
|
97
|
-
expect(subject).to receive(:get_all).once
|
98
|
-
subject.enabled?('foo', 'bar')
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
context 'and a primed cache' do
|
103
|
-
before do
|
104
|
-
cache_key = subject.send(:build_cache_key, 'foo', 'bar', {})
|
105
|
-
subject.cache.store(cache_key, false)
|
106
|
-
end
|
107
|
-
|
108
|
-
it 'does not call #get_all' do
|
109
|
-
expect(subject).to_not receive(:get_all)
|
110
|
-
subject.enabled?('foo', 'bar')
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
39
|
describe '#get_feature' do
|
117
40
|
let(:group) { 'pubserv' }
|
118
41
|
let(:feature) { 'log-stats' }
|
@@ -127,14 +50,6 @@ describe Bandiera::Client do
|
|
127
50
|
expect(stub).to have_been_requested
|
128
51
|
end
|
129
52
|
|
130
|
-
it 'stores the feature value in the cache' do
|
131
|
-
stub_api_request(url, 'response' => true)
|
132
|
-
cache_key = subject.send(:build_cache_key, group, feature, {})
|
133
|
-
|
134
|
-
subject.get_feature(group, feature)
|
135
|
-
expect(subject.cache.key?(cache_key)).to be true
|
136
|
-
end
|
137
|
-
|
138
53
|
context 'and the user has passed through some extra params' do
|
139
54
|
it 'passes them through to the API' do
|
140
55
|
stub = stub_request(:get, url)
|
@@ -174,14 +89,6 @@ describe Bandiera::Client do
|
|
174
89
|
|
175
90
|
expect(response).to be false
|
176
91
|
end
|
177
|
-
|
178
|
-
it 'does not store anything in the cache' do
|
179
|
-
stub_request(:get, url).to_return(status: [0, ''])
|
180
|
-
|
181
|
-
expect(subject.cache).to_not receive(:store)
|
182
|
-
|
183
|
-
subject.get_feature(group, feature)
|
184
|
-
end
|
185
92
|
end
|
186
93
|
|
187
94
|
context 'bandiera is having some problems' do
|
@@ -194,14 +101,6 @@ describe Bandiera::Client do
|
|
194
101
|
|
195
102
|
expect(response).to be false
|
196
103
|
end
|
197
|
-
|
198
|
-
it 'does not store anything in the cache' do
|
199
|
-
stub_request(:get, url).to_return(status: 500, body: '')
|
200
|
-
|
201
|
-
expect(subject.cache).to_not receive(:store)
|
202
|
-
|
203
|
-
subject.get_feature(group, feature)
|
204
|
-
end
|
205
104
|
end
|
206
105
|
|
207
106
|
context 'bandiera times out' do
|
@@ -214,14 +113,6 @@ describe Bandiera::Client do
|
|
214
113
|
|
215
114
|
expect(response).to be false
|
216
115
|
end
|
217
|
-
|
218
|
-
it 'does not store anything in the cache' do
|
219
|
-
stub_request(:get, url).to_timeout
|
220
|
-
|
221
|
-
expect(subject.cache).to_not receive(:store)
|
222
|
-
|
223
|
-
subject.get_feature(group, feature)
|
224
|
-
end
|
225
116
|
end
|
226
117
|
end
|
227
118
|
|
@@ -239,19 +130,6 @@ describe Bandiera::Client do
|
|
239
130
|
expect(stub).to have_been_requested
|
240
131
|
end
|
241
132
|
|
242
|
-
it 'stores the feature values in the cache' do
|
243
|
-
feature_hash = { 'show-stuff' => true, 'show-other-stuff' => false }
|
244
|
-
stub_api_request(url, 'response' => feature_hash)
|
245
|
-
|
246
|
-
subject.get_features_for_group(group)
|
247
|
-
|
248
|
-
cache_key = subject.send(:build_cache_key, group, 'show-stuff', {})
|
249
|
-
expect(subject.cache.key?(cache_key)).to be true
|
250
|
-
|
251
|
-
cache_key = subject.send(:build_cache_key, group, 'show-other-stuff', {})
|
252
|
-
expect(subject.cache.key?(cache_key)).to be true
|
253
|
-
end
|
254
|
-
|
255
133
|
context 'and the user has passed through some extra params' do
|
256
134
|
it 'passes them through to the API' do
|
257
135
|
stub = stub_request(:get, url)
|
@@ -291,14 +169,6 @@ describe Bandiera::Client do
|
|
291
169
|
|
292
170
|
expect(response).to be {}
|
293
171
|
end
|
294
|
-
|
295
|
-
it 'does not store anything in the cache' do
|
296
|
-
stub_request(:get, url).to_return(status: [0, ''])
|
297
|
-
|
298
|
-
expect(subject.cache).to_not receive(:store)
|
299
|
-
|
300
|
-
subject.get_features_for_group(group)
|
301
|
-
end
|
302
172
|
end
|
303
173
|
|
304
174
|
context 'bandiera is having some problems' do
|
@@ -311,14 +181,6 @@ describe Bandiera::Client do
|
|
311
181
|
|
312
182
|
expect(response).to be {}
|
313
183
|
end
|
314
|
-
|
315
|
-
it 'does not store anything in the cache' do
|
316
|
-
stub_request(:get, url).to_return(status: 500, body: '')
|
317
|
-
|
318
|
-
expect(subject.cache).to_not receive(:store)
|
319
|
-
|
320
|
-
subject.get_features_for_group(group)
|
321
|
-
end
|
322
184
|
end
|
323
185
|
|
324
186
|
context 'bandiera times out' do
|
@@ -331,14 +193,6 @@ describe Bandiera::Client do
|
|
331
193
|
|
332
194
|
expect(response).to be {}
|
333
195
|
end
|
334
|
-
|
335
|
-
it 'does not store anything in the cache' do
|
336
|
-
stub_request(:get, url).to_timeout
|
337
|
-
|
338
|
-
expect(subject.cache).to_not receive(:store)
|
339
|
-
|
340
|
-
subject.get_features_for_group(group)
|
341
|
-
end
|
342
196
|
end
|
343
197
|
end
|
344
198
|
|
@@ -355,19 +209,6 @@ describe Bandiera::Client do
|
|
355
209
|
expect(stub).to have_been_requested
|
356
210
|
end
|
357
211
|
|
358
|
-
it 'stores the feature values in the cache' do
|
359
|
-
feature_hash = { 'pubserv' => { 'show-stuff' => true, 'show-other-stuff' => false } }
|
360
|
-
stub_api_request(url, 'response' => feature_hash)
|
361
|
-
|
362
|
-
subject.get_all
|
363
|
-
|
364
|
-
cache_key = subject.send(:build_cache_key, 'pubserv', 'show-stuff', {})
|
365
|
-
expect(subject.cache.key?(cache_key)).to be true
|
366
|
-
|
367
|
-
cache_key = subject.send(:build_cache_key, 'pubserv', 'show-other-stuff', {})
|
368
|
-
expect(subject.cache.key?(cache_key)).to be true
|
369
|
-
end
|
370
|
-
|
371
212
|
context 'and the user has passed through some extra params' do
|
372
213
|
it 'passes them through to the API' do
|
373
214
|
stub = stub_request(:get, url)
|
@@ -407,14 +248,6 @@ describe Bandiera::Client do
|
|
407
248
|
|
408
249
|
expect(response).to be {}
|
409
250
|
end
|
410
|
-
|
411
|
-
it 'does not store anything in the cache' do
|
412
|
-
stub_request(:get, url).to_return(status: [0, ''])
|
413
|
-
|
414
|
-
expect(subject.cache).to_not receive(:store)
|
415
|
-
|
416
|
-
subject.get_all
|
417
|
-
end
|
418
251
|
end
|
419
252
|
|
420
253
|
context 'bandiera is having some problems' do
|
@@ -427,14 +260,6 @@ describe Bandiera::Client do
|
|
427
260
|
|
428
261
|
expect(response).to be {}
|
429
262
|
end
|
430
|
-
|
431
|
-
it 'does not store anything in the cache' do
|
432
|
-
stub_request(:get, url).to_return(status: 500, body: '')
|
433
|
-
|
434
|
-
expect(subject.cache).to_not receive(:store)
|
435
|
-
|
436
|
-
subject.get_all
|
437
|
-
end
|
438
263
|
end
|
439
264
|
|
440
265
|
context 'bandiera times out' do
|
@@ -447,14 +272,6 @@ describe Bandiera::Client do
|
|
447
272
|
|
448
273
|
expect(response).to be {}
|
449
274
|
end
|
450
|
-
|
451
|
-
it 'does not store anything in the cache' do
|
452
|
-
stub_request(:get, url).to_timeout
|
453
|
-
|
454
|
-
expect(subject.cache).to_not receive(:store)
|
455
|
-
|
456
|
-
subject.get_all
|
457
|
-
end
|
458
275
|
end
|
459
276
|
end
|
460
277
|
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'macmillan/utils/rspec/rack_test_helper'
|
3
|
+
|
4
|
+
describe Bandiera::Middleware do
|
5
|
+
let(:app) { ->(env) { [200, env, 'app'] } }
|
6
|
+
let(:request) { req_for('http://example.com') }
|
7
|
+
let(:bandiera_client) { double(:bandiera_client) }
|
8
|
+
let(:bandiera_groups) { [] }
|
9
|
+
let(:user) { double(:user, email: 'bob.flemming@cough.com') }
|
10
|
+
let(:user_uuid) { '12345-12345-12345' }
|
11
|
+
|
12
|
+
subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups) }
|
13
|
+
|
14
|
+
context 'when a bandiera client has NOT been passed' do
|
15
|
+
it 'raises an error' do
|
16
|
+
expect { described_class.new(app) }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'feature flag "groups"' do
|
21
|
+
context 'when NO feature flag "groups" have been defined' do
|
22
|
+
it 'calls #get_all on the bandiera_client' do
|
23
|
+
expect(bandiera_client).to receive(:get_all).and_return({})
|
24
|
+
|
25
|
+
subject.call(request.env)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'stores the returned flags in the env' do
|
29
|
+
pubserv_flags = { 'show-stuff' => true }
|
30
|
+
search_flags = { 'show-search' => false }
|
31
|
+
bandiera_response = { 'pubserv' => pubserv_flags, 'search' => search_flags }
|
32
|
+
|
33
|
+
expect(bandiera_client).to receive(:get_all).and_return(bandiera_response)
|
34
|
+
|
35
|
+
_status, headers, _body = subject.call(request.env)
|
36
|
+
|
37
|
+
expect(headers['bandiera.pubserv']).to eq(pubserv_flags)
|
38
|
+
expect(headers['bandiera.search']).to eq(search_flags)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when SOME feature flag "groups" have been defined' do
|
43
|
+
let(:bandiera_groups) { ['search'] }
|
44
|
+
|
45
|
+
it 'calls #get_features_for_group on the bandiera_client' do
|
46
|
+
expect(bandiera_client).to receive(:get_features_for_group).and_return([])
|
47
|
+
|
48
|
+
subject.call(request.env)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'stores the returned flags in the env' do
|
52
|
+
bandiera_response = { 'show-search' => false }
|
53
|
+
|
54
|
+
expect(bandiera_client).to receive(:get_features_for_group).and_return(bandiera_response)
|
55
|
+
|
56
|
+
_status, headers, _body = subject.call(request.env)
|
57
|
+
|
58
|
+
expect(headers['bandiera.search']).to eq(bandiera_response)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context '"uuid_env_key"' do
|
64
|
+
before do
|
65
|
+
request.env[Macmillan::Utils::Middleware::Uuid.env_key] = user_uuid
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'HAS NOT been set' do
|
69
|
+
it 'uses the default key from Macmillan::Utils' do
|
70
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_id: user_uuid)).and_return({})
|
71
|
+
|
72
|
+
subject.call(request.env)
|
73
|
+
end
|
74
|
+
|
75
|
+
context "and the nothing has set the #{Macmillan::Utils::Middleware::Uuid.env_key} value" do
|
76
|
+
it 'passes nil to the #user_id param' do
|
77
|
+
request.env.delete(Macmillan::Utils::Middleware::Uuid.env_key)
|
78
|
+
|
79
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_id: nil)).and_return({})
|
80
|
+
|
81
|
+
subject.call(request.env)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'HAS been set' do
|
87
|
+
let(:uuid_env_key) { 'wibble' }
|
88
|
+
let(:uuid) { 'qwerty' }
|
89
|
+
|
90
|
+
subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups, uuid_env_key: uuid_env_key) }
|
91
|
+
|
92
|
+
it 'uses the key passed into it' do
|
93
|
+
request.env[uuid_env_key] = uuid
|
94
|
+
|
95
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_id: uuid)).and_return({})
|
96
|
+
|
97
|
+
subject.call(request.env)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context '"user_env_key"' do
|
103
|
+
before do
|
104
|
+
request.env['current_user'] = user
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'when there is no current user' do
|
108
|
+
it 'does not cause errors' do
|
109
|
+
expect do
|
110
|
+
request.env['current_user'] = nil
|
111
|
+
|
112
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: nil)).and_return({})
|
113
|
+
|
114
|
+
subject.call(request.env)
|
115
|
+
end.to_not raise_error
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'HAS NOT been set' do
|
120
|
+
it 'uses the default key - "current_user"' do
|
121
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: user.email)).and_return({})
|
122
|
+
|
123
|
+
subject.call(request.env)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'HAS been set' do
|
128
|
+
let(:user_env_key) { 'wibble' }
|
129
|
+
let(:user) { double(:user, email: 'robert.paulson@example.com') }
|
130
|
+
|
131
|
+
subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups, user_env_key: user_env_key) }
|
132
|
+
|
133
|
+
it 'uses the key passed to it' do
|
134
|
+
request.env[user_env_key] = user
|
135
|
+
|
136
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: user.email)).and_return({})
|
137
|
+
|
138
|
+
subject.call(request.env)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context '"user_group_method"' do
|
144
|
+
before do
|
145
|
+
request.env['current_user'] = user
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when there is no current user' do
|
149
|
+
it 'does not cause errors' do
|
150
|
+
expect do
|
151
|
+
request.env['current_user'] = nil
|
152
|
+
|
153
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: nil)).and_return({})
|
154
|
+
|
155
|
+
subject.call(request.env)
|
156
|
+
end.to_not raise_error
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context 'HAS NOT been set' do
|
161
|
+
it 'uses the default method - "email"' do
|
162
|
+
email = 'ron.manager@football.com'
|
163
|
+
|
164
|
+
expect(user).to receive(:public_send).with(:email).once.and_return(email)
|
165
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: email)).and_return({})
|
166
|
+
|
167
|
+
subject.call(request.env)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'HAS been set' do
|
172
|
+
subject { described_class.new(app, client: bandiera_client, groups: bandiera_groups, user_group_method: :role) }
|
173
|
+
|
174
|
+
it 'uses the method passed to it' do
|
175
|
+
role = 'Administrator'
|
176
|
+
|
177
|
+
expect(user).to receive(:public_send).with(:role).once.and_return(role)
|
178
|
+
expect(bandiera_client).to receive(:get_all).with(hash_including(user_group: role)).and_return({})
|
179
|
+
|
180
|
+
subject.call(request.env)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bandiera-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Macmillan Science and Education (New Publsihing Platforms)
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rack-test
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -67,7 +67,7 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
@@ -81,13 +81,13 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: pry
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
|
-
type: :
|
90
|
+
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rest-client
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -135,6 +135,7 @@ files:
|
|
135
135
|
- ".hound.yml"
|
136
136
|
- ".rspec"
|
137
137
|
- ".rubocop.yml"
|
138
|
+
- ".travis.yml"
|
138
139
|
- Gemfile
|
139
140
|
- HISTORY
|
140
141
|
- LICENSE.txt
|
@@ -143,11 +144,13 @@ files:
|
|
143
144
|
- bandiera-client.gemspec
|
144
145
|
- lib/bandiera/client.rb
|
145
146
|
- lib/bandiera/client/version.rb
|
147
|
+
- lib/bandiera/middleware.rb
|
146
148
|
- spec/lib/bandiera/client_spec.rb
|
149
|
+
- spec/lib/bandiera/middleware_spec.rb
|
147
150
|
- spec/spec_helper.rb
|
148
151
|
homepage: https://github.com/nature/bandiera-client-ruby
|
149
152
|
licenses:
|
150
|
-
-
|
153
|
+
- MIT
|
151
154
|
metadata: {}
|
152
155
|
post_install_message:
|
153
156
|
rdoc_options: []
|
@@ -165,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
168
|
version: '0'
|
166
169
|
requirements: []
|
167
170
|
rubyforge_project:
|
168
|
-
rubygems_version: 2.4.
|
171
|
+
rubygems_version: 2.4.8
|
169
172
|
signing_key:
|
170
173
|
specification_version: 4
|
171
174
|
summary: Simple feature flagging API client.
|