Skip to content

Commit b8cee28

Browse files
[FSSDK-9074] fix: odp event validation (#334)
* fix odp send event validation * add unit tests
1 parent c447d8e commit b8cee28

File tree

5 files changed

+79
-3
lines changed

5 files changed

+79
-3
lines changed

lib/optimizely.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ def get_optimizely_config
895895

896896
# Send an event to the ODP server.
897897
#
898-
# @param action - the event action name.
898+
# @param action - the event action name. Cannot be nil or empty string.
899899
# @param identifiers - a hash for identifiers. The caller must provide at least one key-value pair.
900900
# @param type - the event type (default = "fullstack").
901901
# @param data - a hash for associated data. The default event data will be added to this data before sending to the ODP server.
@@ -911,6 +911,13 @@ def send_odp_event(action:, identifiers:, type: Helpers::Constants::ODP_MANAGER_
911911
return
912912
end
913913

914+
if action.nil? || action.empty?
915+
@logger.log(Logger::ERROR, Helpers::Constants::ODP_LOGS[:ODP_INVALID_ACTION])
916+
return
917+
end
918+
919+
type = Helpers::Constants::ODP_MANAGER_CONFIG[:EVENT_TYPE] if type.nil? || type.empty?
920+
914921
@odp_manager.send_event(type: type, action: action, identifiers: identifiers, data: data)
915922
end
916923

lib/optimizely/helpers/constants.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,8 @@ module Constants
387387
ODP_EVENT_FAILED: 'ODP event send failed (%s).',
388388
ODP_NOT_ENABLED: 'ODP is not enabled.',
389389
ODP_NOT_INTEGRATED: 'ODP is not integrated.',
390-
ODP_INVALID_DATA: 'ODP data is not valid.'
390+
ODP_INVALID_DATA: 'ODP data is not valid.',
391+
ODP_INVALID_ACTION: 'ODP action is not valid (cannot be empty).'
391392
}.freeze
392393

393394
DECISION_NOTIFICATION_TYPES = {

lib/optimizely/odp/odp_event.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@
1717
#
1818

1919
require 'json'
20+
require_relative '../helpers/constants'
2021

2122
module Optimizely
2223
class OdpEvent
2324
# Representation of an odp event which can be sent to the Optimizely odp platform.
25+
26+
KEY_FOR_USER_ID = Helpers::Constants::ODP_MANAGER_CONFIG[:KEY_FOR_USER_ID]
27+
2428
def initialize(type:, action:, identifiers:, data:)
2529
@type = type
2630
@action = action
27-
@identifiers = identifiers
31+
@identifiers = convert_identifiers(identifiers)
2832
@data = add_common_event_data(data)
2933
end
3034

@@ -39,6 +43,22 @@ def add_common_event_data(custom_data)
3943
data
4044
end
4145

46+
def convert_identifiers(identifiers)
47+
# Convert incorrect case/separator of identifier key `fs_user_id`
48+
# (ie. `fs-user-id`, `FS_USER_ID`).
49+
50+
identifiers.clone.each_key do |key|
51+
break if key == KEY_FOR_USER_ID
52+
53+
if ['fs-user-id', KEY_FOR_USER_ID].include?(key.downcase)
54+
identifiers[KEY_FOR_USER_ID] = identifiers.delete(key)
55+
break
56+
end
57+
end
58+
59+
identifiers
60+
end
61+
4262
def to_json(*_args)
4363
{
4464
type: @type,

spec/odp/odp_event_manager_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@
9292
event[:data]['invalid-item'] = {}
9393
expect(Optimizely::Helpers::Validator.odp_data_types_valid?(event[:data])).to be false
9494
end
95+
96+
it 'should convert invalid event identifier' do
97+
event = Optimizely::OdpEvent.new(type: 'type', action: 'action', identifiers: {'fs-user-id' => 'great'}, data: {})
98+
expect(event.instance_variable_get('@identifiers')).to eq({'fs_user_id' => 'great'})
99+
100+
event = Optimizely::OdpEvent.new(type: 'type', action: 'action', identifiers: {'FS-user-ID' => 'great'}, data: {})
101+
expect(event.instance_variable_get('@identifiers')).to eq({'fs_user_id' => 'great'})
102+
103+
event = Optimizely::OdpEvent.new(type: 'type', action: 'action', identifiers: {'FS_USER_ID' => 'great', 'fs.user.id' => 'wow'}, data: {})
104+
expect(event.instance_variable_get('@identifiers')).to eq({'fs_user_id' => 'great', 'fs.user.id' => 'wow'})
105+
106+
event = Optimizely::OdpEvent.new(type: 'type', action: 'action', identifiers: {'fs_user_id' => 'great', 'fsuserid' => 'wow'}, data: {})
107+
expect(event.instance_variable_get('@identifiers')).to eq({'fs_user_id' => 'great', 'fsuserid' => 'wow'})
108+
end
95109
end
96110

97111
describe '#initialize' do

spec/project_spec.rb

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4916,5 +4916,39 @@ class InvalidEventManager; end # rubocop:disable Lint/ConstantDefinitionInBlock
49164916

49174917
project.close
49184918
end
4919+
4920+
it 'should log error with nil action' do
4921+
expect(spy_logger).to receive(:log).once.with(Logger::ERROR, 'ODP action is not valid (cannot be empty).')
4922+
project = Optimizely::Project.new(config_body_integrations_JSON, nil, spy_logger)
4923+
project.send_odp_event(type: 'wow', action: nil, identifiers: {amazing: 'fantastic'}, data: {})
4924+
project.close
4925+
end
4926+
4927+
it 'should log error with empty string action' do
4928+
expect(spy_logger).to receive(:log).once.with(Logger::ERROR, 'ODP action is not valid (cannot be empty).')
4929+
project = Optimizely::Project.new(config_body_integrations_JSON, nil, spy_logger)
4930+
project.send_odp_event(type: 'wow', action: '', identifiers: {amazing: 'fantastic'}, data: {})
4931+
project.close
4932+
end
4933+
4934+
it 'should use default with nil type' do
4935+
project = Optimizely::Project.new(config_body_integrations_JSON, nil, spy_logger)
4936+
expect(project.odp_manager).to receive('send_event').with(type: 'fullstack', action: 'great', identifiers: {amazing: 'fantastic'}, data: {})
4937+
project.send_odp_event(type: nil, action: 'great', identifiers: {amazing: 'fantastic'}, data: {})
4938+
4939+
expect(spy_logger).not_to have_received(:log).with(Logger::ERROR, anything)
4940+
4941+
project.close
4942+
end
4943+
4944+
it 'should use default with empty string type' do
4945+
project = Optimizely::Project.new(config_body_integrations_JSON, nil, spy_logger)
4946+
expect(project.odp_manager).to receive('send_event').with(type: 'fullstack', action: 'great', identifiers: {amazing: 'fantastic'}, data: {})
4947+
project.send_odp_event(type: '', action: 'great', identifiers: {amazing: 'fantastic'}, data: {})
4948+
4949+
expect(spy_logger).not_to have_received(:log).with(Logger::ERROR, anything)
4950+
4951+
project.close
4952+
end
49194953
end
49204954
end

0 commit comments

Comments
 (0)