Skip to content

Commit 6a9c4b8

Browse files
committed
feat: Support transient identities and traits
- Add optional `transient` argument to `get_identity_flags` - Add support for trait configuration values with optional `transient` key - Remove unneeded default argument values from private methods - Remove unneeded comments from the spec
1 parent 0ce354a commit 6a9c4b8

File tree

4 files changed

+57
-46
lines changed

4 files changed

+57
-46
lines changed

lib/flagsmith.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,13 @@ def get_environment_flags # rubocop:disable Naming/AccessorMethodName
148148
# environment, e.g. email address, username, uuid
149149
# traits { key => value } is a dictionary of traits to add / update on the identity in
150150
# Flagsmith, e.g. { "num_orders": 10 }
151+
# in lieu of a trait value, a trait coniguration dictionary can be provided,
152+
# e.g. { "num_orders": { "value": 10, "transient": true } }
151153
# returns Flags object holding all the flags for the given identity.
152-
def get_identity_flags(identifier, **traits)
154+
def get_identity_flags(identifier, transient = false, **traits) # rubocop:disable Style/OptionalBooleanParameter
153155
return get_identity_flags_from_document(identifier, traits) if environment
154156

155-
get_identity_flags_from_api(identifier, traits)
157+
get_identity_flags_from_api(identifier, traits, transient)
156158
end
157159

158160
def feature_enabled?(feature_name, default: false)
@@ -253,16 +255,16 @@ def process_environment_flags_from_api
253255
end
254256

255257
# rubocop:disable Metrics/MethodLength
256-
def get_identity_flags_from_api(identifier, traits = {})
258+
def get_identity_flags_from_api(identifier, traits, transient)
257259
if offline_handler
258260
begin
259-
process_identity_flags_from_api(identifier, traits)
261+
process_identity_flags_from_api(identifier, traits, transient)
260262
rescue StandardError
261263
get_identity_flags_from_document(identifier, traits)
262264
end
263265
else
264266
begin
265-
process_identity_flags_from_api(identifier, traits)
267+
process_identity_flags_from_api(identifier, traits, transient)
266268
rescue StandardError
267269
if default_flag_handler
268270
return Flagsmith::Flags::Collection.new(
@@ -276,8 +278,8 @@ def get_identity_flags_from_api(identifier, traits = {})
276278
end
277279
# rubocop:enable Metrics/MethodLength
278280

279-
def process_identity_flags_from_api(identifier, traits = {})
280-
data = generate_identities_data(identifier, traits)
281+
def process_identity_flags_from_api(identifier, traits, transient)
282+
data = generate_identities_data(identifier, traits, transient)
281283
json_response = api_client.post(@config.identities_url, data.to_json).body
282284

283285
Flagsmith::Flags::Collection.from_api(
@@ -311,10 +313,13 @@ def get_identity_model(identifier, traits = {})
311313
end
312314
# rubocop:enable Metrics/MethodLength
313315

314-
def generate_identities_data(identifier, traits = {})
316+
def generate_identities_data(identifier, traits, transient)
315317
{
316318
identifier: identifier,
317-
traits: traits.map { |key, value| { trait_key: key, trait_value: value } }
319+
transient: transient,
320+
traits: traits.map do |key, value|
321+
value.is_a?(Hash) ? { trait_key: key, trait_value: value[:value], transient: value[:transient] || false } : { trait_key: key, trait_value: value }
322+
end
318323
}
319324
end
320325
end

spec/sdk/fixtures/identities.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@
5959
{
6060
"id": 215352,
6161
"trait_key": "roles",
62-
"trait_value": "[\"admin\",\"staff\"]"
62+
"trait_value": "[\"admin\",\"staff\"]",
63+
"transient": false
6364
},
6465
{
6566
"id": 215988,
6667
"trait_key": "foo",
67-
"trait_value": "bar"
68+
"trait_value": "bar",
69+
"transient": false
6870
}
6971
]
70-
}
72+
}

spec/sdk/flagsmith_spec.rb

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -62,39 +62,6 @@
6262
end
6363
end
6464

65-
# describe '#set_trait' do
66-
# let(:trait_key) { 'foo' }
67-
# let(:trait_value) { 'bar' }
68-
# let(:post_body) do
69-
# {
70-
# identity: { identifier: user_id },
71-
# trait_key: Flagsmith::Flags::Collection.normalize_key(trait_key),
72-
# trait_value: trait_value
73-
# }.to_json
74-
# end
75-
#
76-
# it 'sets a trait for a given user' do
77-
# trait_response = OpenStruct.new(body: {})
78-
# expect(mock_api_client).to receive(:post).with('traits/', post_body).and_return(trait_response)
79-
# subject.set_trait user_id, trait_key, trait_value
80-
# end
81-
#
82-
# it 'errors if user_id.nil?' do
83-
# expect { subject.set_trait nil, trait_key, trait_value }.to raise_error(StandardError)
84-
# end
85-
# end
86-
87-
# describe '#get_traits' do
88-
# it 'returns hash of traits for a given user' do
89-
# traits = subject.get_traits(user_id)
90-
# expect(traits['roles']).to eq(%w[admin staff].to_json)
91-
# expect(traits.length).to eq(2)
92-
# end
93-
# it 'returns {} for user_id.nil?' do
94-
# expect(subject.get_traits(nil)).to eq({})
95-
# end
96-
# end
97-
9865
describe '#normalize_key' do
9966
it 'returns an empty string given nil' do
10067
expect(Flagsmith::Flags::Collection.normalize_key(nil)).to eq('')
@@ -122,4 +89,41 @@
12289
expect(subject.get_identity_segments("identifier", {"age": 39}).length).to eq(1)
12390
end
12491
end
92+
93+
describe '#get_identity_flags' do
94+
context 'with transient identity' do
95+
before {
96+
transient_identities_response = OpenStruct.new(body: { **identities_response.body } )
97+
transient_identities_response.body[:traits] = identities_response.body[:traits].map{ |api_trait| { **api_trait, transient: true } }
98+
allow(mock_api_client).to receive(:post).with(
99+
'identities/', { identifier: user_id, transient: true, traits: [] }.to_json
100+
).and_return(transient_identities_response)
101+
}
102+
103+
it 'sends expected request' do
104+
flags = subject.get_identity_flags(user_id, true)
105+
expect(flags.feature_enabled?(:feature_one)).to eq(false)
106+
expect(flags.feature_enabled?('feature_two')).to eq(true)
107+
expect(flags.feature_enabled?(:feature_three)).to eq(true)
108+
end
109+
end
110+
111+
context 'with transient trait' do
112+
before {
113+
transient_trait_data = { trait_key: "age", trait_value: 42, transient: true }
114+
transient_traits_response = OpenStruct.new(body: { **identities_response.body } )
115+
transient_traits_response.body[:traits] = transient_traits_response.body[:traits] + [transient_trait_data]
116+
allow(mock_api_client).to receive(:post).with(
117+
'identities/', { identifier: user_id, transient: false, traits: [transient_trait_data] }.to_json
118+
).and_return(transient_traits_response)
119+
}
120+
121+
it 'sends expected request' do
122+
flags = subject.get_identity_flags(user_id, age: { value: 42, transient: true })
123+
expect(flags.feature_enabled?(:feature_one)).to eq(false)
124+
expect(flags.feature_enabled?('feature_two')).to eq(true)
125+
expect(flags.feature_enabled?(:feature_three)).to eq(true)
126+
end
127+
end
128+
end
125129
end

spec/sdk/shared_mocks.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
.and_return(mock_api_client)
3131
allow(mock_api_client).to receive(:get).with('flags/').and_return(flags_response)
3232
allow(mock_api_client).to receive(:post).with(
33-
'identities/', { identifier: user_id, traits: [] }.to_json
33+
'identities/', { identifier: user_id, transient: false, traits: [] }.to_json
3434
).and_return(identities_response)
3535
allow(mock_api_client).to receive(:get).with('environment-document/')
3636
.and_return(environment_document_response)

0 commit comments

Comments
 (0)