Skip to content
This repository has been archived by the owner on Jun 27, 2023. It is now read-only.

Commit

Permalink
fix: persist user traits across events (segmentio#581)
Browse files Browse the repository at this point in the history
  • Loading branch information
oscb authored Jun 24, 2022
1 parent 489bffc commit d48ac83
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 17 deletions.
45 changes: 45 additions & 0 deletions packages/core/src/__tests__/methods/identify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
});
});
8 changes: 7 additions & 1 deletion packages/core/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -97,5 +99,9 @@ export const applyRawEventData = (
isAliasEvent(event) || isIdentifyEvent(event)
? event.userId
: userInfo.userId,
traits:
isIdentifyEvent(event) || isGroupEvent(event)
? event.traits
: userInfo.traits,
};
};
7 changes: 4 additions & 3 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface BaseEventType {
messageId?: string;
userId?: string;
timestamp?: string;
traits?: UserTraits | GroupTraits;

context?: PartialContext;
integrations?: SegmentAPIIntegrations;
Expand All @@ -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 {
Expand Down Expand Up @@ -297,5 +298,5 @@ export enum EventType {
export type UserInfoState = {
anonymousId: string;
userId?: string;
traits?: UserTraits;
traits?: UserTraits | GroupTraits;
};
20 changes: 10 additions & 10 deletions packages/plugins/plugin-braze/src/methods/identify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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) {
Expand All @@ -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);
}
}
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('#identify', () => {
});

it('registers superProperties', () => {
payload.traits.prop1 = 'string';
payload.traits!.prop1 = 'string';
settings.superProperties = ['prop1'];
let mockedTraits = { prop1: 'string' };

Expand Down
3 changes: 2 additions & 1 deletion packages/plugins/plugin-mixpanel/src/methods/identify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -54,6 +54,7 @@ export default (
}

if (
event.traits !== undefined &&
settings.people === true &&
settings.peopleProperties !== undefined &&
settings.peopleProperties.length
Expand Down
1 change: 0 additions & 1 deletion release.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ module.exports = {
'@semantic-release/git',
],
extends: 'semantic-release-monorepo',
dryRun: true,
};

0 comments on commit d48ac83

Please sign in to comment.