flipt_client 0.16.1 → 1.0.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/README.md +91 -27
- data/flipt-client-ruby.gemspec +1 -1
- data/lib/ext/darwin_aarch64/libfliptengine.dylib +0 -0
- data/lib/ext/darwin_x86_64/libfliptengine.dylib +0 -0
- data/lib/ext/flipt_engine.h +67 -14
- data/lib/ext/linux_aarch64/libfliptengine.so +0 -0
- data/lib/ext/linux_x86_64/libfliptengine.so +0 -0
- data/lib/ext/windows_x86_64/fliptengine.dll +0 -0
- data/lib/flipt_client/models.rb +62 -0
- data/lib/flipt_client/version.rb +1 -1
- data/lib/flipt_client.rb +121 -48
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8cfa993fb8a7521e64c37cef82bf58088f8961d9779e6c5dc9092696a127c6ff
|
4
|
+
data.tar.gz: 2cddbf45abafabd21660a60ab64378a2b1c4bfdafee4b931805ca8b2a1c2d002
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2cc595e3b40f37c420b920415d65e439a6295ddc1d1b4c22dc0fb2cb98e43e36912842568e1eb47246a088f15eb6bcce530e020daf0c2d34253fec9fa18347d
|
7
|
+
data.tar.gz: 26c75eb569c0e0df06cf287ae7f9cb5beb0466be5e1c1cb8ec49252d38277914afa842f56650d7dd857cb3696d73d17a68ea6082ada6101f3bbfa729142c934f
|
data/README.md
CHANGED
@@ -26,12 +26,26 @@ Upon instantiation, the `flipt-client-ruby` library will fetch the flag state fr
|
|
26
26
|
|
27
27
|
By default, the SDK will poll the Flipt server for new flag state at a regular interval. This interval can be configured using the `update_interval` option when constructing a client. The default interval is 120 seconds.
|
28
28
|
|
29
|
-
### Streaming (Flipt Cloud
|
29
|
+
### Streaming (Flipt Cloud/Flipt v2)
|
30
30
|
|
31
|
-
[Flipt Cloud](https://flipt.io/cloud) users can use the `streaming` fetch method to stream flag state changes from the Flipt server to the SDK.
|
31
|
+
[Flipt Cloud](https://flipt.io/cloud) and [Flipt v2](https://docs.flipt.io/v2) users can use the `streaming` fetch method to stream flag state changes from the Flipt server to the SDK.
|
32
32
|
|
33
33
|
When in streaming mode, the SDK will connect to the Flipt server and open a persistent connection that will remain open until the client is closed. The SDK will then receive flag state changes in real-time.
|
34
34
|
|
35
|
+
### Retries
|
36
|
+
|
37
|
+
The SDK will automatically retry fetching (or initiating streaming) flag state if the client is unable to reach the Flipt server temporarily.
|
38
|
+
|
39
|
+
The SDK will retry up to 3 times with an exponential backoff interval between retries. The base delay is 1 second and the maximum delay is 30 seconds.
|
40
|
+
|
41
|
+
Retriable errors include:
|
42
|
+
|
43
|
+
- `429 Too Many Requests`
|
44
|
+
- `502 Bad Gateway`
|
45
|
+
- `503 Service Unavailable`
|
46
|
+
- `504 Gateway Timeout`
|
47
|
+
- Other potential transient network or DNS errors
|
48
|
+
|
35
49
|
## Supported Architectures
|
36
50
|
|
37
51
|
This SDK currently supports the following OSes/architectures:
|
@@ -53,6 +67,21 @@ gem install ffi -- --enable-system-libffi # to install the gem manually
|
|
53
67
|
bundle config build.ffi --enable-system-libffi # for bundle install
|
54
68
|
```
|
55
69
|
|
70
|
+
## Migration Notes
|
71
|
+
|
72
|
+
### Pre-1.0.0 -> 1.0.0
|
73
|
+
|
74
|
+
This section is for users who are migrating from a previous (pre-1.0.0) version of the SDK.
|
75
|
+
|
76
|
+
- `Flipt::EvaluationClient` has been renamed to `Flipt::Client`. Update all usages and imports accordingly. A deprecation warning is emitted if you use the old class.
|
77
|
+
- All evaluation methods now use **keyword arguments** (e.g., `flag_key:`, `entity_id:`, `context:`) instead of a single hash argument. Update your method calls to use keyword arguments.
|
78
|
+
- All evaluation methods now return **response model objects** (`VariantEvaluationResponse`, `BooleanEvaluationResponse`, `BatchEvaluationResponse`, `ErrorEvaluationResponse`) instead of raw hashes. Update your code to use attribute readers (e.g., `resp.flag_key`, `resp.enabled`).
|
79
|
+
- Batch evaluation responses now contain an array of model objects, not hashes.
|
80
|
+
- Error handling is now standardized and idiomatic. All errors inherit from `Flipt::Error` (with subclasses like `ValidationError`, `EvaluationError`). Update your rescue blocks accordingly.
|
81
|
+
- The minimum supported Ruby version is now 2.7.0.
|
82
|
+
- The client constructor now accepts keyword arguments for configuration (e.g., `url:`, `namespace:`, `authentication:`). The `environment` option is now supported.
|
83
|
+
- The API and documentation are more idiomatic and Ruby-like throughout.
|
84
|
+
|
56
85
|
## Usage
|
57
86
|
|
58
87
|
In your Ruby code you can import this client and use it as so:
|
@@ -60,40 +89,35 @@ In your Ruby code you can import this client and use it as so:
|
|
60
89
|
```ruby
|
61
90
|
require 'flipt_client'
|
62
91
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
#
|
72
|
-
#
|
73
|
-
# You can replace the url with where your upstream Flipt instance points to, the update interval for how long you are willing
|
74
|
-
# to wait for updated flag state, and the auth token if your Flipt instance requires it.
|
75
|
-
client = Flipt::EvaluationClient.new()
|
76
|
-
resp = client.evaluate_variant({ flag_key: 'buzz', entity_id: 'someentity', context: { fizz: 'buzz' } })
|
77
|
-
|
78
|
-
puts resp
|
92
|
+
client = Flipt::Client.new(url: 'http://localhost:8080', authentication: Flipt::ClientTokenAuthentication.new('secret'))
|
93
|
+
|
94
|
+
resp = client.evaluate_variant(flag_key: 'buzz', entity_id: 'someentity', context: { fizz: 'buzz' })
|
95
|
+
puts resp.flag_key # => 'buzz'
|
96
|
+
puts resp.match # => true
|
97
|
+
puts resp.reason # => 'MATCH_EVALUATION_REASON'
|
98
|
+
|
99
|
+
resp = client.evaluate_boolean(flag_key: 'my-feature', entity_id: 'someentity')
|
100
|
+
puts resp.enabled # => true
|
79
101
|
```
|
80
102
|
|
81
103
|
### Constructor Arguments
|
82
104
|
|
83
|
-
The `Flipt::
|
105
|
+
The `Flipt::Client` constructor accepts the following keyword arguments:
|
84
106
|
|
107
|
+
- `environment`: The environment (Flipt v2) to fetch flag state from. If not provided, the client will default to the `default` environment.
|
85
108
|
- `namespace`: The namespace to fetch flag state from. If not provided, the client will default to the `default` namespace.
|
86
|
-
- `
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
109
|
+
- `url`: The URL of the upstream Flipt instance. Defaults to `http://localhost:8080`.
|
110
|
+
- `request_timeout`: Timeout (in seconds) for requests. Defaults to no timeout.
|
111
|
+
- `update_interval`: Interval (in seconds) to fetch new flag state. Defaults to 120 seconds.
|
112
|
+
- `authentication`: The authentication strategy to use. Defaults to no authentication. See [Authentication](#authentication).
|
113
|
+
- `reference`: The [reference](https://docs.flipt.io/guides/user/using-references) to use when fetching flag state.
|
114
|
+
- `fetch_mode`: The fetch mode to use. Defaults to polling.
|
115
|
+
- `error_strategy`: The error strategy to use. Defaults to fail. See [Error Strategies](#error-strategies).
|
116
|
+
- `snapshot`: The snapshot to use when initializing the client. Defaults to no snapshot. See [Snapshotting](#snapshotting).
|
93
117
|
|
94
118
|
### Authentication
|
95
119
|
|
96
|
-
The `
|
120
|
+
The `Flipt::Client` supports the following authentication strategies:
|
97
121
|
|
98
122
|
- No Authentication (default)
|
99
123
|
- [Client Token Authentication](https://docs.flipt.io/authentication/using-tokens)
|
@@ -106,6 +130,46 @@ The client supports the following error strategies:
|
|
106
130
|
- `fail`: The client will throw an error if the flag state cannot be fetched. This is the default behavior.
|
107
131
|
- `fallback`: The client will maintain the last known good state and use that state for evaluation in case of an error.
|
108
132
|
|
133
|
+
### Response Models
|
134
|
+
|
135
|
+
All evaluation methods return response model objects:
|
136
|
+
|
137
|
+
- `evaluate_variant` returns a `Flipt::VariantEvaluationResponse`
|
138
|
+
- `evaluate_boolean` returns a `Flipt::BooleanEvaluationResponse`
|
139
|
+
- `evaluate_batch` returns a `Flipt::BatchEvaluationResponse`
|
140
|
+
|
141
|
+
### Error Handling
|
142
|
+
|
143
|
+
All errors inherit from `Flipt::Error`. Common subclasses include:
|
144
|
+
|
145
|
+
- `Flipt::ValidationError`
|
146
|
+
- `Flipt::EvaluationError`
|
147
|
+
|
148
|
+
You can rescue these errors as needed:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
begin
|
152
|
+
client.evaluate_variant(flag_key: 'missing', entity_id: 'user')
|
153
|
+
rescue Flipt::EvaluationError => e
|
154
|
+
puts "Evaluation failed: #{e.message}"
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
158
|
+
### Snapshotting
|
159
|
+
|
160
|
+
The client supports snapshotting of flag state as well as seeding the client with a snapshot for evaluation. This is helpful if you want to use the client in an environment where the Flipt server is not guaranteed to be available or reachable on startup.
|
161
|
+
|
162
|
+
To get the snapshot for the client, you can use the `snapshot` method. This returns a base64 encoded JSON string that represents the flag state for the client.
|
163
|
+
|
164
|
+
You can set the snapshot for the client using the `snapshot` option when constructing a client.
|
165
|
+
|
166
|
+
**Note:** You most likely will want to also set the `error_strategy` to `fallback` when using snapshots. This will ensure that you wont get an error if the Flipt server is not available or reachable even on the initial fetch.
|
167
|
+
|
168
|
+
You also may want to store the snapshot in a local file so that you can use it to seed the client on startup.
|
169
|
+
|
170
|
+
> [!IMPORTANT]
|
171
|
+
> If the Flipt server becomes reachable after the setting the snapshot, the client will replace the snapshot with the new flag state from the Flipt server.
|
172
|
+
|
109
173
|
## Load Test
|
110
174
|
|
111
175
|
1. To run the load test, you'll need to have Flipt running locally. You can do this by running the following command from the root of the repository:
|
data/flipt-client-ruby.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.description = 'Flipt Client Evaluation SDK'
|
12
12
|
spec.homepage = 'https://www.flipt.io'
|
13
13
|
spec.license = 'MIT'
|
14
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.
|
14
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
|
15
15
|
|
16
16
|
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
17
17
|
|
Binary file
|
Binary file
|
data/lib/ext/flipt_engine.h
CHANGED
@@ -8,7 +8,35 @@
|
|
8
8
|
*
|
9
9
|
* This function will initialize an Engine and return a pointer back to the caller.
|
10
10
|
*/
|
11
|
-
void *
|
11
|
+
void *initialize_engine_ffi(const char *opts);
|
12
|
+
|
13
|
+
/**
|
14
|
+
* # Safety
|
15
|
+
*
|
16
|
+
* This function will initialize an Engine and return a pointer back to the caller.
|
17
|
+
*/
|
18
|
+
void *initialize_engine(const char *opts);
|
19
|
+
|
20
|
+
/**
|
21
|
+
* # Safety
|
22
|
+
*
|
23
|
+
* This function will take in a pointer to the engine and return the current snapshot as a JSON string.
|
24
|
+
*/
|
25
|
+
const char *get_snapshot_ffi(void *engine_ptr);
|
26
|
+
|
27
|
+
/**
|
28
|
+
* # Safety
|
29
|
+
*
|
30
|
+
* This function will take in a pointer to the engine and return the current snapshot as a JSON string.
|
31
|
+
*/
|
32
|
+
const char *get_snapshot(void *engine_ptr);
|
33
|
+
|
34
|
+
/**
|
35
|
+
* # Safety
|
36
|
+
*
|
37
|
+
* This function will take in a pointer to the engine and return a variant evaluation response.
|
38
|
+
*/
|
39
|
+
const char *evaluate_variant_ffi(void *engine_ptr, const char *evaluation_request);
|
12
40
|
|
13
41
|
/**
|
14
42
|
* # Safety
|
@@ -17,6 +45,13 @@ void *initialize_engine(const char *namespace_, const char *opts);
|
|
17
45
|
*/
|
18
46
|
const char *evaluate_variant(void *engine_ptr, const char *evaluation_request);
|
19
47
|
|
48
|
+
/**
|
49
|
+
* # Safety
|
50
|
+
*
|
51
|
+
* This function will take in a pointer to the engine and return a boolean evaluation response.
|
52
|
+
*/
|
53
|
+
const char *evaluate_boolean_ffi(void *engine_ptr, const char *evaluation_request);
|
54
|
+
|
20
55
|
/**
|
21
56
|
* # Safety
|
22
57
|
*
|
@@ -24,6 +59,13 @@ const char *evaluate_variant(void *engine_ptr, const char *evaluation_request);
|
|
24
59
|
*/
|
25
60
|
const char *evaluate_boolean(void *engine_ptr, const char *evaluation_request);
|
26
61
|
|
62
|
+
/**
|
63
|
+
* # Safety
|
64
|
+
*
|
65
|
+
* This function will take in a pointer to the engine and return a batch evaluation response.
|
66
|
+
*/
|
67
|
+
const char *evaluate_batch_ffi(void *engine_ptr, const char *batch_evaluation_request);
|
68
|
+
|
27
69
|
/**
|
28
70
|
* # Safety
|
29
71
|
*
|
@@ -34,30 +76,41 @@ const char *evaluate_batch(void *engine_ptr, const char *batch_evaluation_reques
|
|
34
76
|
/**
|
35
77
|
* # Safety
|
36
78
|
*
|
37
|
-
* This function will take in a pointer to the engine and return a list of flags
|
79
|
+
* This function will take in a pointer to the engine and return a list of flags.
|
80
|
+
*/
|
81
|
+
const char *list_flags_ffi(void *engine_ptr);
|
82
|
+
|
83
|
+
/**
|
84
|
+
* # Safety
|
85
|
+
*
|
86
|
+
* This function will take in a pointer to the engine and return a list of flags.
|
38
87
|
*/
|
39
88
|
const char *list_flags(void *engine_ptr);
|
40
89
|
|
41
90
|
/**
|
42
91
|
* # Safety
|
43
92
|
*
|
44
|
-
* This function will
|
93
|
+
* This function will take in a pointer to the engine and destroy it.
|
94
|
+
*/
|
95
|
+
void destroy_engine_ffi(void *engine_ptr);
|
96
|
+
|
97
|
+
/**
|
98
|
+
* # Safety
|
99
|
+
*
|
100
|
+
* This function will take in a pointer to the engine and destroy it.
|
45
101
|
*/
|
46
102
|
void destroy_engine(void *engine_ptr);
|
47
103
|
|
48
104
|
/**
|
49
105
|
* # Safety
|
50
106
|
*
|
51
|
-
* This function will take in a pointer to
|
52
|
-
* See Rust the safety section in CString::from_raw.
|
107
|
+
* This function will take in a pointer to a string and destroy it.
|
53
108
|
*/
|
54
|
-
void
|
109
|
+
void destroy_string_ffi(char *ptr);
|
55
110
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
extern void destroy_engine_ffi(void* engine);
|
63
|
-
extern void destroy_string_ffi(char* str);
|
111
|
+
/**
|
112
|
+
* # Safety
|
113
|
+
*
|
114
|
+
* This function will take in a pointer to a string and destroy it.
|
115
|
+
*/
|
116
|
+
void destroy_string(char *ptr);
|
Binary file
|
Binary file
|
Binary file
|
data/lib/flipt_client/models.rb
CHANGED
@@ -40,4 +40,66 @@ module Flipt
|
|
40
40
|
}
|
41
41
|
end
|
42
42
|
end
|
43
|
+
|
44
|
+
# VariantEvaluationResponse
|
45
|
+
# @attr_reader [String] flag_key
|
46
|
+
# @attr_reader [Boolean] match
|
47
|
+
# @attr_reader [String] reason
|
48
|
+
# @attr_reader [String] variant_key
|
49
|
+
# @attr_reader [String, nil] variant_attachment
|
50
|
+
# @attr_reader [Array<String>] segment_keys
|
51
|
+
class VariantEvaluationResponse
|
52
|
+
attr_reader :flag_key, :match, :reason, :variant_key, :variant_attachment, :segment_keys
|
53
|
+
|
54
|
+
def initialize(flag_key:, match:, reason:, variant_key:, variant_attachment: nil, segment_keys: [])
|
55
|
+
@flag_key = flag_key
|
56
|
+
@match = match
|
57
|
+
@reason = reason
|
58
|
+
@variant_key = variant_key
|
59
|
+
@variant_attachment = variant_attachment
|
60
|
+
@segment_keys = segment_keys
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# BooleanEvaluationResponse
|
65
|
+
# @attr_reader [String] flag_key
|
66
|
+
# @attr_reader [Boolean] enabled
|
67
|
+
# @attr_reader [String] reason
|
68
|
+
# @attr_reader [Array<String>] segment_keys
|
69
|
+
class BooleanEvaluationResponse
|
70
|
+
attr_reader :flag_key, :enabled, :reason, :segment_keys
|
71
|
+
|
72
|
+
def initialize(flag_key:, enabled:, reason:, segment_keys: [])
|
73
|
+
@flag_key = flag_key
|
74
|
+
@enabled = enabled
|
75
|
+
@reason = reason
|
76
|
+
@segment_keys = segment_keys
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# ErrorEvaluationResponse
|
81
|
+
# @attr_reader [String] flag_key
|
82
|
+
# @attr_reader [String] namespace_key
|
83
|
+
# @attr_reader [String] reason
|
84
|
+
# @attr_reader [String] error_message
|
85
|
+
class ErrorEvaluationResponse
|
86
|
+
attr_reader :flag_key, :namespace_key, :reason, :error_message
|
87
|
+
|
88
|
+
def initialize(flag_key:, namespace_key:, reason:, error_message:)
|
89
|
+
@flag_key = flag_key
|
90
|
+
@namespace_key = namespace_key
|
91
|
+
@reason = reason
|
92
|
+
@error_message = error_message
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# BatchEvaluationResponse
|
97
|
+
# @attr_reader [Array] responses
|
98
|
+
class BatchEvaluationResponse
|
99
|
+
attr_reader :responses
|
100
|
+
|
101
|
+
def initialize(responses: [])
|
102
|
+
@responses = responses
|
103
|
+
end
|
104
|
+
end
|
43
105
|
end
|
data/lib/flipt_client/version.rb
CHANGED
data/lib/flipt_client.rb
CHANGED
@@ -7,9 +7,11 @@ require 'json'
|
|
7
7
|
|
8
8
|
module Flipt
|
9
9
|
class Error < StandardError; end
|
10
|
+
class ValidationError < Error; end
|
11
|
+
class EvaluationError < Error; end
|
10
12
|
|
11
|
-
#
|
12
|
-
class
|
13
|
+
# Client is a Ruby Client Side Evaluation Library for Flipt
|
14
|
+
class Client
|
13
15
|
extend FFI::Library
|
14
16
|
|
15
17
|
FLIPTENGINE = 'fliptengine'
|
@@ -32,8 +34,8 @@ module Flipt
|
|
32
34
|
|
33
35
|
ffi_lib File.expand_path(libfile, __dir__)
|
34
36
|
|
35
|
-
# void *initialize_engine(const char *
|
36
|
-
attach_function :initialize_engine,
|
37
|
+
# void *initialize_engine(const char *opts);
|
38
|
+
attach_function :initialize_engine, [:string], :pointer
|
37
39
|
# void destroy_engine(void *engine_ptr);
|
38
40
|
attach_function :destroy_engine, [:pointer], :void
|
39
41
|
# const char *evaluate_variant(void *engine_ptr, const char *evaluation_request);
|
@@ -46,26 +48,31 @@ module Flipt
|
|
46
48
|
attach_function :list_flags, [:pointer], :strptr
|
47
49
|
# void destroy_string(const char *ptr);
|
48
50
|
attach_function :destroy_string, [:pointer], :void
|
51
|
+
# const char *get_snapshot(void *engine_ptr);
|
52
|
+
attach_function :get_snapshot, [:pointer], :strptr
|
49
53
|
|
50
54
|
# Create a new Flipt client
|
51
55
|
#
|
52
|
-
# @param namespace [String] namespace
|
53
56
|
# @param opts [Hash] options
|
57
|
+
# @option opts [String] :environment Flipt environment (default: 'default')
|
58
|
+
# @option opts [String] :namespace Flipt namespace (default: 'default')
|
54
59
|
# @option opts [String] :url Flipt server url
|
55
60
|
# @option opts [AuthenticationStrategy] :authentication strategy to authenticate with Flipt
|
61
|
+
# @option opts [Integer] :request_timeout timeout in seconds for the request
|
56
62
|
# @option opts [Integer] :update_interval interval in seconds to update the cache
|
57
63
|
# @option opts [String] :reference reference to use for namespace data
|
58
64
|
# @option opts [Symbol] :fetch_mode fetch mode to use for the client (:polling or :streaming).
|
59
|
-
# Note: Streaming is currently only supported when using the SDK with Flipt Cloud
|
65
|
+
# Note: Streaming is currently only supported when using the SDK with Flipt Cloud or Flipt v2.
|
60
66
|
# @option opts [Symbol] :error_strategy error strategy to use for the client (:fail or :fallback).
|
61
|
-
|
62
|
-
|
67
|
+
# @option opts [String] :snapshot snapshot to use when initializing the client
|
68
|
+
def initialize(**opts)
|
69
|
+
@namespace = opts.fetch(:namespace, 'default')
|
63
70
|
|
64
71
|
opts[:authentication] = validate_authentication(opts.fetch(:authentication, NoAuthentication.new))
|
65
72
|
opts[:fetch_mode] = validate_fetch_mode(opts.fetch(:fetch_mode, :polling))
|
66
73
|
opts[:error_strategy] = validate_error_strategy(opts.fetch(:error_strategy, :fail))
|
67
74
|
|
68
|
-
@engine = self.class.initialize_engine(
|
75
|
+
@engine = self.class.initialize_engine(opts.to_json)
|
69
76
|
ObjectSpace.define_finalizer(self, self.class.finalize(@engine))
|
70
77
|
end
|
71
78
|
|
@@ -75,88 +82,154 @@ module Flipt
|
|
75
82
|
|
76
83
|
# Evaluate a variant flag for a given request
|
77
84
|
#
|
78
|
-
# @param
|
79
|
-
# @
|
80
|
-
# @
|
81
|
-
def evaluate_variant(
|
82
|
-
validate_evaluation_request(
|
83
|
-
|
84
|
-
ptr =
|
85
|
+
# @param flag_key [String]
|
86
|
+
# @param entity_id [String]
|
87
|
+
# @param context [Hash]
|
88
|
+
def evaluate_variant(flag_key:, entity_id:, context: {})
|
89
|
+
validate_evaluation_request(flag_key, entity_id, context)
|
90
|
+
req = { flag_key: flag_key, entity_id: entity_id, context: context }
|
91
|
+
resp, ptr = self.class.evaluate_variant(@engine, req.to_json)
|
92
|
+
ptr = FFI::AutoPointer.new(ptr, Client.method(:destroy_string))
|
85
93
|
data = JSON.parse(resp)
|
86
|
-
raise
|
87
|
-
|
88
|
-
data['result']
|
94
|
+
raise EvaluationError, data['error_message'] if data['status'] != 'success'
|
95
|
+
|
96
|
+
r = data['result']
|
97
|
+
VariantEvaluationResponse.new(
|
98
|
+
flag_key: r['flag_key'],
|
99
|
+
match: r['match'],
|
100
|
+
reason: r['reason'],
|
101
|
+
variant_key: r['variant_key'],
|
102
|
+
variant_attachment: r['variant_attachment'],
|
103
|
+
segment_keys: r['segment_keys'] || []
|
104
|
+
)
|
89
105
|
end
|
90
106
|
|
91
107
|
# Evaluate a boolean flag for a given request
|
92
108
|
#
|
93
|
-
# @param
|
94
|
-
# @
|
95
|
-
# @
|
96
|
-
def evaluate_boolean(
|
97
|
-
validate_evaluation_request(
|
98
|
-
|
99
|
-
ptr =
|
109
|
+
# @param flag_key [String]
|
110
|
+
# @param entity_id [String]
|
111
|
+
# @param context [Hash]
|
112
|
+
def evaluate_boolean(flag_key:, entity_id:, context: {})
|
113
|
+
validate_evaluation_request(flag_key, entity_id, context)
|
114
|
+
req = { flag_key: flag_key, entity_id: entity_id, context: context }
|
115
|
+
resp, ptr = self.class.evaluate_boolean(@engine, req.to_json)
|
116
|
+
ptr = FFI::AutoPointer.new(ptr, Client.method(:destroy_string))
|
100
117
|
data = JSON.parse(resp)
|
101
|
-
raise
|
102
|
-
|
103
|
-
data['result']
|
118
|
+
raise EvaluationError, data['error_message'] if data['status'] != 'success'
|
119
|
+
|
120
|
+
r = data['result']
|
121
|
+
BooleanEvaluationResponse.new(
|
122
|
+
flag_key: r['flag_key'],
|
123
|
+
enabled: r['enabled'],
|
124
|
+
reason: r['reason'],
|
125
|
+
segment_keys: r['segment_keys'] || []
|
126
|
+
)
|
104
127
|
end
|
105
128
|
|
106
129
|
# Evaluate a batch of flags for a given request
|
107
130
|
#
|
108
|
-
# @param
|
131
|
+
# @param requests [Array<Hash>] batch evaluation request
|
109
132
|
# - :entity_id [String] entity id
|
110
133
|
# - :flag_key [String] flag key
|
111
|
-
def evaluate_batch(
|
112
|
-
|
113
|
-
|
134
|
+
def evaluate_batch(requests:)
|
135
|
+
unless requests.is_a?(Array)
|
136
|
+
raise ValidationError, 'requests must be an array of evaluation requests'
|
114
137
|
end
|
115
138
|
|
116
|
-
|
117
|
-
|
139
|
+
requests.each do |request|
|
140
|
+
validate_evaluation_request(request[:flag_key], request[:entity_id], request[:context] || {})
|
141
|
+
end
|
142
|
+
resp, ptr = self.class.evaluate_batch(@engine, requests.to_json)
|
143
|
+
ptr = FFI::AutoPointer.new(ptr, Client.method(:destroy_string))
|
118
144
|
data = JSON.parse(resp)
|
119
|
-
raise
|
120
|
-
|
121
|
-
data['result']
|
145
|
+
raise EvaluationError, data['error_message'] if data['status'] != 'success'
|
146
|
+
|
147
|
+
responses = (data['result']['responses'] || []).map do |r|
|
148
|
+
case r['type']
|
149
|
+
when 'VARIANT_EVALUATION_RESPONSE_TYPE'
|
150
|
+
v = r['variant_evaluation_response']
|
151
|
+
VariantEvaluationResponse.new(
|
152
|
+
flag_key: v['flag_key'],
|
153
|
+
match: v['match'],
|
154
|
+
reason: v['reason'],
|
155
|
+
variant_key: v['variant_key'],
|
156
|
+
variant_attachment: v['variant_attachment'],
|
157
|
+
segment_keys: v['segment_keys'] || []
|
158
|
+
)
|
159
|
+
when 'BOOLEAN_EVALUATION_RESPONSE_TYPE'
|
160
|
+
b = r['boolean_evaluation_response']
|
161
|
+
BooleanEvaluationResponse.new(
|
162
|
+
flag_key: b['flag_key'],
|
163
|
+
enabled: b['enabled'],
|
164
|
+
reason: b['reason'],
|
165
|
+
segment_keys: b['segment_keys'] || []
|
166
|
+
)
|
167
|
+
when 'ERROR_EVALUATION_RESPONSE_TYPE'
|
168
|
+
e = r['error_evaluation_response']
|
169
|
+
ErrorEvaluationResponse.new(
|
170
|
+
flag_key: e['flag_key'],
|
171
|
+
namespace_key: e['namespace_key'],
|
172
|
+
reason: e['reason'],
|
173
|
+
error_message: e['error_message']
|
174
|
+
)
|
175
|
+
else
|
176
|
+
raise EvaluationError, "Unknown response type encountered: #{r['type']}"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
BatchEvaluationResponse.new(responses: responses)
|
122
180
|
end
|
123
181
|
|
124
182
|
# List all flags in the namespace
|
125
183
|
def list_flags
|
126
184
|
resp, ptr = self.class.list_flags(@engine)
|
127
|
-
ptr = FFI::AutoPointer.new(ptr,
|
185
|
+
ptr = FFI::AutoPointer.new(ptr, Client.method(:destroy_string))
|
128
186
|
data = JSON.parse(resp)
|
129
187
|
raise Error, data['error_message'] if data['status'] != 'success'
|
130
188
|
|
131
189
|
data['result']
|
132
190
|
end
|
133
191
|
|
192
|
+
# Get the snapshot of the current flag state
|
193
|
+
def snapshot
|
194
|
+
resp, ptr = self.class.get_snapshot(@engine)
|
195
|
+
ptr = FFI::AutoPointer.new(ptr, Client.method(:destroy_string))
|
196
|
+
resp
|
197
|
+
end
|
198
|
+
|
134
199
|
private
|
135
200
|
|
136
|
-
def validate_evaluation_request(
|
137
|
-
if
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
201
|
+
def validate_evaluation_request(flag_key, entity_id, context)
|
202
|
+
raise ValidationError, 'flag_key is required' if flag_key.nil? || flag_key.empty?
|
203
|
+
raise ValidationError, 'entity_id is required' if entity_id.nil? || entity_id.empty?
|
204
|
+
return if context.is_a?(Hash)
|
205
|
+
|
206
|
+
raise ValidationError, 'context must be a Hash<String, String>'
|
142
207
|
end
|
143
208
|
|
144
209
|
def validate_authentication(authentication)
|
145
210
|
return authentication.strategy if authentication.is_a?(AuthenticationStrategy)
|
146
211
|
|
147
|
-
raise
|
212
|
+
raise ValidationError, 'invalid authentication strategy'
|
148
213
|
end
|
149
214
|
|
150
215
|
def validate_fetch_mode(fetch_mode)
|
151
216
|
return fetch_mode if %i[polling streaming].include?(fetch_mode)
|
152
217
|
|
153
|
-
raise
|
218
|
+
raise ValidationError, 'invalid fetch mode'
|
154
219
|
end
|
155
220
|
|
156
221
|
def validate_error_strategy(error_strategy)
|
157
222
|
return error_strategy if %i[fail fallback].include?(error_strategy)
|
158
223
|
|
159
|
-
raise
|
224
|
+
raise ValidationError, 'invalid error strategy'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# Deprecation shim for EvaluationClient
|
229
|
+
class EvaluationClient < Client
|
230
|
+
def initialize(*args, **kwargs)
|
231
|
+
warn '[DEPRECATION] `EvaluationClient` is deprecated. Please use `Client` instead.'
|
232
|
+
super
|
160
233
|
end
|
161
234
|
end
|
162
235
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipt_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flipt Devs
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Flipt Client Evaluation SDK
|
14
14
|
email:
|
@@ -42,7 +42,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
42
|
requirements:
|
43
43
|
- - ">="
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 2.
|
45
|
+
version: 2.7.0
|
46
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
48
|
- - ">="
|