Skip to content

Commit

Permalink
feat(analytics): add support to consent mode in Firebase
Browse files Browse the repository at this point in the history
This adds support to consent mode in Firebase
through `react-native-firebase` package in
`react-native-analytics`. The required version
of `react-native-firebase` that must be installed
for this to work is >= 18.9.0 as this is the first
version that exposes the `setConsent` method.
  • Loading branch information
Bruno Oliveira authored and boliveira committed Feb 26, 2024
1 parent d555078 commit 930bc79
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 6 deletions.
6 changes: 3 additions & 3 deletions packages/react-native-analytics/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"@castleio/react-native-castle": "^1.1.5",
"@farfetch/blackout-core": "^1.58.2",
"@react-native-async-storage/async-storage": "^1.16.0",
"@react-native-firebase/analytics": "^14.11.0",
"@react-native-firebase/app": "^14.11.0",
"@react-native-firebase/analytics": "^18.9.0",
"@react-native-firebase/app": "^18.9.0",
"axios": "^0.21.4",
"react": "^16.8.1",
"react-native": "^0.62.3",
Expand All @@ -25,7 +25,7 @@
"@castleio/react-native-castle": "^1.1.5",
"@farfetch/blackout-core": "^1.58.2",
"@react-native-async-storage/async-storage": "^1.6.1",
"@react-native-firebase/analytics": "^14.11.0",
"@react-native-firebase/analytics": "^18.9.0",
"react-native": "^0.62.3",
"react-native-device-info": "^5.5.8",
"react-native-forter": "^0.1.10"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
OPTION_ON_SET_USER_HANDLER,
OPTION_SCREEN_VIEWS_MAPPER,
OPTION_SET_CUSTOM_USER_ID_PROPERTY,
OPTION_GOOGLE_CONSENT_CONFIG,
} from './constants';
import {
defaultEventsMapper,
Expand Down Expand Up @@ -49,13 +50,14 @@ class FirebaseAnalytics extends Integration {
* @param {object} options - User configured options.
* @param {object} loadData - Analytics' load event data.
*/
constructor(options, loadData) {
constructor(options = {}, loadData) {
super(options, loadData);

checkRNFirebaseAnalyticsInstalled();

this.initialize(options);
this.onSetUser(loadData, options);
this.googleConsentConfig = options[OPTION_GOOGLE_CONSENT_CONFIG];
}

/**
Expand Down Expand Up @@ -363,6 +365,43 @@ class FirebaseAnalytics extends Integration {
await firebaseAnalytics().logEvent(firebaseEvent, properties);
}
}

/**
* Overrides super.setConsent to update the consent values
* on the native side (via react-native-firebase) by matching
* the consent config with the user's given consent.
*
* @param {object} consentData - Consent object containing the user consent.
*/
async setConsent(consentData) {
if (this.googleConsentConfig) {
// Dealing with null or undefined consent values
const safeConsent = consentData || {};

// Fill consent value into consent element, using analytics consent categories
const consentValues = Object.keys(this.googleConsentConfig).reduce(
(result, consentKey) => {
let consentValue = false;

const consent = this.googleConsentConfig[consentKey];

if (consent && consent.categories) {
consentValue = consent.categories.every(
consentCategory => safeConsent[consentCategory],
);
}

return {
...result,
[consentKey]: consentValue,
};
},
{},
);

await firebaseAnalytics().setConsent(consentValues);
}
}
}

export default FirebaseAnalytics;
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import screenTypes from '../../../screenTypes';
import {
MAX_PRODUCT_CATEGORIES,
OPTION_EVENTS_MAPPER,
OPTION_GOOGLE_CONSENT_CONFIG,
OPTION_SCREEN_VIEWS_MAPPER,
OPTION_SET_CUSTOM_USER_ID_PROPERTY,
} from '../constants';
Expand All @@ -31,6 +32,7 @@ const mockFirebaseAnalyticsReturn = {
setUserProperties: jest.fn(),
logEvent: jest.fn(),
logSelectItem: jest.fn(),
setConsent: jest.fn(),
};

jest.mock('@react-native-firebase/analytics', () => {
Expand Down Expand Up @@ -375,9 +377,9 @@ describe('FirebaseAnalyticsIntegration instance', () => {
},
};

createInstance(null, loadData);
createInstance(undefined, loadData);

expect(spy).toHaveBeenCalledWith(loadData, null);
expect(spy).toHaveBeenCalledWith(loadData, {});
});

it('Should call custom `onSetUser` function if passed via the integration options', () => {
Expand Down Expand Up @@ -982,4 +984,37 @@ describe('FirebaseAnalyticsIntegration instance', () => {
});
});
});

describe('Consent', () => {
it('Should update the user consent on the native side when setConsent is called', async () => {
const instance = createInstance({
[OPTION_GOOGLE_CONSENT_CONFIG]: {
ad_user_data: { categories: ['marketing'] },
ad_personalization: { categories: ['marketing'] },
analytics_storage: { categories: ['marketing'] },
ad_storage: { categories: ['marketing'] },
},
});

await instance.setConsent({ marketing: true });

expect(firebaseAnalytics().setConsent).toHaveBeenCalledWith({
ad_personalization: true,
ad_storage: true,
ad_user_data: true,
analytics_storage: true,
});

jest.clearAllMocks();

await instance.setConsent({ marketing: false });

expect(firebaseAnalytics().setConsent).toHaveBeenCalledWith({
ad_personalization: false,
ad_storage: false,
ad_user_data: false,
analytics_storage: false,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export const OPTION_EVENTS_MAPPER = 'eventsMapper';
export const OPTION_SCREEN_VIEWS_MAPPER = 'screenViewsMapper';
export const OPTION_ON_SET_USER_HANDLER = 'onSetUser';
export const OPTION_SET_CUSTOM_USER_ID_PROPERTY = 'setCustomUserIdProperty';
export const OPTION_GOOGLE_CONSENT_CONFIG = 'googleConsentConfig';

export const MESSAGE_PREFIX = '[FirebaseAnalytics]';

0 comments on commit 930bc79

Please sign in to comment.