openfeature-flagsmith-provider 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7194e7bbb567a6f66ad964bde2bfd58544e6091c5da1a721ce076d9ae91b94fc
4
+ data.tar.gz: ece05c570cee2aeffecd0375a7771e6cae9a616f029a9a1d9d54a9d4843a21a7
5
+ SHA512:
6
+ metadata.gz: a1bc411152743dacbf30ca78fed7820733e72db3e732e967e2e8b4cd80528ddfb0c61f204d7f3f41b3de90386093fbf95e005d9d3bdd4def6d6ca2d4e6c67434
7
+ data.tar.gz: deb6a3d707a7a680b273f292f7976c28ee4f0c826671616e443cd846055d961c9a091f067bc34a069745cf9583608876de223f0eea08df1a34803342a0845768
data/.context.md ADDED
@@ -0,0 +1,316 @@
1
+ # Flagsmith OpenFeature Provider - Context
2
+
3
+ **Started:** 2025-11-17
4
+ **Full Design Doc:** `./FLAGSMITH_PROVIDER_DESIGN.md`
5
+
6
+ ---
7
+
8
+ ## Quick Facts
9
+
10
+ **What we're building:** OpenFeature provider for Flagsmith Ruby SDK
11
+ **Target Flagsmith version:** Upcoming release (TBD)
12
+
13
+ ---
14
+
15
+ ## Key Design Decisions ✅
16
+
17
+ | Decision | Choice | Rationale |
18
+ |----------|--------|-----------|
19
+ | Evaluation mode | Remote (default) | Simpler, no polling. Local available via config |
20
+ | No targeting_key | Fall back to environment flags | `get_environment_flags()` vs `get_identity_flags()` |
21
+ | Analytics | Opt-in (disabled default) | Privacy-first approach |
22
+ | Default values | Use OpenFeature's default_value | Match other providers, no custom handler |
23
+ | Configuration | Options object pattern | Clear validation, like GO Feature Flag |
24
+
25
+ ---
26
+
27
+ ## Architecture Map
28
+
29
+ ### Directory Structure
30
+ ```
31
+ openfeature-flagsmith-provider/
32
+ ├── lib/openfeature/flagsmith/
33
+ │ ├── provider.rb # Main provider class
34
+ │ ├── configuration.rb # Options/config with validation
35
+ │ ├── error/errors.rb # Custom exceptions → ErrorCode mapping
36
+ │ └── version.rb # VERSION constant
37
+ ├── spec/ # RSpec tests with WebMock
38
+ ├── openfeature-flagsmith-provider.gemspec
39
+ └── README.md
40
+ ```
41
+
42
+ ### Key Classes
43
+
44
+ **Configuration** (lib/openfeature/flagsmith/configuration.rb):
45
+ ```ruby
46
+ OpenFeature::Flagsmith::Configuration.new(
47
+ environment_key: "required",
48
+ api_url: "https://edge.api.flagsmith.com/api/v1/",
49
+ enable_local_evaluation: false,
50
+ request_timeout_seconds: 10,
51
+ enable_analytics: false
52
+ )
53
+ ```
54
+
55
+ **Provider** (lib/openfeature/flagsmith/provider.rb):
56
+ ```ruby
57
+ class Provider
58
+ attr_reader :metadata
59
+
60
+ # Required methods:
61
+ def fetch_boolean_value(flag_key:, default_value:, evaluation_context:)
62
+ def fetch_string_value(flag_key:, default_value:, evaluation_context:)
63
+ def fetch_number_value(flag_key:, default_value:, evaluation_context:)
64
+ def fetch_integer_value(flag_key:, default_value:, evaluation_context:)
65
+ def fetch_float_value(flag_key:, default_value:, evaluation_context:)
66
+ def fetch_object_value(flag_key:, default_value:, evaluation_context:)
67
+ end
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Critical Mappings
73
+
74
+ ### 1. Context → Identity/Traits
75
+ ```ruby
76
+ # OpenFeature → Flagsmith
77
+ evaluation_context.targeting_key → identifier (for get_identity_flags)
78
+ evaluation_context.fields → traits (as keyword args)
79
+
80
+ # If no targeting_key → use get_environment_flags()
81
+ ```
82
+
83
+ ### 2. Flag Types
84
+ | Flagsmith | OpenFeature Method | Implementation |
85
+ |-----------|-------------------|----------------|
86
+ | `is_feature_enabled()` → Boolean | `fetch_boolean_value` | Direct mapping |
87
+ | `get_feature_value()` → String | `fetch_string_value` | Direct return |
88
+ | `get_feature_value()` → Number | `fetch_number_value` | Parse & validate type |
89
+ | `get_feature_value()` → JSON | `fetch_object_value` | `JSON.parse()` |
90
+
91
+ ### 3. Reason Mapping
92
+ | Situation | OpenFeature Reason |
93
+ |-----------|-------------------|
94
+ | Flag evaluated with identity | `TARGETING_MATCH` |
95
+ | Flag evaluated at environment level | `STATIC` |
96
+ | Flag not found | `DEFAULT` |
97
+ | Flag exists but disabled | `DISABLED` |
98
+ | Error occurred | `ERROR` |
99
+
100
+ ### 4. Error Handling
101
+ ```ruby
102
+ # Pattern: Always return ResolutionDetails with default_value on error
103
+ SDK::Provider::ResolutionDetails.new(
104
+ value: default_value,
105
+ error_code: <appropriate ErrorCode>,
106
+ error_message: "description",
107
+ reason: SDK::Provider::Reason::ERROR
108
+ )
109
+ ```
110
+
111
+ **ErrorCode mapping:**
112
+ - Flag not found → `FLAG_NOT_FOUND`
113
+ - Wrong type → `TYPE_MISMATCH`
114
+ - Network error → `PROVIDER_NOT_READY` or `GENERAL`
115
+ - Invalid context → `INVALID_CONTEXT`
116
+
117
+ ---
118
+
119
+ ## Flagsmith SDK API Reference
120
+
121
+ ### Initialization
122
+ ```ruby
123
+ require "flagsmith"
124
+ client = Flagsmith::Client.new(
125
+ environment_key: "key",
126
+ api_url: "url",
127
+ enable_local_evaluation: false,
128
+ request_timeout_seconds: 10,
129
+ enable_analytics: false
130
+ )
131
+ ```
132
+
133
+ ### Evaluation Methods
134
+ ```ruby
135
+ # Environment-level (no user)
136
+ flags = client.get_environment_flags()
137
+ flags.is_feature_enabled('flag_key') # → Boolean
138
+ flags.get_feature_value('flag_key') # → String/value
139
+
140
+ # Identity-specific (with user context)
141
+ flags = client.get_identity_flags('user@example.com', trait1: 'value', age: 30)
142
+ flags.is_feature_enabled('flag_key')
143
+ flags.get_feature_value('flag_key')
144
+ ```
145
+
146
+ ---
147
+
148
+ ## OpenFeature Provider Contract
149
+
150
+ ### Required Interface
151
+ ```ruby
152
+ # Metadata
153
+ attr_reader :metadata # ProviderMetadata.new(name: "Flagsmith Provider")
154
+
155
+ # Lifecycle (optional)
156
+ def init # Optional initialization
157
+ def shutdown # Optional cleanup
158
+
159
+ # Evaluation methods (required)
160
+ def fetch_boolean_value(flag_key:, default_value:, evaluation_context: nil)
161
+ def fetch_string_value(flag_key:, default_value:, evaluation_context: nil)
162
+ def fetch_number_value(flag_key:, default_value:, evaluation_context: nil)
163
+ def fetch_integer_value(flag_key:, default_value:, evaluation_context: nil)
164
+ def fetch_float_value(flag_key:, default_value:, evaluation_context: nil)
165
+ def fetch_object_value(flag_key:, default_value:, evaluation_context: nil)
166
+ ```
167
+
168
+ ### Return Type
169
+ ```ruby
170
+ OpenFeature::SDK::Provider::ResolutionDetails.new(
171
+ value: <evaluated_value>, # Required
172
+ reason: <Reason constant>, # Required
173
+ variant: "variant_key", # Optional
174
+ flag_metadata: {key: "value"}, # Optional
175
+ error_code: <ErrorCode constant>, # If error
176
+ error_message: "details" # If error
177
+ )
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Code Patterns to Follow
183
+
184
+ ### Type Validation Pattern
185
+ ```ruby
186
+ def evaluate(flag_key:, default_value:, allowed_classes:, evaluation_context:)
187
+ # ... get value from Flagsmith ...
188
+
189
+ unless allowed_classes.include?(value.class)
190
+ return SDK::Provider::ResolutionDetails.new(
191
+ value: default_value,
192
+ error_code: SDK::Provider::ErrorCode::TYPE_MISMATCH,
193
+ error_message: "Expected #{allowed_classes}, got #{value.class}",
194
+ reason: SDK::Provider::Reason::ERROR
195
+ )
196
+ end
197
+
198
+ # return success ResolutionDetails
199
+ end
200
+ ```
201
+
202
+ ### Error Rescue Pattern
203
+ ```ruby
204
+ begin
205
+ # Flagsmith evaluation
206
+ rescue SomeError => e
207
+ return SDK::Provider::ResolutionDetails.new(
208
+ value: default_value,
209
+ error_code: SDK::Provider::ErrorCode::GENERAL,
210
+ error_message: e.message,
211
+ reason: SDK::Provider::Reason::ERROR
212
+ )
213
+ end
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Dependencies
219
+
220
+ ### Runtime
221
+ ```ruby
222
+ spec.add_runtime_dependency "openfeature-sdk", "~> 0.3.1"
223
+ spec.add_runtime_dependency "flagsmith", "~> <VERSION_TBD>"
224
+ ```
225
+
226
+ ### Development
227
+ ```ruby
228
+ spec.add_development_dependency "rake", "~> 13.0"
229
+ spec.add_development_dependency "rspec", "~> 3.12.0"
230
+ spec.add_development_dependency "webmock", "~> 3.0" # Mock Flagsmith calls
231
+ spec.add_development_dependency "standard"
232
+ spec.add_development_dependency "rubocop"
233
+ spec.add_development_dependency "simplecov"
234
+ ```
235
+
236
+ ---
237
+
238
+ ## Testing Strategy
239
+
240
+ ### Mock Flagsmith Responses
241
+ ```ruby
242
+ # Use WebMock to stub Flagsmith API calls
243
+ # OR stub Flagsmith::Client methods directly
244
+
245
+ allow(flagsmith_client).to receive(:get_identity_flags).and_return(mock_flags)
246
+ allow(mock_flags).to receive(:is_feature_enabled).and_return(true)
247
+ allow(mock_flags).to receive(:get_feature_value).and_return("value")
248
+ ```
249
+
250
+ ### Test Coverage Areas
251
+ - ✅ Metadata verification
252
+ - ✅ Configuration validation
253
+ - ✅ Each flag type evaluation (boolean, string, number, integer, float, object)
254
+ - ✅ Type mismatches
255
+ - ✅ Missing flags (return default)
256
+ - ✅ Error handling
257
+ - ✅ Context mapping (with/without targeting_key)
258
+ - ✅ Environment vs Identity evaluation
259
+
260
+ ---
261
+
262
+ ## Implementation Checklist
263
+
264
+ - [ ] Directory structure created
265
+ - [ ] Gemspec with dependencies
266
+ - [ ] Configuration class with validation
267
+ - [ ] Provider skeleton with metadata
268
+ - [ ] Context → Identity/Traits mapping helper
269
+ - [ ] `fetch_boolean_value` implementation
270
+ - [ ] `fetch_string_value` implementation
271
+ - [ ] `fetch_number_value` implementation
272
+ - [ ] `fetch_integer_value` implementation
273
+ - [ ] `fetch_float_value` implementation
274
+ - [ ] `fetch_object_value` implementation
275
+ - [ ] Error handling and custom exceptions
276
+ - [ ] Type validation
277
+ - [ ] RSpec test suite
278
+ - [ ] README with examples
279
+ - [ ] Release configuration
280
+
281
+ ---
282
+
283
+ ## Open Items / Notes
284
+
285
+ - **Flagsmith version**: Waiting for upcoming release - update gemspec when available
286
+ - **Variant support**: Flagsmith doesn't have explicit variants - TBD how to handle
287
+ - **Flag metadata**: Flagsmith has limited metadata - may need to extract from traits/response
288
+
289
+ ---
290
+
291
+ ## Quick Commands
292
+
293
+ ```bash
294
+ # Run tests
295
+ bundle exec rspec
296
+
297
+ # Run linter
298
+ bundle exec rubocop
299
+
300
+ # Install locally for testing
301
+ gem build openfeature-flagsmith-provider.gemspec
302
+ gem install ./openfeature-flagsmith-provider-<version>.gem
303
+
304
+ # Test with real Flagsmith
305
+ # (add example script in spec/manual_test.rb)
306
+ ```
307
+
308
+ ---
309
+
310
+ ## References
311
+
312
+ - Full design doc: `../FLAGSMITH_PROVIDER_DESIGN.md`
313
+ - Flagsmith docs: https://docs.flagsmith.com/clients/server-side
314
+ - OpenFeature spec: https://openfeature.dev/specification/
315
+ - GO Feature Flag provider: `../openfeature-go-feature-flag-provider/`
316
+ - flagd provider: `../openfeature-flagd-provider/`
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ -I lib
2
+ --format documentation
3
+ --color
4
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,5 @@
1
+ inherit_from: ../../shared_config/.rubocop.yml
2
+
3
+ inherit_mode:
4
+ merge:
5
+ - Exclude
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.1.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,22 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.1](https://github.com/open-feature/ruby-sdk-contrib/compare/openfeature-flagsmith-provider-v0.1.0...openfeature-flagsmith-provider/v0.1.1) (2025-11-27)
9
+
10
+
11
+ ### ✨ New Features
12
+
13
+ * add flagsmith provider ([#68](https://github.com/open-feature/ruby-sdk-contrib/issues/68)) ([9e3216a](https://github.com/open-feature/ruby-sdk-contrib/commit/9e3216ac67d1eeb12f423a4c0615442ac52b2a17))
14
+
15
+ ## [Unreleased]
16
+
17
+ ### Added
18
+ - Initial implementation of Flagsmith OpenFeature provider
19
+ - Support for all OpenFeature flag types (boolean, string, number, integer, float, object)
20
+ - Remote and local evaluation modes
21
+ - Environment-level and identity-specific flag evaluation
22
+ - Comprehensive error handling and type validation