Skip to content

Commit

Permalink
Merge pull request #93 from IABTechLab/sas-uid2-3835-store-js-sdk-config
Browse files Browse the repository at this point in the history
store config when javascript sdk is initialized
  • Loading branch information
ashleysmithTTD authored Aug 9, 2024
2 parents 1e5e2fd + 0b3c9df commit 27a8a05
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 3 deletions.
93 changes: 93 additions & 0 deletions src/configManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { ProductDetails } from './product';
import { SdkOptions } from './sdkOptions';

type storedConfig = Pick<
SdkOptions,
'baseUrl' | 'useCookie' | 'refreshRetryPeriod' | 'cookiePath' | 'cookieDomain'
>;

const getConfigFromSdkOptions = (options: SdkOptions): storedConfig => {
const config: storedConfig = {
refreshRetryPeriod: options.refreshRetryPeriod,
baseUrl: options.baseUrl,
useCookie: options.useCookie,
cookiePath: options.cookiePath,
cookieDomain: options.cookieDomain,
};
return config;
};

export const storeConfig = (options: SdkOptions, productDetails: ProductDetails) => {
if (options.useCookie) {
setConfigCookie(options, productDetails);
} else {
setConfigToLocalStorage(options, productDetails);
}
};

export const loadConfig = (
options: SdkOptions,
productDetails: ProductDetails
): storedConfig | null => {
if (options.useCookie) {
return loadConfigFromCookie(productDetails);
} else {
return loadConfigFromLocalStorage(productDetails);
}
};

const setConfigCookie = (options: SdkOptions, productDetails: ProductDetails) => {
const cookieDomain = options.cookieDomain;
const path = options.cookiePath ?? '/';
const value = JSON.stringify(getConfigFromSdkOptions(options));
let cookie =
productDetails.cookieName + '_config' + '=' + encodeURIComponent(value) + ' ;path=' + path;
if (typeof cookieDomain !== 'undefined') {
cookie += ';domain=' + cookieDomain;
}
document.cookie = cookie;
};

const removeConfigCookie = (productDetails: ProductDetails) => {
document.cookie =
productDetails.cookieName + '_config' + '=;expires=Tue, 1 Jan 1980 23:59:59 GMT';
};

const getConfigCookie = (productDetails: ProductDetails) => {
const docCookie = document.cookie;
if (docCookie) {
const payload = docCookie
.split('; ')
.find((row) => row.startsWith(productDetails.cookieName + '_config' + '='));
if (payload) {
return decodeURIComponent(payload.split('=')[1]);
}
}
};

const loadConfigFromCookie = (productDetails: ProductDetails): storedConfig | null => {
const cookieData = getConfigCookie(productDetails);
if (cookieData) {
const result = JSON.parse(cookieData) as storedConfig;
return result;
}
return null;
};

const setConfigToLocalStorage = (options: SdkOptions, productDetails: ProductDetails) => {
const value = JSON.stringify(getConfigFromSdkOptions(options));
localStorage.setItem(productDetails.localStorageKey + '_config', value);
};

const removeConfigFromLocalStorage = (productDetails: ProductDetails) => {
localStorage.removeItem(productDetails.localStorageKey + '_config');
};

const loadConfigFromLocalStorage = (productDetails: ProductDetails): storedConfig | null => {
const data = localStorage.getItem(productDetails.localStorageKey + '_config');
if (data) {
const result = JSON.parse(data) as storedConfig;
return result;
}
return null;
};
61 changes: 60 additions & 1 deletion src/integrationTests/euidSdk.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';

import * as mocks from '../mocks';
import { sdkWindow, EUID, __euidInternalHandleScriptLoad } from '../euidSdk';
import { sdkWindow, EUID, __euidInternalHandleScriptLoad, SdkOptions } from '../euidSdk';
import { EventType, CallbackHandler } from '../callbackManager';
import { __euidSSProviderScriptLoad } from '../secureSignalEuid';
import { UidSecureSignalProvider } from '../secureSignal_shared';
Expand All @@ -18,9 +18,32 @@ getAdvertisingTokenMock = jest.fn<() => Promise<string>>();
const debugOutput = false;

mocks.setupFakeTime();
const mockDomain = 'www.uidapi.com';

const makeIdentity = mocks.makeIdentityV2;

const getConfigCookie = () => {
const docCookie = document.cookie;
if (docCookie) {
const payload = docCookie
.split('; ')
.find((row) => row.startsWith(EUID.COOKIE_NAME + '_config' + '='));
if (payload) {
return JSON.parse(decodeURIComponent(payload.split('=')[1]));
}
}
return null;
};

const getConfigStorage = () => {
const data = localStorage.getItem('UID2-sdk-identity_config');
if (data) {
const result = JSON.parse(data);
return result;
}
return null;
};

describe('when a callback is provided', () => {
beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -125,3 +148,39 @@ describe('when a callback is provided', () => {
});
});
});

describe('Store config EUID', () => {
const identity = makeIdentity();
const options: SdkOptions = {
baseUrl: 'http://test-host',
cookieDomain: mockDomain,
refreshRetryPeriod: 1000,
useCookie: false,
};

beforeEach(() => {
localStorage.removeItem('UID2-sdk-identity_config');
document.cookie = EUID.COOKIE_NAME + '_config' + '=;expires=Tue, 1 Jan 1980 23:59:59 GMT';
});

describe('when useCookie is true', () => {
test('should store config in cookie', () => {
euid.init({ callback: callback, identity: identity, ...options, useCookie: true });
const cookie = getConfigCookie();
expect(cookie).toBeInstanceOf(Object);
expect(cookie).toHaveProperty('cookieDomain');
const storageConfig = getConfigStorage();
expect(storageConfig).toBeNull();
});
});
describe('when useCookie is false', () => {
test('should store config in local storage', () => {
euid.init({ callback: callback, identity: identity, ...options });
const storageConfig = getConfigStorage();
expect(storageConfig).toBeInstanceOf(Object);
expect(storageConfig).toHaveProperty('cookieDomain');
const cookie = getConfigCookie();
expect(cookie).toBeNull();
});
});
});
60 changes: 59 additions & 1 deletion src/integrationTests/options.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { afterEach, beforeEach, describe, expect, jest, test } from '@jest/globals';

import * as mocks from '../mocks';
import { sdkWindow, UID2 } from '../uid2Sdk';
import { SdkOptions, sdkWindow, UID2 } from '../uid2Sdk';

let callback: any;
let uid2: UID2;
Expand Down Expand Up @@ -33,6 +33,28 @@ const getUid2LocalStorage = mocks.getUid2LocalStorage;
const removeUid2Cookie = mocks.removeUid2Cookie;
const removeUid2LocalStorage = mocks.removeUid2LocalStorage;

const getConfigCookie = () => {
const docCookie = document.cookie;
if (docCookie) {
const payload = docCookie
.split('; ')
.find((row) => row.startsWith(UID2.COOKIE_NAME + '_config' + '='));
if (payload) {
return JSON.parse(decodeURIComponent(payload.split('=')[1]));
}
}
return null;
};

const getConfigStorage = () => {
const data = localStorage.getItem('UID2-sdk-identity_config');
if (data) {
const result = JSON.parse(data);
return result;
}
return null;
};

describe('cookieDomain option', () => {
describe('when using default value', () => {
beforeEach(() => {
Expand Down Expand Up @@ -193,3 +215,39 @@ describe('useCookie option', () => {
});
});
});

describe('Store config UID2', () => {
const identity = makeIdentity();
const options: SdkOptions = {
baseUrl: 'http://test-host',
cookieDomain: mockDomain,
refreshRetryPeriod: 1000,
useCookie: false,
};

beforeEach(() => {
localStorage.removeItem('UID2-sdk-identity_config');
document.cookie = UID2.COOKIE_NAME + '_config' + '=;expires=Tue, 1 Jan 1980 23:59:59 GMT';
});

describe('when useCookie is true', () => {
test('should store config in cookie', () => {
uid2.init({ callback: callback, identity: identity, ...options, useCookie: true });
const cookie = getConfigCookie();
expect(cookie).toBeInstanceOf(Object);
expect(cookie).toHaveProperty('cookieDomain');
const storageConfig = getConfigStorage();
expect(storageConfig).toBeNull();
});
});
describe('when useCookie is false', () => {
test('should store config in local storage', () => {
uid2.init({ callback: callback, identity: identity, ...options });
const storageConfig = getConfigStorage();
expect(storageConfig).toBeInstanceOf(Object);
expect(storageConfig).toHaveProperty('cookieDomain');
const cookie = getConfigCookie();
expect(cookie).toBeNull();
});
});
});
3 changes: 3 additions & 0 deletions src/sdkBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { PromiseHandler } from './promiseHandler';
import { StorageManager } from './storageManager';
import { hashAndEncodeIdentifier } from './encoding/hash';
import { ProductDetails, ProductName } from './product';
import { storeConfig } from './configManager';

function hasExpired(expiry: number, now = Date.now()) {
return expiry <= now;
Expand Down Expand Up @@ -179,6 +180,8 @@ export abstract class SdkBase {
if (!isSDKOptionsOrThrow(opts))
throw new TypeError(`Options provided to ${this._product.name} init couldn't be validated.`);

storeConfig(opts, this._product);

this._opts = opts;
this._storageManager = new StorageManager(
{ ...opts },
Expand Down
2 changes: 1 addition & 1 deletion src/sdkOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Identity } from './Identity';
import { InitCallbackOptions } from './initCallbacks';

export type SdkOptions = BaseSdkOptions & InitCallbackOptions & CookieOptions & ApiClientOptions;
type BaseSdkOptions = {
export type BaseSdkOptions = {
refreshRetryPeriod?: number;
identity?: Identity;
useCookie?: boolean;
Expand Down

0 comments on commit 27a8a05

Please sign in to comment.