Skip to content

Commit 7a318ec

Browse files
jaeoptclaudeMat001
authored
[AI-FSSDK] [FSSDK-12670] Block ODP identify events with single identifier (#452)
* [FSSDK-12670] Block ODP identify events with single identifier Change IdentifyUser in event manager to accept identifiers map instead of single userID. Filter out empty values and require 2+ valid identifiers before dispatching. Server-side Go SDK only has fs_user_id (no VUID), so identify events will be correctly skipped. * [FSSDK-12670] Address review feedback: fix log message * [FSSDK-12670] Retrigger CI * [FSSDK-12670] Add comment explaining the < 2 identifiers guard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * [FSSDK-12670] retrigger CI * Trigger CI Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Matjaz Pirnovar <Mat001@users.noreply.github.com>
1 parent e976715 commit 7a318ec

4 files changed

Lines changed: 54 additions & 14 deletions

File tree

pkg/odp/event/event_manager.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func getRetryInterval(retryCount int) time.Duration {
5252
type Manager interface {
5353
// odpConfig is required here since it can be updated anytime and ticker needs to be aware of latest changes
5454
Start(ctx context.Context, odpConfig config.Config)
55-
IdentifyUser(apiKey, apiHost, userID string)
55+
IdentifyUser(apiKey, apiHost string, identifiers map[string]string)
5656
ProcessEvent(apiKey, apiHost string, odpEvent Event) error
5757
FlushEvents(apiKey, apiHost string)
5858
}
@@ -162,16 +162,31 @@ func (bm *BatchEventManager) Start(ctx context.Context, odpConfig config.Config)
162162
}
163163

164164
// IdentifyUser associates a full-stack userid with an established VUID
165-
func (bm *BatchEventManager) IdentifyUser(apiKey, apiHost, userID string) {
165+
func (bm *BatchEventManager) IdentifyUser(apiKey, apiHost string, identifiers map[string]string) {
166166
if !bm.IsOdpServiceIntegrated(apiKey, apiHost) {
167167
bm.logger.Debug(utils.IdentityOdpNotIntegrated)
168168
return
169169
}
170-
identifiers := map[string]string{utils.OdpFSUserIDKey: userID}
170+
171+
// Filter out empty identifier values
172+
validIdentifiers := make(map[string]string)
173+
for k, v := range identifiers {
174+
if v != "" {
175+
validIdentifiers[k] = v
176+
}
177+
}
178+
179+
// Identify requires 2+ identifiers to link (e.g., vuid + fs_user_id).
180+
// A single identifier has no cross-reference value and generates unnecessary traffic.
181+
if len(validIdentifiers) < 2 {
182+
bm.logger.Debug("ODP identify event is not dispatched (fewer than 2 valid identifiers).")
183+
return
184+
}
185+
171186
odpEvent := Event{
172187
Type: utils.OdpEventType,
173188
Action: utils.OdpActionIdentified,
174-
Identifiers: identifiers,
189+
Identifiers: validIdentifiers,
175190
}
176191
_ = bm.ProcessEvent(apiKey, apiHost, odpEvent)
177192
}

pkg/odp/event/event_manager_test.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,18 +178,19 @@ func (e *EventManagerTestSuite) TestEventsDispatchedWhenFlushIntervalReached() {
178178
}
179179

180180
func (e *EventManagerTestSuite) TestIdentifyUserWhenODPNotIntegrated() {
181-
e.eventManager.IdentifyUser("", "1", "123")
181+
identifiers := map[string]string{utils.OdpFSUserIDKey: "123", "vuid": "vuid-123"}
182+
e.eventManager.IdentifyUser("", "1", identifiers)
182183
e.Nil(e.eventManager.ticker)
183184
e.Equal(0, e.eventAPIManager.timesSendEventsCalled)
184185
}
185186

186-
func (e *EventManagerTestSuite) TestIdentifyUserWhenODPIntegrated() {
187-
userID := "123"
188-
expectedEvent := Event{Identifiers: map[string]string{utils.OdpFSUserIDKey: userID}, Type: utils.OdpEventType, Action: utils.OdpActionIdentified}
187+
func (e *EventManagerTestSuite) TestIdentifyUserWhenODPIntegratedWithTwoIdentifiers() {
188+
identifiers := map[string]string{utils.OdpFSUserIDKey: "123", "vuid": "vuid-456"}
189+
expectedEvent := Event{Identifiers: identifiers, Type: utils.OdpEventType, Action: utils.OdpActionIdentified}
189190
e.eventManager.addCommonData(&expectedEvent)
190191
e.eventAPIManager.wg.Add(1)
191192
e.eventManager.batchSize = 1
192-
e.eventManager.IdentifyUser("1", "2", userID)
193+
e.eventManager.IdentifyUser("1", "2", identifiers)
193194
e.eventAPIManager.wg.Wait()
194195
e.Equal(1, e.eventAPIManager.timesSendEventsCalled)
195196

@@ -200,6 +201,20 @@ func (e *EventManagerTestSuite) TestIdentifyUserWhenODPIntegrated() {
200201
e.Equal(expectedEvent, actualEvent)
201202
}
202203

204+
func (e *EventManagerTestSuite) TestIdentifyUserSkippedWithSingleIdentifier() {
205+
identifiers := map[string]string{utils.OdpFSUserIDKey: "123"}
206+
e.eventManager.IdentifyUser("1", "2", identifiers)
207+
e.Equal(0, e.eventAPIManager.timesSendEventsCalled)
208+
e.Equal(0, e.eventManager.eventQueue.Size())
209+
}
210+
211+
func (e *EventManagerTestSuite) TestIdentifyUserSkippedWithEmptyValues() {
212+
identifiers := map[string]string{utils.OdpFSUserIDKey: "123", "vuid": ""}
213+
e.eventManager.IdentifyUser("1", "2", identifiers)
214+
e.Equal(0, e.eventAPIManager.timesSendEventsCalled)
215+
e.Equal(0, e.eventManager.eventQueue.Size())
216+
}
217+
203218
func (e *EventManagerTestSuite) TestProcessEventWithInvalidODPConfig() {
204219
em := NewBatchEventManager(WithAPIManager(&MockEventAPIManager{}))
205220
e.Error(em.ProcessEvent("", "", Event{Action: "123"}))
@@ -442,7 +457,8 @@ func (e *EventManagerTestSuite) TestEventManagerAsyncBehaviour() {
442457
eventAPIManager.shouldNotInformWaitgroup = true
443458
eg := newExecutionContext()
444459
callAllMethods := func(id string) {
445-
eventManager.IdentifyUser("-1", "-1", id)
460+
identifiers := map[string]string{utils.OdpFSUserIDKey: id, "vuid": "vuid-" + id}
461+
eventManager.IdentifyUser("-1", "-1", identifiers)
446462
eventManager.ProcessEvent("-1", "-1", Event{Action: "123"})
447463
}
448464
for i := 0; i < iterations; i++ {

pkg/odp/odp_manager.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ type Manager interface {
4040
Update(apiKey, apiHost string, segmentsToCheck []string)
4141
}
4242

43+
// identifyUserIdentifiers builds the identifiers map for an identify event.
44+
// Server-side SDKs only have fs_user_id (no VUID), so identify events
45+
// will be skipped by the event manager's count check (requires 2+ identifiers).
46+
func identifyUserIdentifiers(userID string) map[string]string {
47+
return map[string]string{utils.OdpFSUserIDKey: userID}
48+
}
49+
4350
// DefaultOdpManager represents default implementation of odp manager
4451
type DefaultOdpManager struct {
4552
enabled bool
@@ -141,7 +148,8 @@ func (om *DefaultOdpManager) IdentifyUser(userID string) {
141148
om.logger.Debug(utils.IdentityOdpDisabled)
142149
return
143150
}
144-
om.EventManager.IdentifyUser(om.OdpConfig.GetAPIKey(), om.OdpConfig.GetAPIHost(), userID)
151+
identifiers := identifyUserIdentifiers(userID)
152+
om.EventManager.IdentifyUser(om.OdpConfig.GetAPIKey(), om.OdpConfig.GetAPIHost(), identifiers)
145153
}
146154

147155
// SendOdpEvent sends an event to the ODP server.

pkg/odp/odp_manager_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ func (m *MockEventManager) Start(ctx context.Context, odpConfig config.Config) {
4141
m.Called(ctx, odpConfig)
4242
}
4343

44-
func (m *MockEventManager) IdentifyUser(apiKey, apiHost, userID string) {
45-
m.Called(apiKey, apiHost, userID)
44+
func (m *MockEventManager) IdentifyUser(apiKey, apiHost string, identifiers map[string]string) {
45+
m.Called(apiKey, apiHost, identifiers)
4646
}
4747

4848
func (m *MockEventManager) ProcessEvent(apiKey, apiHost string, odpEvent event.Event) error {
@@ -192,7 +192,8 @@ func (o *ODPManagerTestSuite) TestFetchQualifiedSegments() {
192192
func (o *ODPManagerTestSuite) TestIdentifyUser() {
193193
o.config.On("GetAPIKey").Return("")
194194
o.config.On("GetAPIHost").Return("")
195-
o.eventManager.On("IdentifyUser", "", "", o.userID)
195+
expectedIdentifiers := map[string]string{utils.OdpFSUserIDKey: o.userID}
196+
o.eventManager.On("IdentifyUser", "", "", expectedIdentifiers)
196197
o.odpManager.IdentifyUser(o.userID)
197198
o.segmentManager.AssertExpectations(o.T())
198199
}

0 commit comments

Comments
 (0)