Skip to content

Commit 4b3c2a8

Browse files
authored
fix: accept symbol feature flag keys (#147)
* fix: accept symbol feature flag keys * refactor: cleanup and simplify
1 parent 3561fd3 commit 4b3c2a8

6 files changed

Lines changed: 93 additions & 6 deletions

File tree

.changeset/symbol-flag-keys.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'posthog-ruby': patch
3+
---
4+
5+
Accept symbol feature flag keys in flag APIs.

lib/posthog/client.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -364,15 +364,15 @@ def is_feature_enabled( # rubocop:disable Naming/PredicateName
364364
!!response
365365
end
366366

367-
# @param [String] flag_key The unique flag key of the feature flag
367+
# @param [String, Symbol] flag_key The unique flag key of the feature flag
368368
# @return [String] The decrypted value of the feature flag payload
369369
def get_remote_config_payload(flag_key)
370-
@feature_flags_poller.get_remote_config_payload(flag_key)
370+
@feature_flags_poller.get_remote_config_payload(flag_key.to_s)
371371
end
372372

373373
# Returns whether the given feature flag is enabled for the given user or not
374374
#
375-
# @param [String] key The key of the feature flag
375+
# @param [String, Symbol] key The key of the feature flag
376376
# @param [String] distinct_id The distinct id of the user
377377
# @param [Hash] groups
378378
# @param [Hash] person_properties key-value pairs of properties to associate with the user.
@@ -455,7 +455,7 @@ def get_feature_flag_result(
455455
# @param [Hash] group_properties
456456
# @param [Boolean] only_evaluate_locally Skip the remote /flags call entirely
457457
# @param [Boolean] disable_geoip Stamped on captured access events
458-
# @param [Array<String>] flag_keys When set, scopes the underlying /flags
458+
# @param [Array<String, Symbol>] flag_keys When set, scopes the underlying /flags
459459
# request to only these flag keys (sent as `flag_keys_to_evaluate`).
460460
# Distinct from {FeatureFlagEvaluations#only}, which filters the
461461
# already-fetched snapshot in memory.
@@ -598,7 +598,7 @@ def get_all_flags(
598598
# @deprecated Use {#get_feature_flag_result} instead, which returns both the flag value and payload
599599
# and properly raises the $feature_flag_called event.
600600
#
601-
# @param [String] key The key of the feature flag
601+
# @param [String, Symbol] key The key of the feature flag
602602
# @param [String] distinct_id The distinct id of the user
603603
# @option [String or boolean] match_value The value of the feature flag to be matched
604604
# @option [Hash] groups
@@ -623,6 +623,7 @@ def get_feature_flag_payload(
623623
'instead — this consolidates flag evaluation into a single `/flags` request per ' \
624624
'incoming request.'
625625
)
626+
key = key.to_s
626627
person_properties, group_properties = add_local_person_and_group_properties(distinct_id, groups,
627628
person_properties, group_properties)
628629
@feature_flags_poller.get_feature_flag_payload(key, distinct_id, match_value, groups, person_properties,
@@ -725,6 +726,7 @@ def _get_feature_flag_result(
725726
only_evaluate_locally: false,
726727
send_feature_flag_events: true
727728
)
729+
key = key.to_s
728730
person_properties, group_properties = add_local_person_and_group_properties(
729731
distinct_id, groups, person_properties, group_properties
730732
)

lib/posthog/feature_flags.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def get_feature_payloads(
125125

126126
def get_flags(distinct_id, groups = {}, person_properties = {}, group_properties = {}, flag_keys = nil,
127127
disable_geoip = nil)
128+
flag_keys = flag_keys.map(&:to_s) if flag_keys.is_a?(Array)
128129
request_data = {
129130
distinct_id: distinct_id,
130131
groups: groups,
@@ -160,7 +161,7 @@ def get_flags(distinct_id, groups = {}, person_properties = {}, group_properties
160161
end
161162

162163
def get_remote_config_payload(flag_key)
163-
_request_remote_config_payload(flag_key)
164+
_request_remote_config_payload(flag_key.to_s)
164165
end
165166

166167
def get_feature_flag(
@@ -171,6 +172,8 @@ def get_feature_flag(
171172
group_properties = {},
172173
only_evaluate_locally = false
173174
)
175+
key = key.to_s
176+
174177
# make sure they're loaded on first run
175178
load_feature_flags
176179

@@ -363,6 +366,8 @@ def get_feature_flag_payload(
363366
group_properties = {},
364367
only_evaluate_locally = false
365368
)
369+
key = key.to_s
370+
366371
if match_value.nil?
367372
match_value = get_feature_flag(
368373
key,
@@ -923,6 +928,7 @@ def _compute_flag_locally(flag, distinct_id, groups = {}, person_properties = {}
923928
def _compute_flag_payload_locally(key, match_value)
924929
return nil if @feature_flags_by_key.nil?
925930

931+
key = key.to_s
926932
response = nil
927933
if [true, false].include? match_value
928934
response = @feature_flags_by_key.dig(key, :filters, :payloads, match_value.to_s.to_sym)

spec/posthog/feature_flag_evaluations_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,16 @@ def capture_stderr
104104
expect(unknown[:properties]['$feature_flag_error']).to eq('flag_missing')
105105
end
106106

107+
it 'accepts symbol flag keys when reading a snapshot' do
108+
stub_flags(flags_response)
109+
snapshot = client.evaluate_flags('user-1')
110+
111+
expect(snapshot.enabled?(:'boolean-flag')).to be(true)
112+
expect(snapshot.get_flag(:'variant-flag')).to eq('variant-value')
113+
expect(snapshot.get_flag_payload(:'variant-flag')).to eq('key' => 'value')
114+
expect(snapshot.only(:'boolean-flag').keys).to eq(['boolean-flag'])
115+
end
116+
107117
it 'enabled? returns false for unknown flags' do
108118
stub_flags(flags_response)
109119
snapshot = client.evaluate_flags('user-1')
@@ -156,6 +166,14 @@ def capture_stderr
156166
)
157167
end
158168

169+
it 'accepts symbol keys in the flag_keys filter array' do
170+
stub_flags(flags_response)
171+
client.evaluate_flags('user-1', flag_keys: [:'boolean-flag'])
172+
expect(WebMock).to have_requested(:post, FLAGS_ENDPOINT).with(
173+
body: hash_including(flag_keys_to_evaluate: %w[boolean-flag])
174+
)
175+
end
176+
159177
it 'returns a usable empty snapshot for empty distinct_id and does not call /flags' do
160178
stub_flags(flags_response)
161179
snapshot = client.evaluate_flags('')

spec/posthog/feature_flag_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,42 @@ module PostHog
5757
person_properties: { 'region' => 'Canada' })).to eq(false)
5858
end
5959

60+
it 'accepts symbol flag keys when evaluating locally' do
61+
api_feature_flag_res = {
62+
'flags' => [
63+
{
64+
'id' => 1,
65+
'name' => 'Symbol Flag',
66+
'key' => 'symbol-flag',
67+
'is_simple_flag' => true,
68+
'active' => true,
69+
'filters' => {
70+
'groups' => [{ 'rollout_percentage' => 100 }],
71+
'payloads' => { 'true' => '{"source":"local"}' }
72+
}
73+
}
74+
]
75+
}
76+
stub_request(
77+
:get,
78+
'https://us.i.posthog.com/flags/definitions?token=testsecret&send_cohorts=true'
79+
).to_return(status: 200, body: api_feature_flag_res.to_json)
80+
stub_request(:post, flags_endpoint).to_return(status: 400)
81+
82+
c = Client.new(api_key: API_KEY, personal_api_key: API_KEY, test_mode: true)
83+
84+
expect(c.get_feature_flag(:'symbol-flag', 'some-distinct-id',
85+
send_feature_flag_events: false)).to eq(true)
86+
expect(c.is_feature_enabled(:'symbol-flag', 'some-distinct-id',
87+
send_feature_flag_events: false)).to eq(true)
88+
89+
result = c.get_feature_flag_result(:'symbol-flag', 'some-distinct-id', send_feature_flag_events: false)
90+
expect(result.key).to eq('symbol-flag')
91+
expect(result.value).to eq(true)
92+
expect(c.get_feature_flag_payload(:'symbol-flag', 'some-distinct-id')).to eq('{"source":"local"}')
93+
assert_not_requested :post, flags_endpoint
94+
end
95+
6096
it 'evaluates group properties' do
6197
api_feature_flag_res = {
6298
'flags' => [

spec/posthog/flags_spec.rb

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,15 @@ module PostHog
361361
})
362362
)
363363
end
364+
365+
it 'accepts symbol flag keys' do
366+
stub_request(:post, flags_endpoint)
367+
.to_return(status: 200, body: flags_v4_response.to_json)
368+
369+
expect(client.get_feature_flag(:'enabled-flag', 'test-distinct-id',
370+
send_feature_flag_events: false)).to eq(true)
371+
expect(client.get_feature_flag_payload(:'enabled-flag', 'test-distinct-id')).to eq('{"foo": 1}')
372+
end
364373
end
365374
end
366375

@@ -400,6 +409,17 @@ module PostHog
400409
# Verify the request was made to the correct URL with token parameter
401410
expect(WebMock).to have_requested(:get, remote_config_endpoint)
402411
end
412+
413+
it 'accepts a symbol flag key' do
414+
remote_config_response = { test: 'payload' }
415+
stub_request(:get, remote_config_endpoint)
416+
.to_return(status: 200, body: remote_config_response.to_json)
417+
418+
result = poller.get_remote_config_payload(:'test-flag')
419+
420+
expect(result[:test]).to eq('payload')
421+
expect(WebMock).to have_requested(:get, remote_config_endpoint)
422+
end
403423
end
404424

405425
describe 'Cohort evaluation' do

0 commit comments

Comments
 (0)