Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(analytics): add support to consent mode in Firebase #66

Merged
merged 1 commit into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]';
Loading