Skip to content

Commit 9bc9d75

Browse files
authored
Merge pull request #50 from Flagsmith/feat/identity_overrides
feat: Add identity overrides to client
2 parents 24443cc + 66896eb commit 9bc9d75

File tree

4 files changed

+88
-8
lines changed

4 files changed

+88
-8
lines changed

lib/flagsmith.rb

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@ class Client # rubocop:disable Metrics/ClassLength
4949
#
5050
# You can see full description in the Flagsmith::Config
5151

52-
attr_reader :config, :environment
52+
attr_reader :config, :environment, :identity_overrides_by_identifier
5353

5454
delegate Flagsmith::Config::OPTIONS => :@config
5555

5656
def initialize(config)
5757
@_mutex = Mutex.new
5858
@config = Flagsmith::Config.new(config)
59+
@identity_overrides_by_identifier = {}
5960

6061
validate_offline_mode!
6162

@@ -113,6 +114,16 @@ def environment_data_polling_manager
113114
# You only need to call this if you wish to bypass environment_refresh_interval_seconds.
114115
def update_environment
115116
@_mutex.synchronize { @environment = environment_from_api }
117+
update_identity_overrides
118+
end
119+
120+
def update_identity_overrides
121+
return unless @environment
122+
123+
@identity_overrides_by_identifier = {}
124+
@environment.identity_overrides.each do |identity|
125+
@identity_overrides_by_identifier[identity.identifier] = identity
126+
end
116127
end
117128

118129
def environment_from_api
@@ -177,7 +188,7 @@ def get_identity_segments(identifier, traits = {})
177188
'Local evaluation or offline handler is required to obtain identity segments.'
178189
end
179190

180-
identity_model = build_identity_model(identifier, traits)
191+
identity_model = get_identity_model(identifier, traits)
181192
segment_models = engine.get_identity_segments(environment, identity_model)
182193
segment_models.map { |sm| Flagsmith::Segments::Segment.new(id: sm.id, name: sm.name) }.compact
183194
end
@@ -194,7 +205,7 @@ def environment_flags_from_document
194205
end
195206

196207
def get_identity_flags_from_document(identifier, traits = {})
197-
identity_model = build_identity_model(identifier, traits)
208+
identity_model = get_identity_model(identifier, traits)
198209

199210
Flagsmith::Flags::Collection.from_feature_state_models(
200211
engine.get_identity_feature_states(environment, identity_model),
@@ -275,19 +286,28 @@ def process_identity_flags_from_api(identifier, traits = {})
275286
)
276287
end
277288

278-
def build_identity_model(identifier, traits = {})
289+
# rubocop:disable Metrics/MethodLength
290+
def get_identity_model(identifier, traits = {})
279291
unless environment
280292
raise Flagsmith::ClientError,
281-
'Unable to build identity model when no local environment present.'
293+
'Unable to get identity model when no local environment present.'
282294
end
283295

284296
trait_models = traits.map do |key, value|
285297
Flagsmith::Engine::Identities::Trait.new(trait_key: key, trait_value: value)
286298
end
299+
300+
if identity_overrides_by_identifier.key? identifier
301+
identity = identity_overrides_by_identifier[identifier]
302+
identity.update_traits trait_models
303+
return identity
304+
end
305+
287306
Flagsmith::Engine::Identity.new(
288307
identity_traits: trait_models, environment_api_key: environment_key, identifier: identifier
289308
)
290309
end
310+
# rubocop:enable Metrics/MethodLength
291311

292312
def generate_identities_data(identifier, traits = {})
293313
{

lib/flagsmith/engine/environments/models.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,35 @@ module Engine
66
class Environment
77
attr_reader :id, :api_key
88
attr_accessor :project, :feature_states, :amplitude_config, :segment_config,
9-
:mixpanel_config, :heap_config
9+
:mixpanel_config, :heap_config, :identity_overrides
1010

11-
def initialize(id:, api_key:, project:, feature_states: [])
11+
def initialize(id:, api_key:, project:, feature_states: [], identity_overrides: [])
1212
@id = id
1313
@api_key = api_key
1414
@project = project
1515
@feature_states = feature_states
16+
@identity_overrides = identity_overrides
1617
end
1718

1819
class << self
20+
# rubocop:disable Metrics/MethodLength
1921
def build(json)
2022
project = Flagsmith::Engine::Project.build(json[:project])
2123
feature_states = json[:feature_states].map do |fs|
2224
Flagsmith::Engine::FeatureState.build(fs)
2325
end
2426

25-
new(**json.slice(:id, :api_key).merge(project: project, feature_states: feature_states))
27+
identity_overrides = json.fetch(:identity_overrides, []).map do |io|
28+
Flagsmith::Engine::Identity.build(io)
29+
end
30+
31+
new(**json.slice(:id, :api_key).merge(
32+
project: project,
33+
feature_states: feature_states,
34+
identity_overrides: identity_overrides
35+
))
2636
end
37+
# rubocop:enable Metrics/MethodLength
2738
end
2839
end
2940

spec/sdk/fixtures/environment.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,31 @@
6666
"segment_id": null,
6767
"enabled": true
6868
}
69+
],
70+
"updated_at": "2023-07-14 16:12:00.000000",
71+
"identity_overrides": [
72+
{
73+
"identifier": "overridden-id",
74+
"identity_uuid": "0f21cde8-63c5-4e50-baca-87897fa6cd01",
75+
"created_date": "2019-08-27T14:53:45.698555Z",
76+
"updated_at": "2023-07-14 16:12:00.000000",
77+
"environment_api_key": "B62qaMZNwfiqT76p38ggrQ",
78+
"identity_features": [
79+
{
80+
"id": 1,
81+
"feature": {
82+
"id": 1,
83+
"name": "some_feature",
84+
"type": "STANDARD"
85+
},
86+
"featurestate_uuid": "1bddb9a5-7e59-42c6-9be9-625fa369749f",
87+
"feature_state_value": "some-overridden-value",
88+
"enabled": false,
89+
"environment": 1,
90+
"identity": null,
91+
"feature_segment": null
92+
}
93+
]
94+
}
6995
]
7096
}

spec/sdk/local_evaluation_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
require_relative 'shared_mocks.rb'
6+
7+
RSpec.describe Flagsmith do
8+
include_context 'shared mocks'
9+
10+
describe '#get_identity_overrides_flags' do
11+
it 'should return identity overrides in local evaluation' do
12+
allow_any_instance_of(Flagsmith::Client).to receive(:api_client).and_return(mock_api_client)
13+
14+
flagsmith = Flagsmith::Client.new(environment_key: mock_api_key, api_url: mock_api_url, enable_local_evaluation: true)
15+
expect(flagsmith.config.local_evaluation?).to be_truthy
16+
17+
flag = flagsmith.get_identity_flags("overridden-id").get_flag("some_feature")
18+
19+
expect(flag.enabled).to be_falsy
20+
expect(flag.value).to eq("some-overridden-value")
21+
end
22+
end
23+
end

0 commit comments

Comments
 (0)