Skip to content

Commit

Permalink
feat: relay IDFA information through consent plugin (#906)
Browse files Browse the repository at this point in the history
* Relay IDFA information through consent plugin
  • Loading branch information
zikaari authored Feb 8, 2024
1 parent a03dfc8 commit a226dc5
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 22 deletions.
36 changes: 22 additions & 14 deletions packages/core/src/plugins/ConsentPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,24 +47,25 @@ export class ConsentPlugin extends Plugin {
analytics.getPlugins().forEach(this.injectConsentFilterIfApplicable);
analytics.onPluginLoaded(this.injectConsentFilterIfApplicable);
this.consentCategoryProvider.setApplicableCategories(this.categories);
this.consentCategoryProvider.onConsentChange((categoryPreferences) => {
this.analytics
?.track(CONSENT_PREF_UPDATE_EVENT, {
consent: {
categoryPreferences,
},
})
.catch((e) => {
throw e;
});
this.consentCategoryProvider.onConsentChange(() => {
this.notifyConsentChange();
});

let lastDeviceAttrs = analytics.context.get()?.device;
analytics.context.onChange((c) => {
const newAttrs = c?.device;
if (
newAttrs?.adTrackingEnabled !== lastDeviceAttrs?.adTrackingEnabled ||
newAttrs?.advertisingId !== lastDeviceAttrs?.advertisingId ||
newAttrs?.trackingStatus !== lastDeviceAttrs?.trackingStatus
) {
this.notifyConsentChange();
}
lastDeviceAttrs = newAttrs;
});
}

async execute(event: SegmentEvent): Promise<SegmentEvent> {
if (this.isConsentUpdateEvent(event)) {
return event;
}

event.context = {
...event.context,
consent: {
Expand Down Expand Up @@ -143,6 +144,13 @@ export class ConsentPlugin extends Plugin {
private isConsentFeatureSetup(): boolean {
return typeof this.analytics?.consentSettings.get() === 'object';
}

private notifyConsentChange() {
// actual preferences will be attached in the execute method
this.analytics?.track(CONSENT_PREF_UPDATE_EVENT).catch((e) => {
throw e;
});
}
}

/**
Expand Down
95 changes: 95 additions & 0 deletions packages/core/src/plugins/__tests__/consent/idfa.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { IdfaPlugin } from '@segment/analytics-react-native-plugin-idfa';
import { createTestClient } from '../../../test-helpers';
import { ConsentPlugin } from '../../ConsentPlugin';
import { createConsentProvider } from './utils';
import noUnmappedDestinations from './mockSettings/NoUnmappedDestinations.json';
import type { Context, ContextDevice } from '@segment/analytics-react-native';

let mockIdfaValue = {
adTrackingEnabled: false,
advertisingId: 'trackMeId',
trackingStatus: 'denied',
};

jest.mock(
'@segment/analytics-react-native-plugin-idfa/lib/commonjs/AnalyticsReactNativePluginIdfa',
() => ({
AnalyticsReactNativePluginIdfa: {
getTrackingAuthorizationStatus: async () => {
return Promise.resolve(mockIdfaValue);
},
},
})
);

describe('IDFA x Consent', () => {
it('triggers consent update event on IDFA change and includes IDFA data', async () => {
const { client, expectEvent } = createTestClient(
{
settings: noUnmappedDestinations.integrations,
consentSettings: noUnmappedDestinations.consentSettings,
},
{ autoAddSegmentDestination: true }
);

const mockConsentStatuses = {
C0001: false,
C0002: false,
C0003: false,
C0004: false,
C0005: false,
};

client.add({
plugin: new ConsentPlugin(
createConsentProvider(mockConsentStatuses),
Object.keys(mockConsentStatuses)
),
});

const idfaPlugin = new IdfaPlugin(false);
client.add({
plugin: idfaPlugin,
});

await client.init();

await idfaPlugin.requestTrackingPermission();

await new Promise((r) => setTimeout(r, 1000));

expectEvent({
event: 'Segment Consent Preference',
context: expect.objectContaining({
device: expect.objectContaining({
adTrackingEnabled: false,
advertisingId: 'trackMeId',
trackingStatus: 'denied',
}) as unknown as ContextDevice,
}) as unknown as Context,
});

// update IDFA data

mockIdfaValue = {
adTrackingEnabled: true,
advertisingId: 'trackMeId',
trackingStatus: 'authorized',
};

await idfaPlugin.requestTrackingPermission();

await new Promise((r) => setTimeout(r, 1000));

expectEvent({
event: 'Segment Consent Preference',
context: expect.objectContaining({
device: expect.objectContaining({
adTrackingEnabled: true,
advertisingId: 'trackMeId',
trackingStatus: 'authorized',
}) as unknown as ContextDevice,
}) as unknown as Context,
});
});
});
25 changes: 17 additions & 8 deletions packages/plugins/plugin-onetrust/src/__tests__/OneTrust.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
Context,
DestinationPlugin,
Plugin,
PluginType,
SegmentClient,
SegmentEvent,
} from '@segment/analytics-react-native';
import { createTestClient } from '@segment/analytics-react-native/src/test-helpers';
import onChange from 'on-change';
Expand Down Expand Up @@ -63,6 +65,7 @@ class MockOneTrustSDK implements OTPublishersNativeSDK {

describe('OneTrustPlugin', () => {
let client: SegmentClient;
let expectEvent: (event: Partial<SegmentEvent>) => void;
let mockOneTrust: MockOneTrustSDK;
const mockBraze = new MockDestination('Braze');
const mockAmplitude = new MockDestination('Amplitude');
Expand All @@ -72,6 +75,7 @@ describe('OneTrustPlugin', () => {
testClient.store.reset();
jest.clearAllMocks();
client = testClient.client as unknown as SegmentClient;
expectEvent = testClient.expectEvent;
mockOneTrust = new MockOneTrustSDK();
client.add({
plugin: new OneTrustPlugin(
Expand Down Expand Up @@ -176,15 +180,20 @@ describe('OneTrustPlugin', () => {
// this is to make sure there are no unneccessary Consent Preference track calls
expect(spy).toHaveBeenCalledTimes(2);

expect(spy).toHaveBeenLastCalledWith('Segment Consent Preference', {
consent: {
categoryPreferences: {
C001: true,
C002: true,
C003: true,
C004: false,
expect(spy).toHaveBeenLastCalledWith('Segment Consent Preference');

expectEvent({
event: 'Segment Consent Preference',
context: expect.objectContaining({
consent: {
categoryPreferences: {
C001: true,
C002: true,
C003: true,
C004: false,
},
},
},
}) as unknown as Context,
});
});
});

0 comments on commit a226dc5

Please sign in to comment.