Skip to content

Commit adbd74e

Browse files
[FSSDK-11459] Ruby - Add SDK Multi-Region Support for Data Hosting (#365)
* [FSSDK-11459] Ruby - Add SDK Multi-Region Support for Data Hosting * Fix lint issues * Fix lint * Fix test * Add with region * Fix lint * Fix lint issue * Correct the Region default value * Fix failed tests * Fix lint * Fix test cases * Fix event builder * Fix the issue * Fix typo * Correct the event name * Fix errors * Fix errors * Fix test cases * Fix test cases * Implement copilot review comments * Fix lint * Implement changes and add new tests * Fix lint issue * Remove unnecessary region params * Remove region from expected param * Remove region from impression expected * Add region to builder * Add region to as_json * Implement * Update region EU * Fix test
1 parent da64665 commit adbd74e

11 files changed

+189
-67
lines changed

lib/optimizely/config/datafile_project_config.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class DatafileProjectConfig < ProjectConfig
3333
:group_id_map, :rollout_id_map, :rollout_experiment_id_map, :variation_id_map,
3434
:variation_id_to_variable_usage_map, :variation_key_map, :variation_id_map_by_experiment_id,
3535
:variation_key_map_by_experiment_id, :flag_variation_map, :integration_key_map, :integrations,
36-
:public_key_for_odp, :host_for_odp, :all_segments
36+
:public_key_for_odp, :host_for_odp, :all_segments, :region
3737
# Boolean - denotes if Optimizely should remove the last block of visitors' IP address before storing event data
3838
attr_reader :anonymize_ip
3939

@@ -69,6 +69,10 @@ def initialize(datafile, logger, error_handler)
6969
@rollouts = config.fetch('rollouts', [])
7070
@send_flag_decisions = config.fetch('sendFlagDecisions', false)
7171
@integrations = config.fetch('integrations', [])
72+
@region = config.fetch('region', 'US')
73+
74+
# Default to US region if not specified
75+
@region = 'US' if @region.nil? || @region.empty?
7276

7377
# Json type is represented in datafile as a subtype of string for the sake of backwards compatibility.
7478
# Converting it to a first-class json type while creating Project Config

lib/optimizely/event/entity/event_context.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ def initialize(
2626
anonymize_ip:,
2727
revision:,
2828
client_name:,
29-
client_version:
29+
client_version:,
30+
region:
3031
)
3132
@account_id = account_id
3233
@project_id = project_id
3334
@anonymize_ip = anonymize_ip
3435
@revision = revision
3536
@client_name = client_name
3637
@client_version = client_version
38+
@region = region
3739
end
3840

3941
def as_json
@@ -43,7 +45,8 @@ def as_json
4345
anonymize_ip: @anonymize_ip,
4446
revision: @revision,
4547
client_name: @client_name,
46-
client_version: @client_version
48+
client_version: @client_version,
49+
region: @region
4750
}
4851
end
4952
end

lib/optimizely/event/event_factory.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ class EventFactory
2828
# EventFactory builds LogEvent objects from a given user_event.
2929
class << self
3030
CUSTOM_ATTRIBUTE_FEATURE_TYPE = 'custom'
31-
ENDPOINT = 'https://logx.optimizely.com/v1/events'
31+
ENDPOINTS = {
32+
US: 'https://logx.optimizely.com/v1/events',
33+
EU: 'https://eu.logx.optimizely.com/v1/events'
34+
}.freeze
3235
POST_HEADERS = {'Content-Type' => 'application/json'}.freeze
3336
ACTIVATE_EVENT_KEY = 'campaign_activated'
3437

@@ -67,7 +70,10 @@ def create_log_event(user_events, logger)
6770

6871
builder.with_visitors(visitors)
6972
event_batch = builder.build
70-
Event.new(:post, ENDPOINT, event_batch.as_json, POST_HEADERS)
73+
74+
endpoint = ENDPOINTS[user_context[:region].to_s.upcase.to_sym] || ENDPOINTS[:US]
75+
76+
Event.new(:post, endpoint, event_batch.as_json, POST_HEADERS)
7177
end
7278

7379
def build_attribute_list(user_attributes, project_config)

lib/optimizely/event/user_event_factory.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ def self.create_impression_event(project_config, experiment, variation_id, metad
3333
#
3434
# Returns Event encapsulating the impression event.
3535
event_context = Optimizely::EventContext.new(
36+
region: project_config.region,
3637
account_id: project_config.account_id,
3738
project_id: project_config.project_id,
3839
anonymize_ip: project_config.anonymize_ip,
@@ -67,6 +68,7 @@ def self.create_conversion_event(project_config, event, user_id, user_attributes
6768
# Returns Event encapsulating the conversion event.
6869

6970
event_context = Optimizely::EventContext.new(
71+
region: project_config.region,
7072
account_id: project_config.account_id,
7173
project_id: project_config.project_id,
7274
anonymize_ip: project_config.anonymize_ip,

lib/optimizely/event_builder.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,17 @@ def get_common_params(project_config, user_id, attributes)
101101
revision: project_config.revision,
102102
client_name: CLIENT_ENGINE,
103103
enrich_decisions: true,
104-
client_version: VERSION
104+
client_version: VERSION,
105+
region: project_config.region || 'US'
105106
}
106107
end
107108
end
108109

109110
class EventBuilder < BaseEventBuilder
110-
ENDPOINT = 'https://logx.optimizely.com/v1/events'
111+
ENDPOINTS = {
112+
US: 'https://logx.optimizely.com/v1/events',
113+
EU: 'https://eu.logx.optimizely.com/v1/events'
114+
}.freeze
111115
POST_HEADERS = {'Content-Type' => 'application/json'}.freeze
112116
ACTIVATE_EVENT_KEY = 'campaign_activated'
113117

@@ -122,11 +126,14 @@ def create_impression_event(project_config, experiment, variation_id, user_id, a
122126
#
123127
# Returns +Event+ encapsulating the impression event.
124128

129+
region = project_config.region || 'US'
125130
event_params = get_common_params(project_config, user_id, attributes)
126131
impression_params = get_impression_params(project_config, experiment, variation_id)
127132
event_params[:visitors][0][:snapshots].push(impression_params)
128133

129-
Event.new(:post, ENDPOINT, event_params, POST_HEADERS)
134+
endpoint = ENDPOINTS[region.to_s.upcase.to_sym]
135+
136+
Event.new(:post, endpoint, event_params, POST_HEADERS)
130137
end
131138

132139
def create_conversion_event(project_config, event, user_id, attributes, event_tags)
@@ -140,11 +147,14 @@ def create_conversion_event(project_config, event, user_id, attributes, event_ta
140147
#
141148
# Returns +Event+ encapsulating the conversion event.
142149

150+
region = project_config.region || 'US'
143151
event_params = get_common_params(project_config, user_id, attributes)
144152
conversion_params = get_conversion_params(event, event_tags)
145153
event_params[:visitors][0][:snapshots] = [conversion_params]
146154

147-
Event.new(:post, ENDPOINT, event_params, POST_HEADERS)
155+
endpoint = ENDPOINTS[region.to_s.upcase.to_sym]
156+
157+
Event.new(:post, endpoint, event_params, POST_HEADERS)
148158
end
149159

150160
private

lib/optimizely/project_config.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def host_for_odp; end
6262

6363
def all_segments; end
6464

65+
def region; end
66+
6567
def experiment_running?(experiment); end
6668

6769
def get_experiment_from_key(experiment_key); end

spec/config/datafile_project_config_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
expect(project_config.sdk_key).to eq(config_body['sdkKey'])
5858
expect(project_config.environment_key).to eq(config_body['environmentKey'])
5959
expect(project_config.send_flag_decisions).to eq(config_body['sendFlagDecisions'])
60+
expect(project_config.region).to eq(config_body['region'])
6061

6162
expected_attribute_key_map = {
6263
'browser_type' => config_body['attributes'][0],
@@ -756,6 +757,23 @@
756757
expect(project_config.rollout_experiment_id_map).to eq(expected_rollout_experiment_id_map)
757758
end
758759

760+
it 'should use US region when no region is specified in datafile' do
761+
project_config = Optimizely::DatafileProjectConfig.new(config_body_JSON, logger, error_handler)
762+
expect(project_config.region).to eq('US')
763+
end
764+
765+
it 'should parse region specified in datafile correctly' do
766+
project_config_us = Optimizely::DatafileProjectConfig.new(config_body_JSON, logger, error_handler)
767+
expect(project_config_us.region).to eq('US')
768+
769+
config_body_eu = config_body.dup
770+
config_body_eu['region'] = 'EU'
771+
config_body_json = JSON.dump(config_body_eu)
772+
project_config_eu = Optimizely::DatafileProjectConfig.new(config_body_json, logger, error_handler)
773+
774+
expect(project_config_eu.region).to eq('EU')
775+
end
776+
759777
it 'should initialize properties correctly upon creating project with typed audience dict' do
760778
project_config = Optimizely::DatafileProjectConfig.new(JSON.dump(OptimizelySpec::CONFIG_DICT_WITH_TYPED_AUDIENCES), logger, error_handler)
761779
config_body = OptimizelySpec::CONFIG_DICT_WITH_TYPED_AUDIENCES

0 commit comments

Comments
 (0)