Skip to content

Commit 1535005

Browse files
authored
feat: enrich commerce event action from Rokt.CommerceEventType (#97)
1 parent 850e3c2 commit 1535005

2 files changed

Lines changed: 247 additions & 2 deletions

File tree

src/Rokt-Kit.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { Batch, KitInterface, IMParticleUser, SDKEvent } from '@mparticle/web-sd
2020
import type { IUserIdentities } from '@mparticle/web-sdk';
2121

2222
// BaseEvent not re-exported from @mparticle/web-sdk/internal, so we import directly from @mparticle/event-models.
23-
import { BaseEvent } from '@mparticle/event-models';
23+
import { BaseEvent, CommerceEvent } from '@mparticle/event-models';
2424

2525
interface RoktKitSettings {
2626
accountId: string;
@@ -915,10 +915,29 @@ class RoktKit implements KitInterface {
915915
this.batchQueue.push(batch);
916916
return 'Batch queued for forwarder: ' + name;
917917
}
918-
this.sendBatchStream(this.mergePendingIdentityEvents(batch));
918+
const enrichedBatch = this.enrichCommerceEventTypes(this.mergePendingIdentityEvents(batch));
919+
this.sendBatchStream(enrichedBatch);
919920
return 'Successfully sent batch to forwarder: ' + name;
920921
}
921922

923+
private enrichCommerceEventTypes(batch: Batch): Batch {
924+
if (!batch.events) {
925+
return batch;
926+
}
927+
for (const event of batch.events) {
928+
if (event.event_type !== 'commerce_event') continue;
929+
930+
const { data } = event as CommerceEvent;
931+
if (!data) continue;
932+
933+
const commerceType = data.custom_flags?.['Rokt.CommerceEventType'];
934+
if (commerceType && isObject(data.product_action)) {
935+
(data.product_action as { action: string }).action = commerceType;
936+
}
937+
}
938+
return batch;
939+
}
940+
922941
private sendBatchStream(batch: Batch): void {
923942
if (window.Rokt && typeof window.Rokt.__batch_stream__ === 'function') {
924943
if (this.batchStreamQueue.length) {

test/src/tests.spec.ts

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5758,6 +5758,232 @@ describe('Rokt Forwarder', () => {
57585758
});
57595759
});
57605760

5761+
describe('#enrichCommerceEventTypes', () => {
5762+
let mockCommerceBatch: Batch;
5763+
5764+
beforeEach(() => {
5765+
(window as any).mParticle.forwarder.batchQueue = [];
5766+
(window as any).mParticle.forwarder.batchStreamQueue = [];
5767+
(window as any).mParticle.forwarder.pendingIdentityEvents = [];
5768+
(window as any).Rokt = new (MockRoktForwarder as any)();
5769+
(window as any).Rokt.createLauncher = async function () {
5770+
return Promise.resolve({
5771+
selectPlacements: function (options: any) {
5772+
(window as any).mParticle.Rokt.selectPlacementsOptions = options;
5773+
(window as any).mParticle.Rokt.selectPlacementsCalled = true;
5774+
},
5775+
});
5776+
};
5777+
(window as any).mParticle.Rokt = (window as any).Rokt;
5778+
(window as any).mParticle.Rokt.attachKitCalled = false;
5779+
(window as any).mParticle.Rokt.attachKit = async (kit: any) => {
5780+
(window as any).mParticle.Rokt.attachKitCalled = true;
5781+
(window as any).mParticle.Rokt.kit = kit;
5782+
Promise.resolve();
5783+
};
5784+
(window as any).mParticle.Rokt.setLocalSessionAttribute = function (key: any, value: any) {
5785+
(window as any).mParticle._Store.localSessionAttributes[key] = value;
5786+
};
5787+
(window as any).mParticle.Rokt.getLocalSessionAttributes = function () {
5788+
return (window as any).mParticle._Store.localSessionAttributes;
5789+
};
5790+
(window as any).mParticle.forwarder.launcher = {
5791+
selectPlacements: function (options: any) {
5792+
(window as any).mParticle.Rokt.selectPlacementsOptions = options;
5793+
(window as any).mParticle.Rokt.selectPlacementsCalled = true;
5794+
},
5795+
};
5796+
(window as any).mParticle.Rokt.filters = {
5797+
userAttributesFilters: [],
5798+
filterUserAttributes: function (attributes: any) {
5799+
return attributes;
5800+
},
5801+
filteredUser: {
5802+
getMPID: function () {
5803+
return '123';
5804+
},
5805+
},
5806+
};
5807+
5808+
mockCommerceBatch = {
5809+
mpid: 'test-mpid-123',
5810+
events: [
5811+
{
5812+
event_type: 'commerce_event',
5813+
data: {
5814+
custom_flags: { 'Rokt.CommerceEventType': 'payment_succeeded' },
5815+
product_action: {
5816+
action: 'unknown',
5817+
products: [{ id: 'SKU-1', name: 'Test Product', price: 50, quantity: 1 }],
5818+
},
5819+
},
5820+
},
5821+
],
5822+
};
5823+
});
5824+
5825+
afterEach(() => {
5826+
delete (window as any).Rokt.__batch_stream__;
5827+
(window as any).mParticle.forwarder.batchQueue = [];
5828+
(window as any).mParticle.forwarder.batchStreamQueue = [];
5829+
(window as any).mParticle.forwarder.pendingIdentityEvents = [];
5830+
(window as any).mParticle.forwarder.isInitialized = false;
5831+
(window as any).mParticle.Rokt.attachKitCalled = false;
5832+
});
5833+
5834+
it('should replace action with Rokt.CommerceEventType custom flag for commerce events', async () => {
5835+
const receivedBatches: any[] = [];
5836+
(window as any).Rokt.__batch_stream__ = function (payload: any) {
5837+
receivedBatches.push(payload);
5838+
};
5839+
5840+
await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {});
5841+
await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled);
5842+
5843+
(window as any).mParticle.forwarder.processBatch(mockCommerceBatch);
5844+
5845+
expect(receivedBatches.length).toBe(1);
5846+
expect(receivedBatches[0].events[0].data.product_action.action).toBe('payment_succeeded');
5847+
});
5848+
5849+
it('should not modify action when Rokt.CommerceEventType custom flag is absent', async () => {
5850+
const receivedBatches: any[] = [];
5851+
(window as any).Rokt.__batch_stream__ = function (payload: any) {
5852+
receivedBatches.push(payload);
5853+
};
5854+
5855+
mockCommerceBatch.events[0].data.custom_flags = {};
5856+
5857+
await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {});
5858+
await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled);
5859+
5860+
(window as any).mParticle.forwarder.processBatch(mockCommerceBatch);
5861+
5862+
expect(receivedBatches.length).toBe(1);
5863+
expect(receivedBatches[0].events[0].data.product_action.action).toBe('unknown');
5864+
});
5865+
5866+
it('should not modify non-commerce events', async () => {
5867+
const receivedBatches: any[] = [];
5868+
(window as any).Rokt.__batch_stream__ = function (payload: any) {
5869+
receivedBatches.push(payload);
5870+
};
5871+
5872+
const nonCommerceBatch: Batch = {
5873+
mpid: 'test-mpid-123',
5874+
events: [
5875+
{
5876+
event_type: 'custom_event',
5877+
data: {
5878+
event_name: 'Test Event',
5879+
custom_event_type: 'other',
5880+
custom_flags: { 'Rokt.CommerceEventType': 'payment_succeeded' },
5881+
},
5882+
},
5883+
],
5884+
};
5885+
5886+
await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {});
5887+
await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled);
5888+
5889+
(window as any).mParticle.forwarder.processBatch(nonCommerceBatch);
5890+
5891+
expect(receivedBatches.length).toBe(1);
5892+
expect(receivedBatches[0].events[0].event_type).toBe('custom_event');
5893+
expect(receivedBatches[0].events[0].data.product_action).toBeUndefined();
5894+
});
5895+
5896+
it('should enrich only commerce events in a mixed batch', async () => {
5897+
const receivedBatches: any[] = [];
5898+
(window as any).Rokt.__batch_stream__ = function (payload: any) {
5899+
receivedBatches.push(payload);
5900+
};
5901+
5902+
const mixedBatch: Batch = {
5903+
mpid: 'test-mpid-123',
5904+
events: [
5905+
{
5906+
event_type: 'custom_event',
5907+
data: { event_name: 'Page View', custom_event_type: 'other' },
5908+
},
5909+
{
5910+
event_type: 'commerce_event',
5911+
data: {
5912+
custom_flags: { 'Rokt.CommerceEventType': 'refund_initiated' },
5913+
product_action: { action: 'unknown', products: [] },
5914+
},
5915+
},
5916+
{
5917+
event_type: 'commerce_event',
5918+
data: {
5919+
custom_flags: {},
5920+
product_action: { action: 'purchase', products: [] },
5921+
},
5922+
},
5923+
],
5924+
};
5925+
5926+
await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {});
5927+
await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled);
5928+
5929+
(window as any).mParticle.forwarder.processBatch(mixedBatch);
5930+
5931+
expect(receivedBatches.length).toBe(1);
5932+
const events = receivedBatches[0].events;
5933+
expect(events[0].event_type).toBe('custom_event');
5934+
expect(events[1].data.product_action.action).toBe('refund_initiated');
5935+
expect(events[2].data.product_action.action).toBe('purchase');
5936+
});
5937+
5938+
it('should not throw when product_action is null', async () => {
5939+
const receivedBatches: any[] = [];
5940+
(window as any).Rokt.__batch_stream__ = function (payload: any) {
5941+
receivedBatches.push(payload);
5942+
};
5943+
5944+
const promoBatch: Batch = {
5945+
mpid: 'test-mpid-123',
5946+
events: [
5947+
{
5948+
event_type: 'commerce_event',
5949+
data: {
5950+
custom_flags: {},
5951+
promotion_action: { action: 'view', promotions: [{ id: 'promo-1', name: 'Summer Sale' }] },
5952+
},
5953+
},
5954+
],
5955+
};
5956+
5957+
await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {});
5958+
await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled);
5959+
5960+
expect(() => {
5961+
(window as any).mParticle.forwarder.processBatch(promoBatch);
5962+
}).not.toThrow();
5963+
5964+
expect(receivedBatches.length).toBe(1);
5965+
expect(receivedBatches[0].events[0].data.promotion_action.action).toBe('view');
5966+
expect(receivedBatches[0].events[0].data.product_action).toBeUndefined();
5967+
});
5968+
5969+
it('should handle batch with no events', async () => {
5970+
const receivedBatches: any[] = [];
5971+
(window as any).Rokt.__batch_stream__ = function (payload: any) {
5972+
receivedBatches.push(payload);
5973+
};
5974+
5975+
const emptyBatch: Batch = { mpid: 'test-mpid-123', events: [] };
5976+
5977+
await (window as any).mParticle.forwarder.init({ accountId: '123456' }, reportService.cb, true, null, {});
5978+
await waitForCondition(() => (window as any).mParticle.Rokt.attachKitCalled);
5979+
5980+
(window as any).mParticle.forwarder.processBatch(emptyBatch);
5981+
5982+
expect(receivedBatches.length).toBe(1);
5983+
expect(receivedBatches[0].events.length).toBe(0);
5984+
});
5985+
});
5986+
57615987
describe('#_setRoktSessionId', () => {
57625988
let setIntegrationAttributeCalls: any[];
57635989

0 commit comments

Comments
 (0)