Skip to content

Commit 2379e99

Browse files
accurate consent handling
1 parent f7ac8b6 commit 2379e99

File tree

9 files changed

+184
-86
lines changed

9 files changed

+184
-86
lines changed

packages/destinations/web/piwikpro/src/index.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,25 @@ describe('Destination PiwikPro', () => {
5151

5252
test('pageview', async () => {
5353
const page_view = getEvent('page view');
54+
const mockFnIgnorePageView = jest.fn();
5455
elb('walker destination', destination);
56+
elb('walker destination', {
57+
push: mockFnIgnorePageView,
58+
config: { mapping: { page: { view: { ignore: true } } } },
59+
});
5560

5661
await elb(page_view);
5762
expect(mockFn).toHaveBeenCalledWith([
5863
'trackPageView',
5964
page_view.data.title,
6065
]);
66+
expect(mockFnIgnorePageView).toHaveBeenCalledTimes(0);
6167

62-
jest.clearAllMocks();
63-
destination.config.mapping = { page: { view: { ignore: true } } };
64-
await elb(page_view);
65-
expect(mockFn).toHaveBeenCalledTimes(0);
68+
// Make sure that mockFnIgnorePageView is called for other events
69+
mockFn.mockClear();
70+
await elb('foo bar');
71+
expect(mockFn).toHaveBeenCalledTimes(1);
72+
expect(mockFnIgnorePageView).toHaveBeenCalledTimes(1);
6673
});
6774

6875
test('event ecommerceOrder', async () => {

packages/sources/node/src/__tests__/destination.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ describe('Destination', () => {
291291
expect(mockPush).toHaveBeenCalledTimes(1);
292292
expect(mockPush.mock.calls[0][0]).toEqual(
293293
expect.objectContaining({
294-
consent: { client: true, demo: true },
294+
consent: { demo: true },
295295
user: { id: 'us3r', session: 's3ss10n' },
296296
globals: { foo: 'bar' },
297297
}),
@@ -378,9 +378,10 @@ describe('Destination', () => {
378378
);
379379

380380
result = await elb('walker consent', { test: true });
381+
381382
expect(mockPushConsent.mock.calls[0][0]).toEqual(
382383
expect.objectContaining({
383-
consent: { client: true, test: true },
384+
consent: { test: true },
384385
}),
385386
);
386387
expect(result).toStrictEqual(

packages/sources/node/src/lib/handle.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,5 @@ export const handleEvent: SourceNode.HandleEvent = async (instance, event) => {
5959
// Check if walker is allowed to run
6060
if (!instance.allowed) return createResult({ status: { ok: false } });
6161

62-
// Add event to internal queue
63-
instance.queue.push(event);
64-
65-
return createResult(
66-
await pushToDestinations(instance, instance.destinations, event),
67-
);
62+
return createResult(await pushToDestinations(instance, event));
6863
};

packages/sources/walkerjs/src/__tests__/destination.test.ts

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ describe('Destination', () => {
4343
expect.objectContaining({
4444
event: event.event,
4545
}),
46-
config,
46+
{ init: true },
4747
undefined,
4848
expect.anything(),
4949
);
@@ -148,35 +148,6 @@ describe('Destination', () => {
148148
);
149149
});
150150

151-
test('preventing data manipulation', async () => {
152-
const data = { a: 1 };
153-
const mockPushUpdate = jest.fn().mockImplementation((event) => {
154-
event.data.foo = 'bar';
155-
});
156-
157-
const destinationUpdate = {
158-
init: mockInit,
159-
push: mockPushUpdate,
160-
config: {},
161-
};
162-
163-
elb('walker run');
164-
elb('walker destination', destinationUpdate);
165-
elb('walker destination', destination);
166-
await elb('entity action', data);
167-
expect(mockPushUpdate).toHaveBeenCalledTimes(1);
168-
expect(mockPush).toHaveBeenCalledTimes(1);
169-
expect(mockPush).toHaveBeenCalledWith(
170-
expect.objectContaining({
171-
event: 'entity action',
172-
data,
173-
}),
174-
config,
175-
undefined,
176-
expect.anything(),
177-
);
178-
});
179-
180151
test('broken destination', async () => {
181152
elb('walker run');
182153

@@ -376,7 +347,7 @@ describe('Destination', () => {
376347

377348
test('consent', async () => {
378349
jest.clearAllMocks();
379-
const { elb } = createSourceWalkerjs({
350+
const { elb, instance } = createSourceWalkerjs({
380351
consent: { functional: true, marketing: false },
381352
pageview: false,
382353
session: false,

packages/sources/walkerjs/src/lib/handle.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export async function handleCommand(
4141
break;
4242
case Const.Commands.Consent:
4343
if (isObject(data)) {
44-
const foo = await setConsent(instance, data as WalkerOS.Consent);
44+
await setConsent(instance, data as WalkerOS.Consent);
4545
}
4646
break;
4747
case Const.Commands.Custom:
@@ -112,10 +112,7 @@ export async function handleEvent(
112112
return result;
113113
}
114114

115-
// Add event to internal queue
116-
instance.queue.push(event);
117-
118-
await pushToDestinations(instance, instance.destinations, event);
115+
await pushToDestinations(instance, event);
119116

120117
result.status.ok = true;
121118
})();

packages/types/src/destination.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Handler, Mapping, On, WalkerOS } from '.';
33
export interface Destination<Custom = unknown, CustomEvent = unknown> {
44
config: Config<Custom, CustomEvent>; // Configuration settings for the destination
55
queue?: WalkerOS.Events; // Non processed events yet and reset with each new run
6+
dlq?: DLQ; // Failed events
67
type?: string; // The type of the destination
78
init?: InitFn<Custom, CustomEvent>;
89
push: PushFn<Custom, CustomEvent>;
@@ -91,9 +92,11 @@ export type Push = {
9192

9293
export type PushSuccess = Array<Ref>;
9394

95+
export type PushError = string; // @TODO should be an error object
96+
9497
export type PushFailure = Array<Ref & { error: PushError }>;
9598

96-
export type PushError = string;
99+
export type DLQ = Array<[WalkerOS.Event, unknown]>; // @TODO should be PushError
97100

98101
export type Result = {
99102
successful: PushSuccess;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import type { Destination, WalkerOS } from '@elbwalker/types';
2+
import { createEvent, clone } from '@elbwalker/utils';
3+
import { pushToDestinations } from '../';
4+
5+
describe('Destination', () => {
6+
let event: WalkerOS.Event;
7+
let destination: Destination.Destination;
8+
let config: Destination.Config;
9+
let mockInit: jest.Mock;
10+
let mockPush: jest.Mock;
11+
12+
function createDestination(
13+
args?: Partial<Destination.Destination>,
14+
): Destination.Destination {
15+
return {
16+
init: mockInit,
17+
push: mockPush,
18+
config: {},
19+
...args,
20+
} as Destination.Destination;
21+
}
22+
23+
function createInstance(
24+
args?: Partial<WalkerOS.Instance>,
25+
): WalkerOS.Instance {
26+
return {
27+
config: {},
28+
destinations: { foo: destination },
29+
globals: {},
30+
hooks: {},
31+
user: {},
32+
consent: {},
33+
queue: [],
34+
...args,
35+
} as unknown as WalkerOS.Instance;
36+
}
37+
38+
beforeEach(() => {
39+
event = createEvent();
40+
mockInit = jest.fn(); //.mockImplementation(console.log);
41+
mockPush = jest.fn(); //.mockImplementation(console.log);
42+
43+
config = { init: false };
44+
45+
destination = {
46+
init: mockInit,
47+
push: mockPush,
48+
config,
49+
};
50+
});
51+
52+
test('preventing data manipulation', async () => {
53+
const clonedEvent = clone(event);
54+
const mockPushUpdate = jest.fn().mockImplementation((event) => {
55+
event.data.foo = 'bar';
56+
});
57+
58+
const destinationUpdate = {
59+
init: mockInit,
60+
push: mockPushUpdate,
61+
config: {},
62+
};
63+
64+
await pushToDestinations(createInstance(), event, {
65+
destinationUpdate,
66+
destination,
67+
});
68+
expect(mockPushUpdate).toHaveBeenCalledTimes(1);
69+
expect(mockPush).toHaveBeenCalledTimes(1);
70+
expect(mockPush).toHaveBeenCalledWith(
71+
clonedEvent,
72+
{ init: true },
73+
undefined,
74+
expect.anything(),
75+
);
76+
});
77+
78+
test('failing init', async () => {
79+
// Simulate a failed init
80+
mockInit.mockImplementation(() => false);
81+
82+
await pushToDestinations(createInstance(), event);
83+
expect(mockInit).toHaveBeenCalledTimes(1);
84+
expect(mockPush).toHaveBeenCalledTimes(0);
85+
expect(destination.config.init).toBeFalsy();
86+
});
87+
88+
test('DLQ', async () => {
89+
const event = createEvent();
90+
// Simulate a failing push
91+
mockPush.mockImplementation(() => {
92+
throw new Error('kaputt');
93+
});
94+
95+
const destination = createDestination();
96+
const result = await pushToDestinations(createInstance(), event, {
97+
destination,
98+
});
99+
expect(result.failed).toHaveLength(1);
100+
expect(result.status.ok).toBeFalsy();
101+
expect(mockPush).toHaveBeenCalledTimes(1);
102+
expect(destination.dlq).toContainEqual([event, new Error('kaputt')]);
103+
});
104+
105+
test('skip on denied consent', async () => {});
106+
});

packages/utils/src/core/consent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function setConsent(
3131
instance: WalkerOS.Instance,
3232
data: WalkerOS.Consent,
3333
) {
34-
const { consent, destinations } = instance;
34+
const { consent } = instance;
3535

3636
let runQueue = false;
3737
const update: WalkerOS.Consent = {};
@@ -51,5 +51,5 @@ export function setConsent(
5151
onApply(instance, 'consent', undefined, update);
5252

5353
// Process previous events if not disabled
54-
if (runQueue) return pushToDestinations(instance, destinations);
54+
if (runQueue) return pushToDestinations(instance);
5555
}

0 commit comments

Comments
 (0)