Skip to content

Commit

Permalink
destination queue
Browse files Browse the repository at this point in the history
  • Loading branch information
alexanderkirtzel committed Jul 22, 2024
1 parent 65985fd commit ec82e23
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 121 deletions.
14 changes: 2 additions & 12 deletions packages/clients/walkerjs/src/lib/consent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function setConsent(
instance: WebClient.Instance,
data: WalkerOS.Consent,
) {
const { consent, destinations, globals, user } = instance;
const { consent, destinations } = instance;

let runQueue = false;
const update: WalkerOS.Consent = {};
Expand All @@ -55,17 +55,7 @@ export function setConsent(

if (runQueue) {
Object.values(destinations).forEach((destination) => {
const queue = destination.queue || [];

// Try to push and remove successful ones from queue
destination.queue = queue.filter((event) => {
// Update previous values with the current state
event.consent = instance.consent;
event.globals = globals;
event.user = user;

return !pushToDestination(instance, destination, event, false);
});
pushToDestination(instance, destination, undefined, false);
});
}
}
119 changes: 118 additions & 1 deletion packages/clients/walkerjs/src/lib/destination.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { WebClient, WebDestination } from '../types';
import { getId } from '@elbwalker/utils';
import { debounce, getId, tryCatch, useHooks } from '@elbwalker/utils';
import { pushToDestination } from './push';
import { WalkerOS } from '@elbwalker/types';

export function addDestination(
instance: WebClient.Instance,
Expand Down Expand Up @@ -54,3 +55,119 @@ export function dataLayerDestination() {

return destination;
}

export function destinationInit(
instance: WebClient.Instance,
destination: WebDestination.Destination,
) {
// Check if the destination was initialized properly or try to do so
if (destination.init && !destination.config.init) {
const init =
useHooks(
destination.init,
'DestinationInit',
instance.hooks,
)(destination.config, instance) !== false; // Actively check for errors

destination.config.init = init;

// don't push if init is false
if (!init) return false;
}

return true; // Destination is ready to push
}

export function destinationPush(
instance: WebClient.Instance,
destination: WebDestination.Destination,
event: WalkerOS.Event,
): boolean {
const { eventConfig, mappingKey } = destinationMapping(
event,
destination.config.mapping,
);

if (eventConfig) {
// Check if event should be processed or ignored
if (eventConfig.ignore) return false;

// Check to use specific event names
if (eventConfig.name) event.event = eventConfig.name;
}

return !!tryCatch(() => {
if (eventConfig?.batch && destination.pushBatch) {
const batched = eventConfig.batched || {
key: mappingKey,
events: [],
};
batched.events.push(event);

eventConfig.batchFn =
eventConfig.batchFn ||
debounce((destination, instance) => {
useHooks(
destination.pushBatch!,
'DestinationPushBatch',
instance.hooks,
)(batched, destination.config, instance);

// Reset the batched events queue
batched.events = [];
}, eventConfig.batch);

eventConfig.batched = batched;
eventConfig.batchFn(destination, instance);
} else {
// It's time to go to the destination's side now
useHooks(destination.push, 'DestinationPush', instance.hooks)(
event,
destination.config,
eventConfig,
instance,
);
}

return true;
})();
}

export function destinationMapping(
event: WalkerOS.Event,
mapping?: WebDestination.Mapping<never>,
) {
// Check for an active mapping for proper event handling
let eventConfig: undefined | WebDestination.EventConfig;
let mappingKey = '';

if (mapping) {
let mappingEntityKey = event.entity; // Default key is the entity name
let mappingEntity = mapping[mappingEntityKey];

if (!mappingEntity) {
// Fallback to the wildcard key
mappingEntityKey = '*';
mappingEntity = mapping[mappingEntityKey];
}

if (mappingEntity) {
let mappingActionKey = event.action; // Default action is the event action
eventConfig = mappingEntity[mappingActionKey];

if (!eventConfig) {
// Fallback to the wildcard action
mappingActionKey = '*';
eventConfig = mappingEntity[mappingActionKey];
}

// Handle individual event settings
if (eventConfig) {
// Save the mapping key for later use
mappingKey = `${mappingEntityKey} ${mappingActionKey}`;
}
}
}

return { eventConfig, mappingKey };
}
140 changes: 32 additions & 108 deletions packages/clients/walkerjs/src/lib/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { handleCommand, handleEvent } from './handle';
import {
Const,
assign,
debounce,
isSameType,
tryCatch,
useHooks,
} from '@elbwalker/utils';
import { allowedToPush } from './consent';
import { getEntities } from './walker';
import { destinationInit, destinationPush } from './destination';

export function createPush(instance: WebClient.Instance): WebClient.Elb {
const push = (
Expand Down Expand Up @@ -104,120 +104,44 @@ export function pushPredefined(
});
}

// @TODO pushToDestinations and include the loop
export function pushToDestination(
instance: WebClient.Instance,
destination: WebDestination.Destination,
event: WalkerOS.Event,
event?: WalkerOS.Event,
useQueue = true,
): boolean {
// Copy the event to prevent mutation
event = assign({}, event);

// Always check for required consent states before pushing
if (!allowedToPush(instance, destination)) {
if (useQueue) {
destination.queue = destination.queue || [];
destination.queue.push(event);
}

// Stop processing the event on this destination
return false;
}

// Check for an active mapping for proper event handling
let mappingEvent: undefined | WebDestination.EventConfig;
const mapping = destination.config.mapping;
let mappingKey = '';

if (mapping) {
let mappingEntityKey = event.entity; // Default key is the entity name
let mappingEntity = mapping[mappingEntityKey];

if (!mappingEntity) {
// Fallback to the wildcard key
mappingEntityKey = '*';
mappingEntity = mapping[mappingEntityKey];
}

if (mappingEntity) {
let mappingActionKey = event.action; // Default action is the event action
mappingEvent = mappingEntity[mappingActionKey];

if (!mappingEvent) {
// Fallback to the wildcard action
mappingActionKey = '*';
mappingEvent = mappingEntity[mappingActionKey];
}

// Handle individual event settings
if (mappingEvent) {
// Check if event should be processed or ignored
if (mappingEvent.ignore) return false;

// Check to use specific event names
if (mappingEvent.name) event.event = mappingEvent.name;

// Save the mapping key for later use
mappingKey = `${mappingEntityKey} ${mappingActionKey}`;
}
}
}

const pushed = !!tryCatch(() => {
// Destination initialization
// Check if the destination was initialized properly or try to do so
if (destination.init && !destination.config.init) {
const init =
useHooks(
destination.init,
'DestinationInit',
instance.hooks,
)(destination.config, instance) !== false; // Actively check for errors

destination.config.init = init;

// don't push if init is false
if (!init) return false;
}
) {
destination.queue = destination.queue || [];

// Debounce the event if needed
const batch = mappingEvent?.batch;
if (mappingEvent && batch && destination.pushBatch) {
const batched = mappingEvent.batched || {
key: mappingKey,
events: [],
};
batched.events.push(event);

mappingEvent.batchFn =
mappingEvent.batchFn ||
debounce((destination, instance) => {
useHooks(
destination.pushBatch!,
'DestinationPushBatch',
instance.hooks,
)(batched, destination.config, instance);

// Reset the batched events queue
batched.events = [];
}, batch);

mappingEvent.batched = batched;
mappingEvent.batchFn(destination, instance);
} else {
// It's time to go to the destination's side now
useHooks(destination.push, 'DestinationPush', instance.hooks)(
event,
destination.config,
mappingEvent,
instance,
);
}
// Add event to queue stack
if (event && useQueue) destination.queue.push(event);

return true;
})();
if (
// Always check for required consent states before pushing
!allowedToPush(instance, destination) ||
// Initialize the destination if needed
!tryCatch(destinationInit)(instance, destination)
)
// Don't push if not allowed or not initialized
return false;

return pushed;
const { consent, globals, user } = instance;

// Process the destinations event queue
destination.queue = destination.queue.filter((event) => {
// Copy the event to prevent mutation
event = event
? assign(event, {
// Update previous values with the current state
consent,
globals,
user,
})
: event; // undefined

//Try to push and remove successful ones from queue
return !destinationPush(instance, destination, event);
});
}

function createEventOrCommand(
Expand Down

0 comments on commit ec82e23

Please sign in to comment.