Skip to content

Commit

Permalink
Central georouting support (#2531)
Browse files Browse the repository at this point in the history
* Adding central georouting support from federal repo

* Unit test case Update

* Unit test case Update

* Remove console

* Creating a helper file for feds utils function

* Removing blog from default allowed list

* Updating georouting fetch calls

* Lint fix

* Test case update

* Renaming fedshelper file

* Updating test file
  • Loading branch information
bandana147 authored Jul 10, 2024
1 parent 7698b38 commit 5b64cf1
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 72 deletions.
32 changes: 1 addition & 31 deletions libs/blocks/global-navigation/utilities/utilities.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import {
getConfig, getMetadata, loadStyle, loadLana, decorateLinks, localizeLink,
} from '../../../utils/utils.js';
import { getFederatedContentRoot } from '../../../utils/federated.js';
import { processTrackingLabels } from '../../../martech/attributes.js';
import { replaceText } from '../../../features/placeholders.js';

loadLana();

const FEDERAL_PATH_KEY = 'federal';

// TODO when porting this to milo core, we should define this on config level
// and allow consumers to add their own origins
const allowedOrigins = [
'https://www.adobe.com',
'https://business.adobe.com',
'https://blog.adobe.com',
'https://milo.adobe.com',
'https://news.adobe.com',
];

export const selectors = {
globalNav: '.global-navigation',
curtain: '.feds-curtain',
Expand Down Expand Up @@ -84,27 +75,6 @@ export function toFragment(htmlStrings, ...values) {
return fragment;
}

// TODO we might eventually want to move this to the milo core utilities
let federatedContentRoot;
export const getFederatedContentRoot = () => {
if (federatedContentRoot) return federatedContentRoot;

const { origin } = window.location;

federatedContentRoot = allowedOrigins.some((o) => origin.replace('.stage', '') === o)
? origin
: 'https://www.adobe.com';

if (origin.includes('localhost') || origin.includes('.hlx.')) {
// Akamai as proxy to avoid 401s, given AEM-EDS MS auth cross project limitations
federatedContentRoot = origin.includes('.hlx.live')
? 'https://main--federal--adobecom.hlx.live'
: 'https://www.stage.adobe.com';
}

return federatedContentRoot;
};

// TODO we should match the akamai patterns /locale/federal/ at the start of the url
// and make the check more strict.
export const getFederatedUrl = (url = '') => {
Expand Down
12 changes: 4 additions & 8 deletions libs/features/georouting/georouting.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,24 +129,20 @@ async function showModal(details) {
return getModal(null, { class: 'locale-modal', id: 'locale-modal', content: details, closeEvent: 'closeModal' });
}

export default async function loadGeoRouting(config, createTag, getMetadata) {
export default async function loadGeoRouting(config, createTag, getMetadata, geoDetails = {}) {
const { locale } = config;

const urlLocale = locale.prefix.replace('/', '');
const storedInter = sessionStorage.getItem('international') || getCookie('international');
const storedLocale = storedInter === 'us' ? '' : storedInter;

const resp = await fetch(`${config.contentRoot ?? ''}/georouting.json`);
if (!resp.ok) return;
const json = await resp.json();

const urlGeoData = json.data.find((d) => d.prefix === urlLocale);
const urlGeoData = geoDetails.data?.find((d) => d.prefix === urlLocale);
if (!urlGeoData) return;

if (storedLocale || storedLocale === '') {
// Show modal when url and cookie disagree
if (urlLocale.split('_')[0] !== storedLocale.split('_')[0]) {
const localeMatches = json.data.filter((d) => d.prefix === storedLocale);
const localeMatches = geoDetails.data?.filter((d) => d.prefix === storedLocale);
const details = await getDetails(urlGeoData, localeMatches, config, createTag, getMetadata);
if (details) { await showModal(details); }
}
Expand All @@ -156,7 +152,7 @@ export default async function loadGeoRouting(config, createTag, getMetadata) {
// Show modal when derived countries from url locale and akamai disagree
const akamaiCode = await getAkamaiCode();
if (akamaiCode && !getCodes(urlGeoData).includes(akamaiCode)) {
const localeMatches = getMatches(json.data, akamaiCode);
const localeMatches = getMatches(geoDetails.data, akamaiCode);
const details = await getDetails(urlGeoData, localeMatches, config, createTag, getMetadata);
if (details) { await showModal(details); }
}
Expand Down
26 changes: 19 additions & 7 deletions libs/features/georoutingv2/georoutingv2.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getFederatedContentRoot } from '../../utils/federated.js';

let config;
let createTag;
let getMetadata;
Expand Down Expand Up @@ -281,15 +283,25 @@ export default async function loadGeoRouting(
loadBlock = loadBlockFunc;
loadStyle = loadStyleFunc;

const resp = await fetch(`${config.contentRoot ?? ''}/georoutingv2.json`);
if (!resp.ok) {
// eslint-disable-next-line import/no-cycle
const { default: loadGeoRoutingOld } = await import('../georouting/georouting.js');
loadGeoRoutingOld(config, createTag, getMetadata);
return;
const urls = [
`${config.contentRoot ?? ''}/georoutingv2.json`,
`${config.contentRoot ?? ''}/georouting.json`,
`${getFederatedContentRoot()}/federal/georouting/georoutingv2.json`,
];
let resp;
for (const url of urls) {
resp = await fetch(url);
if (resp.ok) {
if (url.includes('georouting.json')) {
const json = await resp.json();
// eslint-disable-next-line import/no-cycle
const { default: loadGeoRoutingOld } = await import('../georouting/georouting.js');
loadGeoRoutingOld(config, createTag, getMetadata, json);
}
break;
}
}
const json = await resp.json();

const { locale } = config;

const urlLocale = locale.prefix.replace('/', '');
Expand Down
30 changes: 30 additions & 0 deletions libs/utils/federated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getConfig } from './utils.js';

let federatedContentRoot;
/* eslint-disable import/prefer-default-export */
export const getFederatedContentRoot = () => {
const cdnWhitelistedOrigins = [
'https://www.adobe.com',
'https://business.adobe.com',
'https://blog.adobe.com',
'https://milo.adobe.com',
'https://news.adobe.com',
];
const { allowedOrigins = [] } = getConfig();
if (federatedContentRoot) return federatedContentRoot;

const { origin } = window.location;

federatedContentRoot = [...allowedOrigins, ...cdnWhitelistedOrigins].some((o) => origin.replace('.stage', '') === o)
? origin
: 'https://www.adobe.com';

if (origin.includes('localhost') || origin.includes('.hlx.')) {
// Akamai as proxy to avoid 401s, given AEM-EDS MS auth cross project limitations
federatedContentRoot = origin.includes('.hlx.live')
? 'https://main--federal--adobecom.hlx.live'
: 'https://www.stage.adobe.com';
}

return federatedContentRoot;
};
10 changes: 0 additions & 10 deletions test/blocks/global-navigation/utilities/utilities.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
toFragment,
getFedsPlaceholderConfig,
federatePictureSources,
getFederatedContentRoot,
getAnalyticsValue,
decorateCta,
hasActiveLink,
Expand Down Expand Up @@ -49,15 +48,6 @@ describe('global navigation utilities', () => {
expect(fragment2.tagName).to.equal('SPAN');
});

// No tests for using the the live url and .hlx. urls
// as mocking window.location.origin is not possible
describe('getFedsContentRoot', () => {
it('should return content source for localhost', () => {
const contentSource = getFederatedContentRoot();
expect(contentSource).to.equal(baseHost);
});
});

describe('federatePictureSources', () => {
// The test scenarios tests decorated or non decorated links.
// https://adobe.com/media.png
Expand Down
29 changes: 15 additions & 14 deletions test/features/georouting/georouting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { stub } from 'sinon';
import { expect } from '@esm-bundle/chai';

const { default: init, getCookie } = await import('../../../libs/features/georouting/georouting.js');
let { createTag, getMetadata } = await import('../../../libs/utils/utils.js');
let { getMetadata } = await import('../../../libs/utils/utils.js');
const { createTag } = await import('../../../libs/utils/utils.js');

const mockConfig = {
locales: {
Expand Down Expand Up @@ -136,7 +137,7 @@ describe('GeoRouting', () => {

it('Does create a modal if detected country from IP is CH and url prefix is US', async () => {
// prepare
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -146,7 +147,7 @@ describe('GeoRouting', () => {
// prepare
setUserCountryFromIP('US');
sessionStorage.setItem('international', 'us');
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.be.null;
Expand All @@ -157,7 +158,7 @@ describe('GeoRouting', () => {
it('If aiming for CH page and IP in Switzerland no modal is shown', async () => {
// prepare
mockConfig.locale.prefix = 'ch_de';
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.be.null;
Expand All @@ -167,7 +168,7 @@ describe('GeoRouting', () => {

it('If aiming for US page but IP in Switzerland shows CH links and US continue', async () => {
// prepare
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -192,7 +193,7 @@ describe('GeoRouting', () => {
mockConfig.locale.prefix = 'ch_fr';
setUserCountryFromIP('US');
sessionStorage.setItem('international', 'de');
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -215,7 +216,7 @@ describe('GeoRouting', () => {
// prepare
mockConfig.locale.prefix = 'mena_en';
setUserCountryFromIP('BW');
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -239,7 +240,7 @@ describe('GeoRouting', () => {
mockConfig.locale.prefix = 'mena_en';
setUserCountryFromIP('BW');
document.cookie = 'international=ch_de;path=/';
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -262,7 +263,7 @@ describe('GeoRouting', () => {
// prepare
mockConfig.locale.prefix = 'ch_de';
sessionStorage.setItem('international', 'ch_fr');
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
mockConfig.locale.prefix = '';
Expand All @@ -272,7 +273,7 @@ describe('GeoRouting', () => {
it('If IP is from an unknown country no modal is show', async () => {
// prepare
setUserCountryFromIP('NOEXIST');
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.be.null;
Expand All @@ -284,7 +285,7 @@ describe('GeoRouting', () => {
// prepare
stubFallbackMetadata('off');
stubHeadRequestToReturnVal('/ch_it', false);
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -308,7 +309,7 @@ describe('GeoRouting', () => {
it('Will show fallback links if fallbackrouting is on and page exist request fails', async () => {
// prepare
stubHeadRequestToReturnVal('/ch_it', false);
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.not.be.null;
Expand All @@ -328,7 +329,7 @@ describe('GeoRouting', () => {
stubHeadRequestToReturnVal('/ch_de', false);
stubHeadRequestToReturnVal('/ch_it', false);
stubHeadRequestToReturnVal('/ch_fr', false);
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
// assert
expect(modal).to.be.null;
Expand All @@ -341,7 +342,7 @@ describe('GeoRouting', () => {

it('Sets international and georouting_presented cookies on link click in modal', async () => {
// prepare
await init(mockConfig, createTag, getMetadata);
await init(mockConfig, createTag, getMetadata, mockGeoroutingJson);
const modal = document.querySelector('.dialog-modal');
const cookie = getCookie('international');
const storage = sessionStorage.getItem('international');
Expand Down
52 changes: 50 additions & 2 deletions test/features/georoutingv2/georoutingv2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { setViewport } from '@web/test-runner-commands';

const { default: init, getCookie } = await import('../../../libs/features/georoutingv2/georoutingv2.js');
let { getMetadata } = await import('../../../libs/utils/utils.js');
const { getFederatedContentRoot } = await import('../../../libs/utils/federated.js');
const { createTag, loadStyle, loadBlock, setConfig } = await import('../../../libs/utils/utils.js');

const mockConfig = {
Expand Down Expand Up @@ -212,8 +213,38 @@ function stubHeadRequestToReturnVal(prefix, val) {
);
}

const stubFetchForGeorouting = () => {
const stubFetchForGeorouting = (val) => {
window.fetch.withArgs('/georoutingv2.json').returns(
new Promise((resolve) => {
resolve({
ok: val,
json: () => mockGeoroutingJson,
});
}),
);
mockGeoroutingJson.georouting.data.forEach((d) => {
const prefix = d.prefix ? `/${d.prefix}` : '';
stubHeadRequestToReturnVal(prefix, true);
});
};

const stubFetchForGeoroutingOld = (val) => {
window.fetch.withArgs('/georouting.json').returns(
new Promise((resolve) => {
resolve({
ok: val,
json: () => mockGeoroutingJson,
});
}),
);
mockGeoroutingJson.georouting.data.forEach((d) => {
const prefix = d.prefix ? `/${d.prefix}` : '';
stubHeadRequestToReturnVal(prefix, true);
});
};

const stubFetchForFederalGeorouting = () => {
window.fetch.withArgs(`${getFederatedContentRoot()}/federal/georouting/georoutingv2.json`).returns(
new Promise((resolve) => {
resolve({
ok: true,
Expand All @@ -240,7 +271,7 @@ const closeModal = () => {
describe('GeoRouting', () => {
before(() => {
setUserCountryFromIP();
stubFetchForGeorouting();
stubFetchForGeorouting(true);
setGeorouting();
});
after(() => {
Expand Down Expand Up @@ -597,4 +628,21 @@ describe('GeoRouting', () => {
// cleanup
setGeorouting('on');
});

it('Will load georouting even if georoutingv2 and georouting file is not found', async () => {
stubFetchForGeorouting(false);
stubFetchForGeoroutingOld(false);
stubFetchForFederalGeorouting();
await init(mockConfig, createTag, getMetadata, loadBlock, loadStyle);
const modal = document.querySelector('.dialog-modal');
expect(modal).to.not.be.null;
});

it('Will load old georouting modal if georoutingv2 is not found', async () => {
stubFetchForGeorouting(false);
stubFetchForGeoroutingOld(true);
await init(mockConfig, createTag, getMetadata, loadBlock, loadStyle);
const modal = document.querySelector('.dialog-modal');
expect(modal).to.not.be.null;
});
});

0 comments on commit 5b64cf1

Please sign in to comment.