Skip to content

Commit 63ef30c

Browse files
feat: remove Braze Segment events filtering (#29134)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until this PR meets the canonical Definition of Ready For Review in `docs/readme/ready-for-review.md`. In short: the template must be materially complete (not just section titles present), all status checks must be currently passing, and the only expected follow-up commits must be reviewer-driven. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> Remove Braze Segment events filtering. As a consequence, all Segments events will be forwarded to Braze ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: ## **Related issues** Fixes: https://consensyssoftware.atlassian.net/browse/GE-199 ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** <!-- Every checklist item must be consciously assessed before marking this PR as "Ready for review". A checked box means you deliberately considered that responsibility, not that you literally performed every action listed. Unchecked boxes are ambiguous: they are not an implicit "N/A" and they are not a silent "skip". See `docs/readme/ready-for-review.md` for the full checklist semantics. --> - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I've included tests if applicable - [ ] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. #### Performance checks (if applicable) - [ ] I've tested on Android - Ideally on a mid-range device; emulator is acceptable - [ ] I've tested with a power user scenario - Use these [power-user SRPs](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/edit-v2/401401446401?draftShareId=9d77e1e1-4bdc-4be1-9ebb-ccd916988d93) to import wallets with many accounts and tokens - [ ] I've instrumented key operations with Sentry traces for production performance metrics - See [`trace()`](/app/util/trace.ts) for usage and [`addToken`](/app/components/Views/AddAsset/components/AddCustomToken/AddCustomToken.tsx#L274) for an example For performance guidelines and tooling, see the [Performance Guide](https://consensyssoftware.atlassian.net/wiki/spaces/TL1/pages/400085549067/Performance+Guide+for+Engineers). ## **Pre-merge reviewer checklist** <!-- Reviewer checklist items follow the same semantics as the author checklist: an unchecked box is ambiguous, a checked box means the reviewer consciously assessed that responsibility. See `docs/readme/ready-for-review.md`. --> - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Broadens analytics forwarding to Braze by removing allowlist gates and remote-flag syncing, which could increase data volume and risk unintentionally sending sensitive traits/events if upstream payloads aren’t tightly controlled. > > **Overview** > **Removes Braze allowlist-based filtering** so `BrazePlugin` forwards *all* `track` events and `identify` traits to the native Braze SDK whenever a `profileId` is set. > > This deletes the remote-feature-flag plumbing (`syncBrazeAllowlists`, init-messenger wiring, and `setAllowedEvents`/`setAllowedTraits`) and updates unit tests accordingly; `analytics-controller-init` now only injects `getBrazePlugin()` without subscribing to RemoteFeatureFlag updates. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 3256565. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Samir Mehta <12882259+samir-acle@users.noreply.github.com>
1 parent 25adb40 commit 63ef30c

8 files changed

Lines changed: 45 additions & 491 deletions

File tree

app/core/Braze/index.test.ts

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ import {
22
setBrazeUser,
33
clearBrazeUser,
44
getBrazePlugin,
5-
syncBrazeAllowlists,
65
resetBrazePluginForTesting,
76
} from './index';
87
import { BrazePlugin } from '../Engine/controllers/analytics-controller/BrazePlugin';
98

109
const mockGetSessionProfile = jest.fn();
1110
const mockSetBrazeProfileId = jest.fn();
12-
const mockSetAllowedEvents = jest.fn();
13-
const mockSetAllowedTraits = jest.fn();
1411
const mockSetLanguage = jest.fn();
1512

1613
jest.mock('../Engine/Engine', () => ({
@@ -29,8 +26,6 @@ jest.mock('../Engine/controllers/analytics-controller/BrazePlugin', () => ({
2926
type: 'destination',
3027
key: 'Appboy',
3128
setBrazeProfileId: mockSetBrazeProfileId,
32-
setAllowedEvents: mockSetAllowedEvents,
33-
setAllowedTraits: mockSetAllowedTraits,
3429
setLanguage: mockSetLanguage,
3530
})),
3631
}));
@@ -93,85 +88,4 @@ describe('Braze service', () => {
9388
expect(mockSetBrazeProfileId).toHaveBeenCalledWith(undefined);
9489
});
9590
});
96-
97-
describe('syncBrazeAllowlists', () => {
98-
it('updates both allowlists from a valid config', () => {
99-
syncBrazeAllowlists({
100-
allowedEvents: ['Event A', 'Event B'],
101-
allowedTraits: ['trait_x', 'trait_y'],
102-
});
103-
104-
expect(mockSetAllowedEvents).toHaveBeenCalledWith(['Event A', 'Event B']);
105-
expect(mockSetAllowedTraits).toHaveBeenCalledWith(['trait_x', 'trait_y']);
106-
});
107-
108-
it('handles partial config (only events)', () => {
109-
syncBrazeAllowlists({ allowedEvents: ['Event A'] });
110-
111-
expect(mockSetAllowedEvents).toHaveBeenCalledWith(['Event A']);
112-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
113-
});
114-
115-
it('handles partial config (only traits)', () => {
116-
syncBrazeAllowlists({ allowedTraits: ['trait_a'] });
117-
118-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
119-
expect(mockSetAllowedTraits).toHaveBeenCalledWith(['trait_a']);
120-
});
121-
122-
it('no-ops when flag value is undefined', () => {
123-
syncBrazeAllowlists(undefined);
124-
125-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
126-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
127-
});
128-
129-
it('no-ops when flag value is null', () => {
130-
syncBrazeAllowlists(null);
131-
132-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
133-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
134-
});
135-
136-
it('rejects non-object flag values', () => {
137-
syncBrazeAllowlists('not-an-object');
138-
139-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
140-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
141-
});
142-
143-
it('rejects arrays as flag values', () => {
144-
syncBrazeAllowlists(['not', 'a', 'config']);
145-
146-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
147-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
148-
});
149-
150-
it('ignores allowedEvents when it contains non-strings', () => {
151-
syncBrazeAllowlists({
152-
allowedEvents: ['valid', 123, null],
153-
allowedTraits: ['trait_a'],
154-
});
155-
156-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
157-
expect(mockSetAllowedTraits).toHaveBeenCalledWith(['trait_a']);
158-
});
159-
160-
it('ignores allowedTraits when it is not an array', () => {
161-
syncBrazeAllowlists({
162-
allowedEvents: ['Event A'],
163-
allowedTraits: 'not-an-array',
164-
});
165-
166-
expect(mockSetAllowedEvents).toHaveBeenCalledWith(['Event A']);
167-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
168-
});
169-
170-
it('no-ops when object has no valid arrays', () => {
171-
syncBrazeAllowlists({ allowedEvents: 42, allowedTraits: true });
172-
173-
expect(mockSetAllowedEvents).not.toHaveBeenCalled();
174-
expect(mockSetAllowedTraits).not.toHaveBeenCalled();
175-
});
176-
});
17791
});

app/core/Braze/index.ts

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -25,67 +25,6 @@ export function getBrazePlugin(): BrazePlugin {
2525
return brazePlugin;
2626
}
2727

28-
/**
29-
* Validate that a remote flag value has the expected BrazeAllowedConfig shape.
30-
*
31-
* @param value - The raw flag value from RemoteFeatureFlagController.
32-
* @returns Validated allowedEvents/allowedTraits arrays, or undefined.
33-
*/
34-
function validateBrazeAllowedConfig(
35-
value: unknown,
36-
): { allowedEvents?: string[]; allowedTraits?: string[] } | undefined {
37-
if (!value || typeof value !== 'object' || Array.isArray(value)) {
38-
return undefined;
39-
}
40-
41-
const record = value as Record<string, unknown>;
42-
const result: { allowedEvents?: string[]; allowedTraits?: string[] } = {};
43-
44-
if (
45-
Array.isArray(record.allowedEvents) &&
46-
record.allowedEvents.every((e: unknown) => typeof e === 'string')
47-
) {
48-
result.allowedEvents = record.allowedEvents as string[];
49-
}
50-
51-
if (
52-
Array.isArray(record.allowedTraits) &&
53-
record.allowedTraits.every((t: unknown) => typeof t === 'string')
54-
) {
55-
result.allowedTraits = record.allowedTraits as string[];
56-
}
57-
58-
return result.allowedEvents || result.allowedTraits ? result : undefined;
59-
}
60-
61-
/**
62-
* Update the Braze plugin allowlists from a remote feature flag value.
63-
* Called by analytics-controller-init on startup and on flag updates.
64-
*
65-
* @param flagValue - The raw remote feature flag value for brazeAllowedConfig.
66-
*/
67-
export function syncBrazeAllowlists(flagValue: unknown): void {
68-
try {
69-
const brazeConfig = validateBrazeAllowedConfig(flagValue);
70-
if (!brazeConfig) {
71-
return;
72-
}
73-
74-
const plugin = getBrazePlugin();
75-
if (brazeConfig.allowedEvents) {
76-
plugin.setAllowedEvents(brazeConfig.allowedEvents);
77-
}
78-
if (brazeConfig.allowedTraits) {
79-
plugin.setAllowedTraits(brazeConfig.allowedTraits);
80-
}
81-
} catch (error) {
82-
Logger.error(
83-
error as Error,
84-
'[Braze] Failed to sync allowlists from remote config',
85-
);
86-
}
87-
}
88-
8928
/**
9029
* Resolve the profile ID from the current session and forward it to the
9130
* Braze Segment plugin so all subsequent identify / track / flush calls

0 commit comments

Comments
 (0)