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

Consent plugin updates and test cases #894

Merged
merged 3 commits into from
Dec 1, 2023
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
2 changes: 0 additions & 2 deletions packages/core/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-unused-vars */

import type { SegmentClient } from './analytics';
import { Timeline } from './timeline';
import {
Expand Down
60 changes: 36 additions & 24 deletions packages/core/src/plugins/ConsentPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ConsentPlugin extends Plugin {
}

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

Expand All @@ -77,18 +77,36 @@ export class ConsentPlugin extends Plugin {
}

private injectConsentFilterIfApplicable = (plugin: Plugin) => {
if (
this.isDestinationPlugin(plugin) &&
plugin.key !== SEGMENT_DESTINATION_KEY
) {
const settings = this.analytics?.settings.get()?.[plugin.key];

if (this.isDestinationPlugin(plugin)) {
plugin.add(
new ConsentFilterPlugin(
this.containsConsentSettings(settings)
? settings.consentSettings.categories
: []
)
new ConsentFilterPlugin((event) => {
const settings = this.analytics?.settings.get() || {};
const preferences = event.context?.consent?.categoryPreferences || {};

if (plugin.key === SEGMENT_DESTINATION_KEY) {
return (
this.isConsentUpdateEvent(event) ||
!(
Object.values(preferences).every((consented) => !consented) &&
Object.entries(settings)
.filter(([k]) => k !== SEGMENT_DESTINATION_KEY)
.every(([_, v]) => this.containsConsentSettings(v))
)
);
}

const integrationSettings = settings?.[plugin.key];

if (this.containsConsentSettings(integrationSettings)) {
const categories = integrationSettings.consentSettings.categories;
return (
!this.isConsentUpdateEvent(event) &&
categories.every((category) => preferences?.[category])
);
}

return true;
})
);
}
};
Expand All @@ -105,6 +123,10 @@ export class ConsentPlugin extends Plugin {
?.categories === 'object'
);
};

private isConsentUpdateEvent(event: SegmentEvent): boolean {
return (event as TrackEventType).event === CONSENT_PREF_UPDATE_EVENT;
}
}

/**
Expand All @@ -114,21 +136,11 @@ export class ConsentPlugin extends Plugin {
class ConsentFilterPlugin extends Plugin {
type = PluginType.before;

constructor(private categories: string[]) {
constructor(private shouldAllowEvent: (event: SegmentEvent) => boolean) {
super();
}

execute(event: SegmentEvent): SegmentEvent | undefined {
const preferences = event.context?.consent?.categoryPreferences;

// if consent plugin is active but the setup isn't properly configured - events are blocked by default
if (!preferences || this.categories.length === 0) {
return undefined;
}

// all categories this destination is tagged with must be present, and allowed in consent preferences
return this.categories.every((category) => preferences?.[category])
? event
: undefined;
return this.shouldAllowEvent(event) ? event : undefined;
}
}
Submodule analytics-swift-consent added at 8e2324
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { createTestClient } from '../../../__tests__/__helpers__/setupSegmentClient';
import { ConsentPlugin } from '../../ConsentPlugin';

import {
setupTestDestinations,
createConsentProvider,
createSegmentWatcher,
} from './utils';
import consentNotEnabledAtSegment from './mockSettings/ConsentNotEnabledAtSegment.json';

describe('Consent not enabled at Segment', () => {
const createClient = () =>
createTestClient(
{
settings: consentNotEnabledAtSegment.integrations,
},
{ autoAddSegmentDestination: true }
);

test('no to all', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: false,
C0002: false,
C0003: false,
C0004: false,
C0005: false,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).toHaveBeenCalled();
expect(testDestinations.dest1.track).toHaveBeenCalled();
expect(testDestinations.dest2.track).toHaveBeenCalled();
expect(testDestinations.dest3.track).toHaveBeenCalled();
expect(testDestinations.dest4.track).toHaveBeenCalled();
expect(testDestinations.dest5.track).toHaveBeenCalled();
});

test('yes to some', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: false,
C0002: true,
C0003: false,
C0004: true,
C0005: true,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).toHaveBeenCalled();
expect(testDestinations.dest1.track).toHaveBeenCalled();
expect(testDestinations.dest2.track).toHaveBeenCalled();
expect(testDestinations.dest3.track).toHaveBeenCalled();
expect(testDestinations.dest4.track).toHaveBeenCalled();
expect(testDestinations.dest5.track).toHaveBeenCalled();
});

test('yes to all', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: true,
C0002: true,
C0003: true,
C0004: true,
C0005: true,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).toHaveBeenCalled();
expect(testDestinations.dest1.track).toHaveBeenCalled();
expect(testDestinations.dest2.track).toHaveBeenCalled();
expect(testDestinations.dest3.track).toHaveBeenCalled();
expect(testDestinations.dest4.track).toHaveBeenCalled();
expect(testDestinations.dest5.track).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { createTestClient } from '../../../__tests__/__helpers__/setupSegmentClient';
import { ConsentPlugin } from '../../ConsentPlugin';

import {
setupTestDestinations,
createConsentProvider,
createSegmentWatcher,
} from './utils';
import destinationsMultipleCategories from './mockSettings/DestinationsMultipleCategories.json';

describe('Destinations multiple categories', () => {
const createClient = () =>
createTestClient(
{
settings: destinationsMultipleCategories.integrations,
},
{ autoAddSegmentDestination: true }
);

test('no to all', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: false,
C0002: false,
C0003: false,
C0004: false,
C0005: false,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).not.toHaveBeenCalled();
expect(testDestinations.dest1.track).not.toHaveBeenCalled();
expect(testDestinations.dest2.track).not.toHaveBeenCalled();
});

test('yes to 1', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: true,
C0002: false,
C0003: false,
C0004: false,
C0005: false,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).toHaveBeenCalled();
expect(testDestinations.dest1.track).not.toHaveBeenCalled();
expect(testDestinations.dest2.track).toHaveBeenCalled();
});

test('yes to 2', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: false,
C0002: true,
C0003: false,
C0004: false,
C0005: false,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).toHaveBeenCalled();
expect(testDestinations.dest1.track).not.toHaveBeenCalled();
expect(testDestinations.dest2.track).not.toHaveBeenCalled();
});

test('yes to all', async () => {
const { client } = createClient();
const testDestinations = setupTestDestinations(client);
const mockConsentStatuses = {
C0001: true,
C0002: true,
C0003: true,
C0004: true,
C0005: true,
};

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

await client.init();

const segmentDestination = createSegmentWatcher(client);

await client.track('test');

expect(segmentDestination).toHaveBeenCalled();
expect(testDestinations.dest1.track).toHaveBeenCalled();
expect(testDestinations.dest2.track).toHaveBeenCalled();
});
});
Loading
Loading