Skip to content

Commit a300fc5

Browse files
josecolellaclaude
andauthored
feat: add flag metadata defaulting and immutability (#221)
Signed-off-by: Jose Colella <jose.colella@gusto.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 844fd52 commit a300fc5

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

lib/open_feature/sdk/provider/resolution_details.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,20 @@
33
module OpenFeature
44
module SDK
55
module Provider
6-
ResolutionDetails = Struct.new(:value, :reason, :variant, :error_code, :error_message, :flag_metadata, keyword_init: true)
6+
EMPTY_FLAG_METADATA = {}.freeze
7+
8+
ResolutionDetails = Struct.new(:value, :reason, :variant, :error_code, :error_message, :flag_metadata, keyword_init: true) do
9+
def flag_metadata
10+
raw = self[:flag_metadata]
11+
if raw.nil?
12+
EMPTY_FLAG_METADATA
13+
elsif raw.frozen?
14+
raw
15+
else
16+
raw.dup.freeze
17+
end
18+
end
19+
end
720
end
821
end
922
end

spec/specification/flag_evaluation_api_spec.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,54 @@
222222
end
223223
end
224224

225+
context "1.4 - Flag Metadata" do
226+
context "Requirement 1.4.14" do
227+
specify "flag_metadata defaults to an empty hash when not provided" do
228+
resolution = OpenFeature::SDK::Provider::ResolutionDetails.new(value: true)
229+
expect(resolution.flag_metadata).to eq({})
230+
end
231+
232+
specify "flag_metadata defaults to an empty hash when explicitly set to nil" do
233+
resolution = OpenFeature::SDK::Provider::ResolutionDetails.new(value: true, flag_metadata: nil)
234+
expect(resolution.flag_metadata).to eq({})
235+
end
236+
237+
specify "flag_metadata preserves provided values" do
238+
metadata = {"scope" => "user", "version" => 2}
239+
resolution = OpenFeature::SDK::Provider::ResolutionDetails.new(value: true, flag_metadata: metadata)
240+
expect(resolution.flag_metadata).to eq({"scope" => "user", "version" => 2})
241+
end
242+
243+
specify "flag_metadata defaults correctly through client evaluation" do
244+
provider = OpenFeature::SDK::Provider::NoOpProvider.new
245+
OpenFeature::SDK.set_provider(provider)
246+
client = OpenFeature::SDK.build_client
247+
248+
details = client.fetch_boolean_details(flag_key: "test", default_value: false)
249+
expect(details.flag_metadata).to eq({})
250+
end
251+
end
252+
253+
context "Requirement 1.4.15.1" do
254+
specify "flag_metadata is frozen when not provided" do
255+
resolution = OpenFeature::SDK::Provider::ResolutionDetails.new(value: true)
256+
expect(resolution.flag_metadata).to be_frozen
257+
end
258+
259+
specify "flag_metadata is frozen when provided" do
260+
metadata = {"scope" => "user"}
261+
resolution = OpenFeature::SDK::Provider::ResolutionDetails.new(value: true, flag_metadata: metadata)
262+
expect(resolution.flag_metadata).to be_frozen
263+
end
264+
265+
specify "flag_metadata cannot be mutated" do
266+
metadata = {"scope" => "user"}
267+
resolution = OpenFeature::SDK::Provider::ResolutionDetails.new(value: true, flag_metadata: metadata)
268+
expect { resolution.flag_metadata["new_key"] = "value" }.to raise_error(FrozenError)
269+
end
270+
end
271+
end
272+
225273
context "Logger Methods" do
226274
specify "delegates logger getter to configuration" do
227275
logger = double("Logger")

0 commit comments

Comments
 (0)