Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 83 additions & 22 deletions libs/features/mep/addons/event.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,110 @@
import { isSignedOut, getConfig, getMepEnablement, loadIms } from '../../../utils/utils.js';
import { getCookie } from '../../../martech/helpers.js';
import { isSignedOut, getConfig, getMepEnablement, loadIms, getCookie } from '../../../utils/utils.js';

const defaultReturn = { isRegistered: false };

// Persist only what personalization reads; never the RF authToken/userKey.
const store = window.localStorage;
const TTL_REGISTERED = 24 * 60 * 60 * 1000;
const TTL_UNREGISTERED = 3 * 60 * 1000;
const cacheKey = (eventCode, userId) => `mep-event:${eventCode}:${userId}`;

// TODO(MWPW-199051): enable once VEAL confirms the redirect-cookie name/domain.
// When enabling, clearRegisteredFlag must set domain= to match — a host-only
// delete won't clear a .adobe.com cookie, leaving the flag stuck.
const REDIRECT_INVALIDATION_ENABLED = false;
const justRegistered = (eventCode) => REDIRECT_INVALIDATION_ENABLED
&& getCookie(`feds_${eventCode}_registeredByRedirect`) === 'true';
/* c8 ignore next 3 */
const clearRegisteredFlag = (eventCode) => {
document.cookie = `feds_${eventCode}_registeredByRedirect=; Max-Age=0; path=/`;
};

function readCache(eventCode, userId) {
try {
const raw = store.getItem(cacheKey(eventCode, userId));
if (!raw) return null;
const { isRegistered, inPersonAttendee, exp } = JSON.parse(raw);
if (!exp || Date.now() > exp) return null;
return { isRegistered, inPersonAttendee };
} catch {
return null;
}
}

function writeCache(eventCode, userId, data) {
try {
const ttl = data.isRegistered ? TTL_REGISTERED : TTL_UNREGISTERED;
store.setItem(cacheKey(eventCode, userId), JSON.stringify({
isRegistered: !!data.isRegistered,
inPersonAttendee: !!data.inPersonAttendee,
exp: Date.now() + ttl,
}));
} catch {
// storage unavailable — non-fatal
}
}

async function getUserId() {
if (isSignedOut() && !getMepEnablement('signedIn')) return false;
if (getMepEnablement('userId') !== 'undefined') return getMepEnablement('userId');
/* c8 ignore next 3 */
await loadIms();
const { userId } = await window.adobeIMS.getProfile();
return userId;
const signedInEnable = getMepEnablement('signedIn') || getMepEnablement('signedin');
if (isSignedOut() && !signedInEnable) return false;
const userIdEnable = getMepEnablement('userId') || getMepEnablement('userid');
if (userIdEnable && userIdEnable !== 'undefined') return userIdEnable;
/* c8 ignore start */
try {
await loadIms();
const { userId } = await window.adobeIMS.getProfile();
return userId;
} catch {
return false;
}
/* c8 ignore stop */
}
async function fetchFromRainfocus(eventId) {

async function fetchFromRainfocus(eventCode) {
const userId = await getUserId();
if (!userId) return {};
if (!userId) return defaultReturn;

/* c8 ignore next 3 */
if (justRegistered(eventCode)) {
store.removeItem(cacheKey(eventCode, userId));
clearRegisteredFlag(eventCode);
} else {
const cached = readCache(eventCode, userId);
if (cached) return cached;
}

const accessToken = window.adobeIMS.getAccessToken()?.token;
if (!accessToken) return {};
if (!accessToken) return defaultReturn;

const domainSuffix = getConfig()?.env?.name === 'prod' ? '' : '.stage';
const url = `https://www${domainSuffix}.adobe.com/events/api/rf-auth-seq-generic/${eventId}?user_id=${encodeURIComponent(userId)}`;
const url = `https://www${domainSuffix}.adobe.com/events/api/rf-auth-seq-generic/${eventCode}?user_id=${encodeURIComponent(userId)}`;
try {
const response = await fetch(url, {
method: 'GET',
headers: { Authorization: `Bearer ${accessToken}` },
credentials: 'same-origin',
});
if (response.ok) return response.json();
if (response.ok) {
// RF returns {} (not { isRegistered: false }) when not registered.
const data = { isRegistered: false, ...(await response.json()) };
writeCache(eventCode, userId, data);
return data;
}
window.lana?.log(`Unable to fetch from Rainfocus: ${response.statusText}`, {
tags: 'mep-event',
severity: 'error',
});
return {};
return defaultReturn;
} catch (e) {
window.lana?.log(`Unable to fetch from Rainfocus: ${e.toString()}`, {
tags: 'mep-event',
severity: 'error',
});
return {};
return defaultReturn;
}
}
export default async function init(eventId) {
if (eventId === true) return eventId;

const consentCookieValue = getCookie('OptanonConsent');
if (consentCookieValue?.includes('C0002:0')) return false;

const eventDetails = await fetchFromRainfocus(eventId);
return eventDetails?.isRegistered === true;
export default async function init(eventCode) {
if (eventCode === true) return { isRegistered: eventCode };
return fetchFromRainfocus(eventCode);
}
2 changes: 1 addition & 1 deletion libs/features/personalization/personalization.js
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ async function getPersonalizationVariant(
if (userEntitlements?.includes(name)) return true;
const { lob, event } = config.mep.promises;
if (lob && lob === name.split('lob-')[1]?.toLowerCase()) return true;
if (name === 'registered' && event) return true;
if (name === 'registered' && event?.isRegistered) return true;
return PERSONALIZATION_KEYS.includes(name) && PERSONALIZATION_TAGS[name]();
};

Expand Down
2 changes: 1 addition & 1 deletion libs/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2119,7 +2119,7 @@ export function enablePersonalizationV2() {
}

export function loadMepAddons() {
const mepAddons = ['lob', 'event-id'];
const mepAddons = ['lob', 'event-code'];
const promises = {};
mepAddons.forEach((addon) => {
const enablement = getMepEnablement(addon);
Expand Down
54 changes: 41 additions & 13 deletions test/features/mep/addons/event.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,34 +27,36 @@ const setMetadata = (name, content) => {
describe('event', () => {
beforeEach(() => {
document.head.innerHTML = '';
localStorage.clear();
setMetadata('signedIn', 'on');
setCookie('OptanonConsent', 'C0002:1');
window.adobeIMS = { getAccessToken: () => ({ token: '1234567890' }) };
});
afterEach(() => {
setCookie('feds_adobe-max-2025_registeredByRedirect', '; Max-Age=0');
});
it('should return true when sending true into init', async () => {
const event = await init(true);
expect(event).to.equal(true);
});
it('should return false when consent cookie is set to C0002:0', async () => {
setCookie('OptanonConsent', 'C0002:0');
const event = await init('adobe-max-2025');
expect(event).to.equal(false);
expect(event.isRegistered).to.equal(true);
});
it('should return false when the user is signed out', async () => {
document.head.innerHTML = '';
const event = await init('adobe-max-2025');
expect(event).to.equal(false);
expect(event.isRegistered).to.equal(false);
});
it('should return false when userId is set to off', async () => {
setMetadata('userId', 'off');
it('should read enablements from lowercase DA metadata', async () => {
document.head.innerHTML = '';
setMetadata('signedin', 'on');
setMetadata('userid', '1234567890');
setFetchResponse({ ok: true, isRegistered: true });
const event = await init('adobe-max-2025');
expect(event).to.equal(false);
expect(event.isRegistered).to.equal(true);
});
it('should return false when getAccessToken returns an empty object', async () => {
setMetadata('userId', '1234567890');
window.adobeIMS.getAccessToken = () => Promise.resolve({});
const event = await init('adobe-max-2025');
expect(event).to.equal(false);
expect(event.isRegistered).to.equal(false);
});
it('should return false when api returns false', async () => {
setMetadata('userId', '1234567890');
Expand All @@ -63,7 +65,7 @@ describe('event', () => {
isRegistered: false,
});
const event = await init('adobe-max-2025');
expect(event).to.equal(false);
expect(event.isRegistered).to.equal(false);
});
it('should return true when api returns true', async () => {
setMetadata('userId', '1234567890');
Expand All @@ -72,6 +74,32 @@ describe('event', () => {
isRegistered: true,
});
const event = await init('adobe-max-2025');
expect(event).to.equal(true);
expect(event.isRegistered).to.equal(true);
});
it('should serve a cached result without re-calling the API', async () => {
setMetadata('userId', '1234567890');
setFetchResponse({ ok: true, isRegistered: true });
const first = await init('adobe-max-2025');
expect(first.isRegistered).to.equal(true);
// A cache hit must ignore a now-changed API response.
setFetchResponse({ ok: true, isRegistered: false });
const second = await init('adobe-max-2025');
expect(second.isRegistered).to.equal(true);
});
it('should normalize an empty API response to isRegistered false', async () => {
setMetadata('userId', '1234567890');
setFetchResponse({});
const event = await init('adobe-max-2025');
expect(event.isRegistered).to.equal(false);
});
// Un-skip when REDIRECT_INVALIDATION_ENABLED flips on (MWPW-199051).
it.skip('should bypass the cache when the registration-redirect cookie is set', async () => {
setMetadata('userId', '1234567890');
setFetchResponse({ ok: true, isRegistered: false });
await init('adobe-max-2025');
setCookie('feds_adobe-max-2025_registeredByRedirect', 'true');
setFetchResponse({ ok: true, isRegistered: true });
const event = await init('adobe-max-2025');
expect(event.isRegistered).to.equal(true);
});
});
10 changes: 10 additions & 0 deletions test/utils/utils-mep.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,16 @@ describe('MEP Utils', () => {
console.log(promises);
expect(promises.lob).to.be.a('promise');
});
it('loads event from event-code metadata', async () => {
document.head.innerHTML = '<meta name="event-code" content="max2025">';
const promises = loadMepAddons();
expect(promises.event).to.be.a('promise');
});
it('does not load event from the legacy event-id metadata', async () => {
document.head.innerHTML = '<meta name="event-id" content="max2025">';
const promises = loadMepAddons();
expect(promises.event).to.be.undefined;
});
afterEach(() => {
document.head.innerHTML = '';
});
Expand Down
Loading