diff --git a/head.html b/head.html index 14aab57a2..3359cb012 100644 --- a/head.html +++ b/head.html @@ -3,4 +3,4 @@ - \ No newline at end of file + diff --git a/solutions/scripts/scripts.js b/solutions/scripts/scripts.js index 37ab4299f..83efc000a 100644 --- a/solutions/scripts/scripts.js +++ b/solutions/scripts/scripts.js @@ -32,6 +32,8 @@ export const DEFAULT_COUNTRY = 'au'; export const METADATA_ANALYTICS_TAGS = 'analytics-tags'; +const TARGET_TENANT = 'bitdefender'; + const HREFLANG_MAP = [ ['en-ro', { baseUrl: 'https://www.bitdefender.ro', pageType: '.html' }], ['de', { baseUrl: 'https://www.bitdefender.de', pageType: '.html' }], @@ -459,7 +461,7 @@ function getExperimentDetails() { return { experimentId, experimentVariant }; } -function pushPageLoadToDataLayer() { +function pushPageLoadToDataLayer(targetExperimentDetails) { const { hostname } = window.location; if (!hostname) { return; @@ -469,7 +471,9 @@ function pushPageLoadToDataLayer() { const environment = getEnvironment(hostname, languageCountry.country); const tags = getTags(getMetadata(METADATA_ANALYTICS_TAGS)); - const experimentDetails = getExperimentDetails(); + const experimentDetails = targetExperimentDetails ?? getExperimentDetails(); + // eslint-disable-next-line no-console + console.debug(`Experiment details: ${JSON.stringify(experimentDetails)}`); pushToDataLayer('page load started', { pageInstanceID: environment, @@ -511,7 +515,13 @@ async function loadEager(doc) { await window.hlx.plugins.run('loadEager'); - pushPageLoadToDataLayer(); + let targetExperimentDetails = null; + if (getMetadata('target-experiment') !== '') { + const { runTargetExperiment } = await import('./target.js'); + targetExperimentDetails = await runTargetExperiment(TARGET_TENANT); + } + + pushPageLoadToDataLayer(targetExperimentDetails); if (getMetadata('template') !== '') { loadCSS(`${window.hlx.codeBasePath}/styles/${getMetadata('template')}.css`); diff --git a/solutions/scripts/target.js b/solutions/scripts/target.js new file mode 100644 index 000000000..f85d55906 --- /dev/null +++ b/solutions/scripts/target.js @@ -0,0 +1,148 @@ +import { getMetadata, sampleRUM } from './lib-franklin.js'; + +const ADOBE_TARGET_SESSION_ID_PARAM = 'adobeTargetSessionId'; + +/** + * Convert a URL to a relative URL. + * @param url + * @returns {*|string} + */ +function getPlainPageUrl(url) { + const { pathname, search, hash } = new URL(url, window.location.href); + const plainPagePathname = pathname.endsWith('/') ? `${pathname}index.plain.html` : `${pathname}.plain.html`; + return `${plainPagePathname}${search}${hash}`; +} + +/** + * Generate a random session id. + * @param length + * @returns {string} + */ +function generateSessionID(length = 16) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let sessionID = ''; + for (let i = 0; i < length; i += 1) { + const randomIndex = Math.floor(Math.random() * characters.length); + sessionID += characters.charAt(randomIndex); + } + return sessionID; +} + +/** + * Get or create a session id for the current user. + * @returns {string} + */ +function getOrCreateSessionId() { + let sessionId = sessionStorage.getItem(ADOBE_TARGET_SESSION_ID_PARAM); + // eslint-disable-next-line no-console + console.debug(`Session id: ${sessionId}`); + if (!sessionId) { + sessionId = generateSessionID(); + // eslint-disable-next-line no-console + console.debug(`Generated new session id: ${sessionId}`); + sessionStorage.setItem(ADOBE_TARGET_SESSION_ID_PARAM, sessionId); + } + return sessionId; +} + +/** + * Fetch the target offers for the current location. + * @returns {Promise} + */ +async function fetchChallengerPageUrl(tenant, targetLocation) { + // eslint-disable-next-line no-console + console.debug(`Fetching target offers for location: ${targetLocation}`); + const res = await fetch(`https://${tenant}.tt.omtrdc.net/rest/v1/delivery?client=${tenant}&sessionId=${getOrCreateSessionId()}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + context: { + channel: 'web', + }, + execute: { + pageLoad: {}, + mboxes: [ + { + name: targetLocation, + index: 0, + }, + ], + }, + }), + }); + + const payload = await res.json(); + const mbox = payload.execute.mboxes.find((m) => m.name === targetLocation); + const { url } = mbox?.options[0].content ?? { url: null }; + if (!url) { + // eslint-disable-next-line no-console + console.error('No challenger url found'); + throw new Error('No challenger url found'); + } + + // eslint-disable-next-line no-console + console.debug(`Resolved challenger url: ${url}`); + return url; +} + +/** + * Replace the current page with the challenger page. + * @param url The challenger page url. + * @returns {Promise} + */ +async function navigateToChallengerPage(url) { + const plainPath = getPlainPageUrl(url); + + // eslint-disable-next-line no-console + console.debug(`Navigating to challenger page: ${plainPath}`); + + const resp = await fetch(plainPath); + if (!resp.ok) { + throw new Error(`Failed to fetch challenger page: ${resp.status}`); + } + + const mainElement = document.querySelector('main'); + if (!mainElement) { + throw new Error('Main element not found'); + } + + mainElement.innerHTML = await resp.text(); +} + +// eslint-disable-next-line import/prefer-default-export +export async function runTargetExperiment(clientId) { + try { + const experimentId = getMetadata('target-experiment'); + const targetLocation = getMetadata('target-experiment-location'); + if (!experimentId || !targetLocation) { + // eslint-disable-next-line no-console + console.log('Experiment id or target location not found'); + return null; + } + + // eslint-disable-next-line no-console + console.debug(`Running Target experiment ${experimentId} at location ${targetLocation}`); + + const pageUrl = await fetchChallengerPageUrl(clientId, targetLocation); + // eslint-disable-next-line no-console + console.debug(`Challenger page url: ${pageUrl}`); + + await navigateToChallengerPage(pageUrl); + + sampleRUM('target-experiment', { + source: `target:${experimentId}`, + target: pageUrl, + }); + + return { + experimentId, + experimentVariant: pageUrl, + }; + } catch (e) { + // eslint-disable-next-line no-console + console.error('Error running target experiment:', e); + return null; + } +}