Skip to content

Instantly share code, notes, and snippets.

@yairEO
Created August 28, 2023 20:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yairEO/89d0b42b67e708832a878f259eecb172 to your computer and use it in GitHub Desktop.
Save yairEO/89d0b42b67e708832a878f259eecb172 to your computer and use it in GitHub Desktop.
browserStorage.js - better reading/writings to localstorage
import {isString} from 'utility/types';
export const VERSION = 1; // current version of persisted data. if code change breaks persisted data, verison number should be bumped.
export const STORAGE_KEY_PREFIX = 'amdocs/catalog';
/**
* checks arguments are as expected
* @param {string} keyName
* @param {string} type
* @returns boolean
*/
const isValid = (keyName, type) => {
if (!window[`${type}Storage`]) {
console.warn('wrong storage type: ', type);
return false;
}
if (!keyName || !isString(keyName)) {
console.warn('missing keyName or invalid type');
return false;
}
return true;
};
/**
*
* @param {string} keyName
* @returns full storage path name, conststing of pre-defined, base path prefix and a custom dynamic path suffix
*/
export const getPathString = (keyName, version) => `${STORAGE_KEY_PREFIX}/${keyName}/v${version || VERSION}`;
/**
* extracts the storage path's version, which is at the end of the key
* @param {string} key
* @returns number
*/
const getKeyVersion = key => +key.split('/v').pop();
export function removeBrowserStorage(keyName, {type = 'local', version = VERSION} = {}) {
if (!isValid(keyName, type)) return;
window[`${type}Storage`].removeItem(getPathString(keyName, version));
}
export function getBrowserStorage(keyName, {type = 'local'} = {}) {
if (!isValid(keyName, type)) return;
let value;
Object.keys(window[`${type}Storage`])
.filter(key => key.includes('/' + keyName))
.forEach(key => {
const keyVersion = getKeyVersion(key);
if (keyVersion === VERSION) {
try { value = JSON.parse(window[`${type}Storage`][key]); }
catch (err){ console.warn(err); }
}
// remove any previous version(s)
else
removeBrowserStorage(keyName, {version: keyVersion});
});
return value;
}
/**
* Sets a browser local/session storage key/value
* @param {string} keyName storage key name which gets concatenated to the base STORAGE_KEY_PREFIX
* @param {*} data any type of data which can be represented as string
* @param {string} type local/session storage type
* @returns
*/
export function setBrowserStorage(keyName, data, {type = 'local'} = {}) {
if (!isValid(keyName, type)) return;
if (!data) {
console.warn('setBrowserStorage: no data (forgot?)');
return false;
}
window[`${type}Storage`].setItem(getPathString(keyName), JSON.stringify(data));
// go over all existing storage keys and find the one matching the keyName
const foundStorageKey = Object.keys(window[`${type}Storage`]).find(key => key.includes('/' + keyName));
// gets the key's version, which is suffixed at the end
const storageKeyVersion = getKeyVersion(foundStorageKey);
// if found key has a deprecated version, remove that key
if (storageKeyVersion !== VERSION)
removeBrowserStorage(keyName, {version: storageKeyVersion});
window[`${type}Storage`].setItem(getPathString(keyName), JSON.stringify(data));
}
import sinon from 'sinon';
import {expect} from 'chai';
import {getPathString, getBrowserStorage, setBrowserStorage} from './browserStorage';
describe('Browser storage', () => {
const sandbox = sinon.createSandbox();
const data = {name: 'John Doe', age: 30};
afterEach(() => {
sandbox.restore();
});
describe('setBrowserStorage', () => {
it('should call window.localStorage.setItem with the correct arguments', () => {
const setItemStub = sandbox.stub(window.localStorage, 'setItem');
setBrowserStorage('user', data);
expect(setItemStub.calledOnceWith(getPathString('user'), JSON.stringify(data))).to.be.true;
});
it('should not call window.localStorage.setItem when keyName is not a string', () => {
const setItemStub = sandbox.stub(window.localStorage, 'setItem');
setBrowserStorage(null, data);
expect(setItemStub.notCalled).to.be.true;
});
it('should not call window.localStorage.setItem when type is not "local" or "session"', () => {
const setItemStub = sandbox.stub(window.localStorage, 'setItem');
setBrowserStorage('user', data, {type: 'invalid'});
expect(setItemStub.notCalled).to.be.true;
});
});
describe('getBrowserStorage', () => {
it('should call window.localStorage.getItem with the correct arguments', () => {
const getItemStub = sandbox.stub(window.localStorage, 'getItem');
const path = getPathString('user');
getBrowserStorage('user');
expect(getItemStub.calledWith(path)).to.be.true;
});
it('should not call window.localStorage.getItem when keyName is not a string', () => {
const getItemStub = sandbox.stub(window.localStorage, 'getItem');
expect(getBrowserStorage(null)).to.be.undefined;
expect(getItemStub.notCalled).to.be.true;
});
it('should not call window.localStorage.getItem when type is not "local" or "session"', () => {
const getItemStub = sandbox.stub(window.localStorage, 'getItem');
expect(getBrowserStorage('user', {type: 'invalid'})).to.be.undefined;
expect(getItemStub.notCalled).to.be.true;
});
});
});
export * from './browserStorage';
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment