Skip to content

Commit c0dadc7

Browse files
authored
fix: handle null as the first argument in identify (#950)
1 parent 1c04276 commit c0dadc7

File tree

3 files changed

+76
-12
lines changed

3 files changed

+76
-12
lines changed

.changeset/nasty-tables-sort.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@segment/analytics-next': patch
3+
---
4+
5+
Fixes calls to .identify() with null as id

packages/browser/src/core/arguments-resolver/__tests__/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,31 @@ describe(resolveUserArguments, () => {
429429
expect(traits).toEqual(userTraits)
430430
expect(options).toEqual({})
431431
})
432+
433+
it('should accept (undefined, traits)', () => {
434+
user.reset()
435+
const [id, traits, options] = resolver(undefined, userTraits)
436+
expect(traits).toEqual(userTraits)
437+
expect(options).toEqual({})
438+
expect(id).toEqual(null)
439+
})
440+
441+
it('should accept (null, traits) with unknown identity', () => {
442+
user.reset()
443+
const [id, traits, options] = resolver(null, userTraits)
444+
expect(traits).toEqual(userTraits)
445+
expect(options).toEqual({})
446+
expect(id).toEqual(null)
447+
})
448+
449+
it('should accept (null, traits) when identity is set', () => {
450+
user.reset()
451+
user.identify('something')
452+
const [id, traits, options] = resolver(null, userTraits)
453+
expect(traits).toEqual(userTraits)
454+
expect(options).toEqual({})
455+
expect(id).toEqual('something')
456+
})
432457
})
433458

434459
describe(resolveAliasArguments, () => {

packages/browser/src/core/arguments-resolver/index.ts

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -111,22 +111,56 @@ export const resolveUserArguments = <T extends Traits, U extends User>(
111111
user: U
112112
): ResolveUser<T> => {
113113
return (...args): ReturnType<ResolveUser<T>> => {
114-
let id: string | ID | null = null
115-
id = args.find(isString) ?? args.find(isNumber)?.toString() ?? user.id()
116-
117-
const objects = args.filter((obj) => {
118-
if (id === null) {
119-
return isPlainObject(obj)
114+
const values: {
115+
id?: ID
116+
traits?: T | null
117+
options?: Options
118+
callback?: Callback
119+
} = {}
120+
// It's a stack so it's reversed so that we go through each of the expected arguments
121+
const orderStack: Array<keyof typeof values> = [
122+
'callback',
123+
'options',
124+
'traits',
125+
'id',
126+
]
127+
128+
// Read each argument and eval the possible values here
129+
for (const arg of args) {
130+
let current = orderStack.pop()
131+
if (current === 'id') {
132+
if (isString(arg) || isNumber(arg)) {
133+
values.id = arg.toString()
134+
continue
135+
}
136+
if (arg === null || arg === undefined) {
137+
continue
138+
}
139+
// First argument should always be the id, if it is not a valid value we can skip it
140+
current = orderStack.pop()
120141
}
121-
return isPlainObject(obj) || obj === null
122-
}) as Array<Traits | null>
123142

124-
const traits = (objects[0] ?? {}) as T
125-
const opts = (objects[1] ?? {}) as Options
143+
// Traits and Options
144+
if (
145+
(current === 'traits' || current === 'options') &&
146+
(arg === null || arg === undefined || isPlainObject(arg))
147+
) {
148+
values[current] = arg as T
149+
}
126150

127-
const resolvedCallback = args.find(isFunction) as Callback | undefined
151+
// Callback
152+
if (isFunction(arg)) {
153+
values.callback = arg as Callback
154+
break // This is always the last argument
155+
}
156+
}
128157

129-
return [id, traits, opts, resolvedCallback]
158+
return [
159+
values.id ?? user.id(),
160+
(values.traits ?? {}) as T,
161+
values.options ?? {},
162+
values.callback,
163+
]
130164
}
131165
}
132166

0 commit comments

Comments
 (0)