diff --git a/packages/core/src/__tests__/methods/identify.test.ts b/packages/core/src/__tests__/methods/identify.test.ts index ca1235dc..99c757bb 100644 --- a/packages/core/src/__tests__/methods/identify.test.ts +++ b/packages/core/src/__tests__/methods/identify.test.ts @@ -102,4 +102,49 @@ describe('methods #identify', () => { }, }); }); + + it('persists identity traits accross events', () => { + const client = new SegmentClient(clientArgs); + jest.spyOn(client, 'process'); + // @ts-ignore accessing the internal timeline to check the processed events + jest.spyOn(client.timeline, 'process'); + + client.identify('new-user-id', { name: 'Mary', age: 30 }); + + const expectedEvent = { + traits: { + name: 'Mary', + age: 30, + }, + userId: 'new-user-id', + type: 'identify', + }; + + expect(client.process).toHaveBeenCalledTimes(1); + expect(client.process).toHaveBeenCalledWith(expectedEvent); + + expect(client.userInfo.get()).toEqual({ + ...initialUserInfo, + userId: 'new-user-id', + traits: expectedEvent.traits, + }); + + client.track('track event'); + + // @ts-ignore + expect(client.timeline.process).toHaveBeenLastCalledWith({ + anonymousId: 'very-anonymous', + event: 'track event', + integrations: {}, + messageId: 'mocked-uuid', + properties: {}, + timestamp: '2010-01-01T00:00:00.000Z', + traits: { + age: 30, + name: 'Mary', + }, + type: 'track', + userId: 'new-user-id', + }); + }); }); diff --git a/packages/core/src/events.ts b/packages/core/src/events.ts index ec89c073..10090f4b 100644 --- a/packages/core/src/events.ts +++ b/packages/core/src/events.ts @@ -82,11 +82,13 @@ const isAliasEvent = (event: SegmentEvent): event is AliasEventType => event.type === EventType.AliasEvent; const isIdentifyEvent = (event: SegmentEvent): event is AliasEventType => event.type === EventType.IdentifyEvent; +const isGroupEvent = (event: SegmentEvent): event is GroupEventType => + event.type === EventType.GroupEvent; export const applyRawEventData = ( event: SegmentEvent, userInfo: UserInfoState -) => { +): SegmentEvent => { return { ...event, anonymousId: userInfo.anonymousId, @@ -97,5 +99,9 @@ export const applyRawEventData = ( isAliasEvent(event) || isIdentifyEvent(event) ? event.userId : userInfo.userId, + traits: + isIdentifyEvent(event) || isGroupEvent(event) + ? event.traits + : userInfo.traits, }; }; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ff9c4825..46317870 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -26,6 +26,7 @@ interface BaseEventType { messageId?: string; userId?: string; timestamp?: string; + traits?: UserTraits | GroupTraits; context?: PartialContext; integrations?: SegmentAPIIntegrations; @@ -46,13 +47,13 @@ export interface ScreenEventType extends BaseEventType { export interface IdentifyEventType extends BaseEventType { type: EventType.IdentifyEvent; - traits: UserTraits; + traits?: UserTraits; } export interface GroupEventType extends BaseEventType { type: EventType.GroupEvent; groupId: string; - traits: GroupTraits; + traits?: GroupTraits; } export interface AliasEventType extends BaseEventType { @@ -297,5 +298,5 @@ export enum EventType { export type UserInfoState = { anonymousId: string; userId?: string; - traits?: UserTraits; + traits?: UserTraits | GroupTraits; }; diff --git a/packages/plugins/plugin-braze/src/methods/identify.ts b/packages/plugins/plugin-braze/src/methods/identify.ts index 6e57ffbd..e178ad25 100644 --- a/packages/plugins/plugin-braze/src/methods/identify.ts +++ b/packages/plugins/plugin-braze/src/methods/identify.ts @@ -9,7 +9,7 @@ export default (payload: IdentifyEventType) => { ReactAppboy.changeUser(payload.userId); } - if (payload.traits.birthday) { + if (payload.traits?.birthday !== undefined) { const data = new Date(payload.traits.birthday); ReactAppboy.setDateOfBirth( data.getFullYear(), @@ -19,19 +19,19 @@ export default (payload: IdentifyEventType) => { ); } - if (payload.traits.email) { + if (payload.traits?.email !== undefined) { ReactAppboy.setEmail(payload.traits.email); } - if (payload.traits.firstName) { + if (payload.traits?.firstName !== undefined) { ReactAppboy.setFirstName(payload.traits.firstName); } - if (payload.traits.lastName) { + if (payload.traits?.lastName !== undefined) { ReactAppboy.setLastName(payload.traits.lastName); } - if (payload.traits.gender) { + if (payload.traits?.gender !== undefined) { const validGenders = ['m', 'f', 'n', 'o', 'p', 'u']; const isValidGender = validGenders.indexOf(payload.traits.gender) > -1; if (isValidGender) { @@ -41,15 +41,15 @@ export default (payload: IdentifyEventType) => { } } - if (payload.traits.phone) { + if (payload.traits?.phone !== undefined) { ReactAppboy.setPhoneNumber(payload.traits.phone); } - if (payload.traits.address) { - if (payload.traits.address.city) { + if (payload.traits?.address !== undefined) { + if (payload.traits.address.city !== undefined) { ReactAppboy.setHomeCity(payload.traits.address.city); } - if (payload.traits.address.country) { + if (payload.traits?.address.country !== undefined) { ReactAppboy.setCountry(payload.traits.address.country); } } @@ -64,7 +64,7 @@ export default (payload: IdentifyEventType) => { 'address', ]; - Object.entries(payload.traits).forEach(([key, value]) => { + Object.entries(payload.traits ?? {}).forEach(([key, value]) => { if (appBoyTraits.indexOf(key) < 0) { ReactAppboy.setCustomUserAttribute(key, value as any); } diff --git a/packages/plugins/plugin-mixpanel/src/methods/__tests__/identify.test.ts b/packages/plugins/plugin-mixpanel/src/methods/__tests__/identify.test.ts index 2d4313da..a9a92384 100644 --- a/packages/plugins/plugin-mixpanel/src/methods/__tests__/identify.test.ts +++ b/packages/plugins/plugin-mixpanel/src/methods/__tests__/identify.test.ts @@ -63,7 +63,7 @@ describe('#identify', () => { }); it('registers superProperties', () => { - payload.traits.prop1 = 'string'; + payload.traits!.prop1 = 'string'; settings.superProperties = ['prop1']; let mockedTraits = { prop1: 'string' }; diff --git a/packages/plugins/plugin-mixpanel/src/methods/identify.ts b/packages/plugins/plugin-mixpanel/src/methods/identify.ts index 3ffa2030..d4365e7b 100644 --- a/packages/plugins/plugin-mixpanel/src/methods/identify.ts +++ b/packages/plugins/plugin-mixpanel/src/methods/identify.ts @@ -24,7 +24,7 @@ export default ( settings: SegmentMixpanelSettings ) => { const userId = event.userId; - const mixpanelTraits = mapTransform(event.traits); + const mixpanelTraits = mapTransform(event.traits ?? {}); if (userId !== undefined) { mixpanel.identify(userId); @@ -54,6 +54,7 @@ export default ( } if ( + event.traits !== undefined && settings.people === true && settings.peopleProperties !== undefined && settings.peopleProperties.length diff --git a/release.config.js b/release.config.js index f0ba007f..c3b6bfc0 100644 --- a/release.config.js +++ b/release.config.js @@ -11,5 +11,4 @@ module.exports = { '@semantic-release/git', ], extends: 'semantic-release-monorepo', - dryRun: true, };