diff --git a/package.json b/package.json index b66b5e0..d74ed6e 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "digital-data-manager", "description": "The hassle-free way to integrate Digital Data Layer on your website.", "author": "Driveback LLC <opensource@driveback.ru>", - "version": "1.2.14", + "version": "1.2.15", "license": "MIT", "main": "dist/dd-manager.js", "scripts": { diff --git a/src/EventDataEnricher.js b/src/EventDataEnricher.js index 6342b5e..992ef33 100644 --- a/src/EventDataEnricher.js +++ b/src/EventDataEnricher.js @@ -31,20 +31,58 @@ class EventDataEnricher } static enrichIntegrationData(event, digitalData, integration) { - const enrichedEvent = clone(event); + const eventName = event.name; + let enrichedEvent = clone(event); const enrichableProps = integration.getEnrichableEventProps(event); for (const prop of enrichableProps) { - if (!dotProp.getProp(event, prop)) { + if (!dotProp.getProp(event, prop) && digitalData) { const ddlPropValue = dotProp.getProp(digitalData, prop); if (ddlPropValue !== undefined) { dotProp.setProp(enrichedEvent, prop, ddlPropValue); } } } - integration.overrideEvent(enrichedEvent); + + // handle event override + if (integration.getEventOverrideFunction()) { + integration.getEventOverrideFunction()(enrichedEvent); + } + // handle product override + if (integration.getProductOverrideFunction()) { + enrichedEvent = EventDataEnricher.overrideEventProducts(enrichedEvent, integration); + } + return enrichedEvent; } + static overrideEventProducts(event, integration) { + const eventName = event.name; + if (event.product) { + integration.getProductOverrideFunction()(event.product); + } else if (event.listing && event.listing.items) { + for (const product of event.listing.items) { + integration.getProductOverrideFunction()(product); + } + } else if (event.cart && event.cart.lineItems) { + for (const lineItem of event.cart.lineItems) { + integration.getProductOverrideFunction()(lineItem.product); + } + } else if (event.transaction && event.transaction.lineItems) { + for (const lineItem of event.transaction.lineItems) { + integration.getProductOverrideFunction()(lineItem.product); + } + } else if (event.listItem || event.listItems) { + if (event.listItem) { + integration.getProductOverrideFunction()(event.listItem.product); + } else if (event.listItems) { + for (const listItem of event.listItems) { + integration.getProductOverrideFunction()(listItem.product); + } + } + } + return event; + } + static product(product, digitalData) { let productId; diff --git a/src/Integration.js b/src/Integration.js index d7e3398..eaa401f 100644 --- a/src/Integration.js +++ b/src/Integration.js @@ -4,6 +4,7 @@ import loadIframe from './functions/loadIframe'; import loadPixel from './functions/loadPixel'; import format from './functions/format'; import noop from './functions/noop'; +import log from './functions/log'; import each from './functions/each'; import deleteProperty from './functions/deleteProperty'; import debug from 'debug'; @@ -15,6 +16,7 @@ class Integration extends EventEmitter constructor(digitalData, options, tags) { super(); this.options = options; + this.overrideFunctions = {}; if (options && options.overrideFunctions) { this.defineOverrideFunctions(options.overrideFunctions); } @@ -22,17 +24,49 @@ class Integration extends EventEmitter this.tags = tags || {}; this.onLoad = this.onLoad.bind(this); this._isEnriched = false; + this._productOverrideErrorFired = false; } defineOverrideFunctions(overrideFunctions) { if (overrideFunctions.event) { - this.overrideEvent = overrideFunctions.event.bind(this); + this.overrideFunctions.event = (event) => { + try { + overrideFunctions.event.bind(this)(event); + } catch (e) { + log(`function override error for event ${event.name} in integration ${this.getName()}: ${e}`, log.ERROR); + } + }; } if (overrideFunctions.product) { - this.overrideProduct = overrideFunctions.product.bind(this); + this.overrideFunctions.product = (product) => { + try { + overrideFunctions.product.bind(this)(product); + } catch (e) { + if (!this._productOverrideErrorFired) { + log(`function override error for product in integration ${this.getName()}: ${e}`, log.ERROR); + this._productOverrideErrorFired = true; + } + } + }; } } + getProductOverrideFunction() { + return this.overrideFunctions.product; + } + + getEventOverrideFunction() { + return this.overrideFunctions.event; + } + + setName(name) { + this.name = name; + } + + getName() { + return this.name; + } + overrideProduct() { // abstract } diff --git a/src/ddManager.js b/src/ddManager.js index dcc7ae5..e5457fa 100644 --- a/src/ddManager.js +++ b/src/ddManager.js @@ -188,7 +188,7 @@ function _initializeIntegrations(settings) { ddManager = { - VERSION: '1.2.14', + VERSION: '1.2.15', setAvailableIntegrations: (availableIntegrations) => { _availableIntegrations = availableIntegrations; @@ -276,8 +276,11 @@ ddManager = { if (!integration instanceof Integration || !name) { throw new TypeError('attempted to add an invalid integration'); } - _integrations[name] = integration; + + integration.setName(name); integration.setDDManager(ddManager); + + _integrations[name] = integration; }, getIntegration: (name) => { diff --git a/src/functions/log.js b/src/functions/log.js new file mode 100644 index 0000000..7d63119 --- /dev/null +++ b/src/functions/log.js @@ -0,0 +1,26 @@ +import noop from './noop'; + +const MESSAGE = 'message'; +const WARNING = 'warning'; +const ERROR = 'error'; + +function log(msg, type) { + /* eslint-disable */ + console.log = console.log || noop; + console.warn = console.warn || console.log; + console.error = console.error || console.warn; + if (!type) { + console.log(msg); + } else if (type === WARNING) { + console.warn(msg); + } else if (type === ERROR) { + console.error(msg); + } + /* eslint-enable */ +} + +log.MESSAGE = MESSAGE; +log.WARNING = WARNING; +log.ERROR = ERROR; + +export default log; diff --git a/src/integrations/Emarsys.js b/src/integrations/Emarsys.js index c6eff24..893c754 100644 --- a/src/integrations/Emarsys.js +++ b/src/integrations/Emarsys.js @@ -12,10 +12,9 @@ function calculateLineItemSubtotal(lineItem) { return price * quantity; } -function mapLineItems(lineItems, overrideProduct) { +function mapLineItems(lineItems) { return lineItems.map((lineItem) => { const product = lineItem.product; - overrideProduct(product); const lineItemSubtotal = lineItem.subtotal || calculateLineItemSubtotal(lineItem); return { item: product.id || product.skuCode, @@ -149,7 +148,7 @@ class Emarsys extends Integration { window.ScarabQueue.push(['setCustomerId', user.userId]); } if (cart.lineItems && cart.lineItems.length > 0) { - window.ScarabQueue.push(['cart', mapLineItems(cart.lineItems, this.overrideProduct)]); + window.ScarabQueue.push(['cart', mapLineItems(cart.lineItems)]); } else { window.ScarabQueue.push(['cart', []]); } @@ -174,7 +173,6 @@ class Emarsys extends Integration { onViewedProductDetail(event) { const product = event.product || {}; - this.overrideProduct(product); if (product.id || product.skuCode) { window.ScarabQueue.push(['view', product.id || product.skuCode]); } @@ -194,7 +192,7 @@ class Emarsys extends Integration { if (transaction.orderId && transaction.lineItems) { window.ScarabQueue.push(['purchase', { orderId: transaction.orderId, - items: mapLineItems(transaction.lineItems, this.overrideProduct), + items: mapLineItems(transaction.lineItems), }]); } go(); diff --git a/src/integrations/RetailRocket.js b/src/integrations/RetailRocket.js index c6311e9..5e69cc9 100644 --- a/src/integrations/RetailRocket.js +++ b/src/integrations/RetailRocket.js @@ -234,7 +234,6 @@ class RetailRocket extends Integration { break; } const product = lineItems[i].product; - this.overrideProduct(product); items.push({ id: product.id, qnt: lineItems[i].quantity, @@ -329,7 +328,6 @@ class RetailRocket extends Integration { let isValid = this.validateLineItem(lineItem); const product = lineItem.product; - this.overrideProduct(product); if (!product.id) { isValid = false; } @@ -345,7 +343,6 @@ class RetailRocket extends Integration { getProductId(product) { product = product || {}; - this.overrideProduct(product); const productId = product.id; return productId; diff --git a/test/EventDataEnricherSpec.js b/test/EventDataEnricherSpec.js index efe0732..9d96704 100644 --- a/test/EventDataEnricherSpec.js +++ b/test/EventDataEnricherSpec.js @@ -479,9 +479,8 @@ describe('EventDataEnricher', () => { id: '123' } }; - emarsys.initialize(); - emarsys.trackEvent(event); - assert.equal(window.ScarabQueue[0][1], 's/123'); + const enrichedEvent = EventDataEnricher.enrichIntegrationData(event, _digitalData, emarsys); + assert.equal(enrichedEvent.product.id, 's/123'); }); }); diff --git a/test/integrations/RetailRocketSpec.js b/test/integrations/RetailRocketSpec.js index 97158c7..30f36ab 100644 --- a/test/integrations/RetailRocketSpec.js +++ b/test/integrations/RetailRocketSpec.js @@ -16,7 +16,9 @@ describe('Integrations: RetailRocket', () => { userIdProperty: 'user.email', overrideFunctions: { product: (product) => { - product.id = product.id.replace(/_/g, ''); + if (product && product.id) { + product.id = product.id.replace(/_/g, ''); + } }, }, };