featureflow 0.7.0 → 0.8.0
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/CHANGELOG.md +8 -0
- data/README.md +16 -0
- data/lib/featureflow/application.rb +28 -0
- data/lib/featureflow/client.rb +8 -2
- data/lib/featureflow/configuration.rb +4 -0
- data/lib/featureflow/events_client.rb +11 -7
- data/lib/featureflow/polling_client.rb +8 -3
- data/lib/featureflow/version.rb +1 -1
- 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: d5b0b34f2239d222c1899b7b7af000ab6ee80dc31c481076c73a3220a8f6e76b
|
|
4
|
+
data.tar.gz: fc7e045b04d49220ed536cd885aa7ecdcd63f85d22feed2b41fab7ca6ea434bf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c7834c0bd38d21c36bdbfe588491916b4d3f99c50bfce8ab33a654849fb3d58920918eada5616fc168373792c8373e86f5fbb239c210d1ce213bdd1c643002d
|
|
7
|
+
data.tar.gz: 362fae11135765cb677f26ee3619b57b499bc569d4af61119add4410704dfdcebae7e79d4652314f9d8d185f34dff8502383b0cc9ebf4b81f2d6003f3ea1fb8f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
# Change log
|
|
2
|
+
## [0.8.0] - 2026-08-13
|
|
3
|
+
### Added
|
|
4
|
+
- `application` configuration option (falling back to the `FEATUREFLOW_APPLICATION`
|
|
5
|
+
environment variable): names this workload, sent as `X-Featureflow-Application` on
|
|
6
|
+
every request so the dashboard can attribute usage and flag evaluations per
|
|
7
|
+
application. Slug-validated client-side (case forgiven; an invalid value is dropped
|
|
8
|
+
with a warning and no header is sent).
|
|
9
|
+
|
|
2
10
|
## [0.7.0] - 2026-08-11
|
|
3
11
|
### Fixed
|
|
4
12
|
- Condition operators fail closed instead of raising into the host application: an
|
data/README.md
CHANGED
|
@@ -153,6 +153,22 @@ featureflow.evaluate('key-three', user).is? 'custom' # == true
|
|
|
153
153
|
|
|
154
154
|
```
|
|
155
155
|
|
|
156
|
+
#### Naming your application
|
|
157
|
+
|
|
158
|
+
Optionally tag this workload with an application name so the Featureflow dashboard can
|
|
159
|
+
attribute SDK usage and flag evaluations to it (Admin → SDKs, and the "Evaluated by"
|
|
160
|
+
panel on each feature's statistics tab):
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
featureflow = Featureflow::Client.new(api_key: FEATUREFLOW_SERVER_KEY,
|
|
164
|
+
application: 'checkout-api')
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The name is a slug — lowercase letters, numbers, `.`, `_` and `-`, at most 64
|
|
168
|
+
characters. An invalid value is dropped with a warning and no tag is sent. The
|
|
169
|
+
`FEATUREFLOW_APPLICATION` environment variable is used when the option is not set in
|
|
170
|
+
code.
|
|
171
|
+
|
|
156
172
|
#### Further documentation
|
|
157
173
|
Further documentation can be found [here](http://docs.featureflow.io/docs)
|
|
158
174
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Featureflow
|
|
2
|
+
# Validates the configured application tag, sent as the X-Featureflow-Application
|
|
3
|
+
# header on every request so the dashboard can attribute usage per workload.
|
|
4
|
+
#
|
|
5
|
+
# Contract slug (featureflow-client-sdk-testbed/CONTRACT.md): lowercase [a-z0-9._-],
|
|
6
|
+
# max 64 chars. The server sanitises defensively; the SDK validates strictly so a
|
|
7
|
+
# typo is a visible warning here rather than a silently mangled tag there.
|
|
8
|
+
class Application
|
|
9
|
+
PATTERN = /\A[a-z0-9._-]{1,64}\z/.freeze
|
|
10
|
+
|
|
11
|
+
# Case is forgiven (lowercased); anything else invalid is dropped with a warning
|
|
12
|
+
# and no X-Featureflow-Application header is sent.
|
|
13
|
+
def self.sanitise(raw)
|
|
14
|
+
return nil if raw.nil? || raw == ''
|
|
15
|
+
unless raw.is_a?(String)
|
|
16
|
+
Featureflow.logger.warn "ignoring application #{raw.inspect} - must be a string"
|
|
17
|
+
return nil
|
|
18
|
+
end
|
|
19
|
+
value = raw.strip.downcase
|
|
20
|
+
unless PATTERN.match?(value)
|
|
21
|
+
Featureflow.logger.warn "ignoring application #{raw.inspect} - must be lowercase " \
|
|
22
|
+
'a-z, 0-9, dot, underscore or hyphen, max 64 chars'
|
|
23
|
+
return nil
|
|
24
|
+
end
|
|
25
|
+
value
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/featureflow/client.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'time'
|
|
2
|
+
require 'featureflow/application'
|
|
2
3
|
require 'featureflow/evaluate'
|
|
3
4
|
require 'featureflow/polling_client'
|
|
4
5
|
require 'featureflow/events_client'
|
|
@@ -10,6 +11,10 @@ module Featureflow
|
|
|
10
11
|
@configuration.validate!
|
|
11
12
|
@features = {}
|
|
12
13
|
|
|
14
|
+
# Optional workload name sent as X-Featureflow-Application on every request so
|
|
15
|
+
# usage and flag evaluations are attributable per application in the dashboard.
|
|
16
|
+
@application = Application.sanitise(@configuration.application)
|
|
17
|
+
|
|
13
18
|
Featureflow.logger.info 'initializing client'
|
|
14
19
|
|
|
15
20
|
@failover_variants = {}
|
|
@@ -20,7 +25,7 @@ module Featureflow
|
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
unless @configuration.disable_events
|
|
23
|
-
@events_client = EventsClient.new @configuration.event_endpoint, @configuration.api_key
|
|
28
|
+
@events_client = EventsClient.new @configuration.event_endpoint, @configuration.api_key, @application
|
|
24
29
|
@events_client.register_features @configuration.with_features
|
|
25
30
|
end
|
|
26
31
|
|
|
@@ -39,7 +44,8 @@ module Featureflow
|
|
|
39
44
|
@configuration.endpoint,
|
|
40
45
|
@configuration.api_key,
|
|
41
46
|
poll_interval: 10,
|
|
42
|
-
timeout: 30
|
|
47
|
+
timeout: 30,
|
|
48
|
+
application: @application
|
|
43
49
|
)
|
|
44
50
|
end
|
|
45
51
|
|
|
@@ -5,12 +5,16 @@ class Featureflow::Configuration
|
|
|
5
5
|
attr_accessor :disable_events
|
|
6
6
|
attr_accessor :with_features
|
|
7
7
|
attr_accessor :logger
|
|
8
|
+
# Optional workload name for per-application usage attribution in the dashboard;
|
|
9
|
+
# sent as X-Featureflow-Application on every request. See Featureflow::Application.
|
|
10
|
+
attr_accessor :application
|
|
8
11
|
|
|
9
12
|
DEFAULT_ENDPOINT = 'https://app.featureflow.io'
|
|
10
13
|
DEFAULT_EVENT_ENDPOINT = 'https://events.featureflow.io'
|
|
11
14
|
|
|
12
15
|
def initialize
|
|
13
16
|
self.api_key = ENV["FEATUREFLOW_SERVER_KEY"]
|
|
17
|
+
self.application = ENV["FEATUREFLOW_APPLICATION"]
|
|
14
18
|
self.endpoint = DEFAULT_ENDPOINT
|
|
15
19
|
self.event_endpoint = DEFAULT_EVENT_ENDPOINT
|
|
16
20
|
self.disable_events = false
|
|
@@ -7,9 +7,11 @@ module Featureflow
|
|
|
7
7
|
#LOCK = Mutex.new
|
|
8
8
|
|
|
9
9
|
class EventsClient
|
|
10
|
-
def initialize(url, api_key)
|
|
10
|
+
def initialize(url, api_key, application = nil)
|
|
11
11
|
@url = url
|
|
12
12
|
@api_key = api_key
|
|
13
|
+
# Already sanitised by the Client (see lib/featureflow/application.rb).
|
|
14
|
+
@application = application
|
|
13
15
|
@eventsQueue = Queue.new
|
|
14
16
|
@scheduler = start_scheduler
|
|
15
17
|
end
|
|
@@ -104,15 +106,17 @@ module Featureflow
|
|
|
104
106
|
=end
|
|
105
107
|
|
|
106
108
|
private def send_event(event_name, method, path, body)
|
|
109
|
+
headers = {
|
|
110
|
+
'Authorization' => "Bearer #{@api_key}",
|
|
111
|
+
'Content-Type' => 'application/json;charset=UTF-8',
|
|
112
|
+
'Accept' => 'Application/Json',
|
|
113
|
+
'X-Featureflow-Client' => 'RubyClient/' + Featureflow::VERSION
|
|
114
|
+
}
|
|
115
|
+
headers['X-Featureflow-Application'] = @application if @application
|
|
107
116
|
connection = Excon.new(@url)
|
|
108
117
|
response = connection.request(method: method,
|
|
109
118
|
path: path,
|
|
110
|
-
headers:
|
|
111
|
-
'Authorization' => "Bearer #{@api_key}",
|
|
112
|
-
'Content-Type' => 'application/json;charset=UTF-8',
|
|
113
|
-
'Accept' => 'Application/Json',
|
|
114
|
-
'X-Featureflow-Client' => 'RubyClient/' + Featureflow::VERSION
|
|
115
|
-
},
|
|
119
|
+
headers: headers,
|
|
116
120
|
omit_default_port: true,
|
|
117
121
|
body: JSON.generate(body))
|
|
118
122
|
if response.status >= 400
|
|
@@ -5,7 +5,8 @@ module Featureflow
|
|
|
5
5
|
class PollingClient
|
|
6
6
|
DEFAULT_OPTIONS = {
|
|
7
7
|
poll_interval: 30,
|
|
8
|
-
timeout: 30
|
|
8
|
+
timeout: 30,
|
|
9
|
+
application: nil
|
|
9
10
|
}.freeze
|
|
10
11
|
LOCK = Mutex.new
|
|
11
12
|
|
|
@@ -35,12 +36,16 @@ module Featureflow
|
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
def load_features
|
|
38
|
-
|
|
39
|
+
headers = {
|
|
39
40
|
'Authorization' => "Bearer #{@api_key}",
|
|
40
41
|
'Accept' => 'Application/Json',
|
|
41
42
|
'If-None-Match' => @etag,
|
|
42
43
|
'X-Featureflow-Client' => 'RubyClient/' + Featureflow::VERSION
|
|
43
|
-
}
|
|
44
|
+
}
|
|
45
|
+
# Write-only telemetry: never affects the response, so it cannot fragment the CDN cache.
|
|
46
|
+
headers['X-Featureflow-Application'] = @options[:application] if @options[:application]
|
|
47
|
+
response = Excon.get(@url + + '/api/sdk/v1/features', headers: headers,
|
|
48
|
+
omit_default_port: true, read_timeout: @options[:timeout])
|
|
44
49
|
|
|
45
50
|
if response.status == 200
|
|
46
51
|
Featureflow.logger.debug "updating features"
|
data/lib/featureflow/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: featureflow
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oliver Oldfield-Hodge
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -105,6 +105,7 @@ files:
|
|
|
105
105
|
- LICENSE
|
|
106
106
|
- README.md
|
|
107
107
|
- lib/featureflow.rb
|
|
108
|
+
- lib/featureflow/application.rb
|
|
108
109
|
- lib/featureflow/client.rb
|
|
109
110
|
- lib/featureflow/conditions.rb
|
|
110
111
|
- lib/featureflow/configuration.rb
|