Skip to content

Commit 444f95a

Browse files
Revert "Revert "Braze: Enhancement - migrate user merge/delete to go through the BitP…""
1 parent 8d1e091 commit 444f95a

4 files changed

Lines changed: 63 additions & 72 deletions

File tree

declarations.d.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ declare module '@env' {
1111
export const BRAZE_API_KEY_ANDROID: string;
1212
export const BRAZE_API_KEY_IOS: string;
1313
export const BRAZE_EXPORT_API_KEY: string;
14-
export const BRAZE_MERGE_AND_DELETE_API_KEY: string;
15-
export const BRAZE_REST_API_ENDPOINT: string;
1614
export const COINBASE_CLIENT_ID: string;
1715
export const COINBASE_CLIENT_SECRET: string;
1816
export const DISABLE_DEVELOPMENT_LOGGING: string;

src/lib/Braze/index.ts

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import Braze from '@braze/react-native-sdk';
2-
import axios from 'axios';
32
import {
43
BRAZE_API_ENDPOINT,
54
BRAZE_API_KEY_ANDROID,
65
BRAZE_API_KEY_IOS,
7-
BRAZE_MERGE_AND_DELETE_API_KEY,
8-
BRAZE_REST_API_ENDPOINT,
96
} from '@env';
107
import {checkNotifications, RESULTS} from 'react-native-permissions';
118
import {NativeModules, Platform} from 'react-native';
@@ -105,52 +102,6 @@ const setUserAttributes = (attributes: BrazeUserAttributes) => {
105102
});
106103
};
107104

108-
const mergeUsers = async (
109-
user_to_merge: string,
110-
user_to_keep: string,
111-
): Promise<any> => {
112-
const url = 'https://' + BRAZE_REST_API_ENDPOINT + '/users/merge';
113-
const body = {
114-
merge_updates: [
115-
{
116-
identifier_to_merge: {
117-
external_id: user_to_merge,
118-
},
119-
identifier_to_keep: {
120-
external_id: user_to_keep,
121-
},
122-
},
123-
],
124-
};
125-
const headers = {
126-
'Content-Type': 'application/json',
127-
Authorization: 'Bearer ' + BRAZE_MERGE_AND_DELETE_API_KEY,
128-
};
129-
try {
130-
const {data} = await axios.post(url, body, {headers});
131-
return data;
132-
} catch (err: any) {
133-
throw err.response?.data?.message || err.message || err;
134-
}
135-
};
136-
137-
const deleteUser = async (eid: string): Promise<any> => {
138-
const url = 'https://' + BRAZE_REST_API_ENDPOINT + '/users/delete';
139-
const body = {
140-
external_ids: [eid],
141-
};
142-
const headers = {
143-
'Content-Type': 'application/json',
144-
Authorization: 'Bearer ' + BRAZE_MERGE_AND_DELETE_API_KEY,
145-
};
146-
try {
147-
const {data} = await axios.post(url, body, {headers});
148-
return data;
149-
} catch (err: any) {
150-
throw err.response?.data?.message || err.message || err;
151-
}
152-
};
153-
154105
export type BrazeUserAttributes = {
155106
[K in (typeof nonCustomAttributes)[number]]?: string;
156107
} & Record<string, any>;
@@ -258,14 +209,6 @@ class BrazeClientWrapper {
258209
};
259210
}
260211

261-
merge(userToMerge: string, userToKeep: string) {
262-
return mergeUsers(userToMerge, userToKeep);
263-
}
264-
265-
delete(eid: string) {
266-
return deleteUser(eid);
267-
}
268-
269212
async screen(name: string, properties: Record<string, any> = {}) {
270213
if (!(await this.ensureReady())) {
271214
return;

src/store/bitpay-id/bitpay-id.effects.spec.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ jest.mock('../../lib/Mixpanel', () => ({
6262

6363
jest.mock('../../lib/Braze', () => ({
6464
BrazeWrapper: {
65-
merge: jest.fn(() => Promise.resolve()),
6665
identify: jest.fn(() => Promise.resolve()),
6766
setEmail: jest.fn(),
6867
setEmailNotificationSubscriptionType: jest.fn(),
@@ -150,17 +149,19 @@ jest.mock('../../api/bitpay', () => ({
150149

151150
import AuthApi from '../../api/auth';
152151
import UserApi from '../../api/user';
152+
import BitPayIdApi from '../../api/bitpay';
153153
import {getPasskeyStatus, signInWithPasskey} from '../../utils/passkey';
154154
import * as helperMethods from '../../utils/helper-methods';
155155
import {isAnonymousBrazeEid} from '../app/app.effects';
156-
import {BrazeWrapper} from '../../lib/Braze';
156+
import {Analytics} from '../analytics/analytics.effects';
157157

158158
const MockAuthApi = AuthApi as jest.Mocked<typeof AuthApi>;
159159
const MockUserApi = UserApi as jest.Mocked<typeof UserApi>;
160160
const MockGetPasskeyStatus = getPasskeyStatus as jest.Mock;
161161
const MockSignInWithPasskey = signInWithPasskey as jest.Mock;
162162
const MockIsAnonymousBrazeEid = isAnonymousBrazeEid as jest.Mock;
163-
const MockBrazeWrapperMerge = BrazeWrapper.merge as jest.Mock;
163+
const MockBitPayIdApiCall = BitPayIdApi.apiCall as jest.Mock;
164+
const MockAnalyticsEndMergingUser = Analytics.endMergingUser as jest.Mock;
164165
const MockSleep = jest
165166
.spyOn(helperMethods, 'sleep')
166167
.mockResolvedValue(undefined);
@@ -288,16 +289,16 @@ describe('startBitPayIdAnalyticsInit', () => {
288289
it('does nothing when user is falsy', async () => {
289290
const store = baseStore();
290291
await store.dispatch(startBitPayIdAnalyticsInit(null as any));
291-
expect(MockBrazeWrapperMerge).not.toHaveBeenCalled();
292+
expect(MockBitPayIdApiCall).not.toHaveBeenCalled();
292293
});
293294

294-
it('calls BrazeWrapper.merge when brazeEid is anonymous and differs from user eid', async () => {
295+
it('calls the mergeBrazeUser API when brazeEid is anonymous and differs from user eid', async () => {
295296
MockIsAnonymousBrazeEid.mockReturnValueOnce(true);
296297

297298
const store = configureTestStore({
298299
BITPAY_ID: {
299300
session: makeSession(),
300-
apiToken: {[Network.mainnet]: ''},
301+
apiToken: {[Network.mainnet]: 'token1'},
301302
},
302303
APP: {
303304
network: Network.mainnet,
@@ -310,14 +311,17 @@ describe('startBitPayIdAnalyticsInit', () => {
310311
const user = makeUser({eid: 'new-eid-xyz'});
311312
await store.dispatch(startBitPayIdAnalyticsInit(user));
312313

313-
expect(MockBrazeWrapperMerge).toHaveBeenCalledWith(
314-
'old-anon-eid',
315-
'new-eid-xyz',
314+
expect(MockBitPayIdApiCall).toHaveBeenCalledWith(
315+
'token1',
316+
'mergeBrazeUser',
317+
{
318+
userToMerge: 'old-anon-eid',
319+
},
316320
);
317321
expect(MockSleep).toHaveBeenCalledWith(5000);
318322
});
319323

320-
it('does NOT call BrazeWrapper.merge when brazeEid is not anonymous', async () => {
324+
it('does NOT call the mergeBrazeUser API when brazeEid is not anonymous', async () => {
321325
MockIsAnonymousBrazeEid.mockReturnValueOnce(false);
322326

323327
const store = configureTestStore({
@@ -334,7 +338,33 @@ describe('startBitPayIdAnalyticsInit', () => {
334338
});
335339

336340
await store.dispatch(startBitPayIdAnalyticsInit(makeUser()));
337-
expect(MockBrazeWrapperMerge).not.toHaveBeenCalled();
341+
expect(MockBitPayIdApiCall).not.toHaveBeenCalled();
342+
});
343+
344+
it('still completes the merging lifecycle (sleep + endMergingUser) when mergeBrazeUser fails', async () => {
345+
MockIsAnonymousBrazeEid.mockReturnValueOnce(true);
346+
MockBitPayIdApiCall.mockRejectedValueOnce(new Error('merge failed'));
347+
348+
const store = configureTestStore({
349+
BITPAY_ID: {
350+
session: makeSession(),
351+
apiToken: {[Network.mainnet]: 'token1'},
352+
},
353+
APP: {
354+
network: Network.mainnet,
355+
brazeEid: 'old-anon-eid',
356+
emailNotifications: {accepted: false},
357+
notificationsAccepted: false,
358+
},
359+
});
360+
361+
const user = makeUser({eid: 'new-eid-xyz'});
362+
await expect(
363+
store.dispatch(startBitPayIdAnalyticsInit(user)),
364+
).resolves.toBeUndefined();
365+
366+
expect(MockSleep).toHaveBeenCalledWith(5000);
367+
expect(MockAnalyticsEndMergingUser).toHaveBeenCalled();
338368
});
339369

340370
it('derives givenName/familyName from name when they are missing', async () => {

src/store/bitpay-id/bitpay-id.effects.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {BasicUserInfo, InitialUserData} from '../../api/user/user.types';
99
import {Network} from '../../constants';
1010
import Dosh from '../../lib/dosh';
1111
import {MixpanelWrapper} from '../../lib/Mixpanel';
12-
import {BrazeWrapper} from '../../lib/Braze';
1312
import {isAxiosError, isRateLimitError} from '../../utils/axios';
1413
import {generateSalt, hashPassword} from '../../utils/password';
1514
import {Analytics} from '../analytics/analytics.effects';
@@ -45,6 +44,27 @@ interface StartLoginParams {
4544
gCaptchaResponse?: string;
4645
}
4746

47+
export const mergeBrazeUser =
48+
(userToMerge: string): Effect<Promise<void>> =>
49+
async (dispatch, getState) =>
50+
(async () => {
51+
try {
52+
const {APP, BITPAY_ID} = getState();
53+
await BitPayIdApi.apiCall(
54+
BITPAY_ID.apiToken[APP.network],
55+
'mergeBrazeUser',
56+
{userToMerge},
57+
);
58+
} catch (err: any) {
59+
const errMsg = err instanceof Error ? err.message : JSON.stringify(err);
60+
logManager.error(
61+
'[mergeBrazeUser] Failed to merge Braze user.',
62+
errMsg,
63+
);
64+
throw err;
65+
}
66+
})();
67+
4868
export const startBitPayIdAnalyticsInit =
4969
(user: BasicUserInfo): Effect<void> =>
5070
async (dispatch, getState) => {
@@ -91,7 +111,7 @@ export const startBitPayIdAnalyticsInit =
91111
previousBrazeEid,
92112
eid,
93113
);
94-
await BrazeWrapper.merge(previousBrazeEid, eid);
114+
await dispatch(mergeBrazeUser(previousBrazeEid));
95115
} catch (error) {
96116
const errMsg =
97117
error instanceof Error ? error.message : JSON.stringify(error);

0 commit comments

Comments
 (0)