Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 13 additions & 1 deletion packages/analytics-browser/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ import { parseLegacyCookies } from './cookie-migration';
import { DEFAULT_IDENTITY_STORAGE, DEFAULT_SERVER_ZONE } from './constants';
import { AmplitudeBrowser } from './browser-client';
import { VERSION } from './version';
import { getDomain } from './attribution/helpers';
import { getDomain, isSubdomainOf } from './attribution/helpers';

const TLD_CACHE_KEY = 'AMP_TLD';
const tldCache = new SessionStorage<string>();

// Exported for testing purposes only. Do not expose to public interface.
export class BrowserConfig extends Config implements IBrowserConfig {
Expand Down Expand Up @@ -486,6 +489,14 @@ export const createTransport = (transport?: TransportTypeOrOptions) => {
};

export const getTopLevelDomain = async (url?: string, diagnosticsClient?: IDiagnosticsClient) => {
// see if the TLD is cached returun that and skip the cookie check
if (!url && typeof location !== 'undefined') {
const cachedTld = await tldCache.get(TLD_CACHE_KEY);
// also do a sanity check that the TLD is a subdomain of the hostname
if (cachedTld && isSubdomainOf(location.hostname, cachedTld)) {
return cachedTld;
}
}
if (
!(await new CookieStorage<number>(undefined, { diagnosticsClient }).isEnabled()) ||
(!url && (typeof location === 'undefined' || !location.hostname))
Expand All @@ -506,6 +517,7 @@ export const getTopLevelDomain = async (url?: string, diagnosticsClient?: IDiagn

// if the transaction succeeded, the domain is valid
if (result) {
!url && (await tldCache.set(TLD_CACHE_KEY, '.' + domain));
return '.' + domain;
}
} catch (e) {
Expand Down
44 changes: 44 additions & 0 deletions packages/analytics-browser/test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,50 @@ describe('config', () => {
BrowserUtils.CookieStorage.isDomainWritable = isDomainWritableBefore;
}
});

describe('tldCache', () => {
/* eslint-disable-next-line @typescript-eslint/unbound-method */
const isDomainWritableBefore = BrowserUtils.CookieStorage.isDomainWritable;
const originalLocation = window.location;
beforeAll(() => {
// mock isDomainWritable to return true
BrowserUtils.CookieStorage.isDomainWritable = jest.fn().mockImplementation((domain: string) => {
if (domain === 'co.uk') return Promise.resolve(false);
if (domain === 'example.co.uk') return Promise.resolve(true);
return Promise.resolve(false);
});
Object.defineProperty(window, 'location', {
value: {
hostname: 'example.co.uk',
},
configurable: true,
});
});

afterAll(() => {
BrowserUtils.CookieStorage.isDomainWritable = isDomainWritableBefore;
Object.defineProperty(window, 'location', {
value: originalLocation,
configurable: true,
});
});

test('should return cached TLD when available', async () => {
const tld1 = await Config.getTopLevelDomain();
expect(sessionStorage.getItem('AMP_TLD')).toBe('".example.co.uk"');
const tld2 = await Config.getTopLevelDomain();
expect(tld1).toBe('.example.co.uk');
expect(tld2).toBe('.example.co.uk');
});

test('should not return cached TLD if it is not a subdomain of the hostname', async () => {
const tld1 = await Config.getTopLevelDomain();
sessionStorage.setItem('AMP_TLD', 'CORRUPTED_VALUE');
const tld2 = await Config.getTopLevelDomain();
expect(tld1).toBe('.example.co.uk');
expect(tld2).toBe('.example.co.uk');
});
});
});

describe('fetchRemoteConfig', () => {
Expand Down
Loading