From b6f27f6350a9eb3b7db0bbbe9856ff5ee83a1496 Mon Sep 17 00:00:00 2001 From: mrjvs Date: Sun, 24 Oct 2021 23:10:47 +0200 Subject: [PATCH] versioned store implementation --- src/App.js | 8 ++ src/lib/storage/base.js | 146 +++++++++++++++++++++++++++++++++++++ src/lib/storage/watched.js | 14 ++++ 3 files changed, 168 insertions(+) create mode 100644 src/lib/storage/base.js create mode 100644 src/lib/storage/watched.js diff --git a/src/App.js b/src/App.js index 8e248937..91ca1a56 100644 --- a/src/App.js +++ b/src/App.js @@ -2,6 +2,7 @@ import { SearchView } from './views/Search'; import { MovieView } from './views/Movie'; import { useMovie, MovieProvider } from './hooks/useMovie'; import './index.css'; +import {store} from './lib/storage/watched.js' function Router() { const { streamData } = useMovie(); @@ -11,6 +12,13 @@ function Router() { function App() { return ( +
+

Testing

+ +
); diff --git a/src/lib/storage/base.js b/src/lib/storage/base.js new file mode 100644 index 00000000..4781ad67 --- /dev/null +++ b/src/lib/storage/base.js @@ -0,0 +1,146 @@ +function buildStoreObject(data) { + return { + versions: data.versions, + currentVersion: data.maxVersion, + id: data.storageString, + update(obj) { + if (!obj) + throw new Error("object to update is not an object"); + + // repeat until object fully updated + while (obj["--version"] !== this.currentVersion) { + // get version + let version = obj["--version"] || 0; + if (version === undefined || version.constructor !== Number) + version = -42; // invalid on purpose so it will reset + else { + version = (version+1).toString() + } + + // check if version exists + if (!this.versions[version]) { + console.error(`Version not found for storage item in store ${this.id}, resetting`); + obj = null; + break; + } + + // update object + obj = this.versions[version].update(obj); + console.log(obj); + } + + // if resulting obj is null, use latest version as init object + if (obj === null) { + console.error(`Storage item for store ${this.id} has been reset due to faulty updates`); + return this.versions[this.currentVersion.toString()].init(); + } + + // updates succesful, return + return obj; + }, + get() { + // get from storage api + const store = this; + let data = localStorage.getItem(this.id); + + // parse json if item exists + if (data) { + try { + data = JSON.parse(data); + if (!data.constructor) { + console.error(`Storage item for store ${this.id} has not constructor`) + throw new Error("storage item has no constructor") + } + if (data.constructor !== Object) { + console.error(`Storage item for store ${this.id} is not an object`) + throw new Error("storage item is not an object") + } + } catch (_) { + // if errored, set to null so it generates new one, see below + console.error(`Failed to parse storage item for store ${this.id}`) + data = null; + } + } + + // if item doesnt exist, generate from version init + if (!data) { + data = this.versions[this.currentVersion.toString()].init(); + } + + // update the data if needed + data = this.update(data); + + // add a save object to return value + data.save = function save() { + localStorage.setItem(store.id, JSON.stringify(data)); + } + + // return data + return data; + } + } +} + +/* + * Builds a versioned store + * + * manages versioning of localstorage items +*/ +export function versionedStoreBuilder() { + return { + _data: { + versionList: [], + maxVersion: 0, + versions: {}, + storageString: null, + }, + setKey(str) { + this._data.storageString = str; + return this; + }, + addVersion(num, updateFunc, initFunc) { + // update max version list + if (num > this._data.maxVersion) + this._data.maxVersion = num; + // add to version list + this._data.versionList.push(num); + + // register version + this._data.versions[num.toString()] = { + version: num, // version number + update: (data) => { // update function, and increment version + updateFunc(data); + data["--version"] = num; + return data; + }, + init: () => { // return an initial object + const data = initFunc(); + data["--version"] = num; + return data; + } + } + return this; + }, + build() { + // check if version list doesnt skip versions + const versionListSorted = this._data.versionList.sort((a,b)=>a-b); + versionListSorted.forEach((v, i, arr) => { + if (i === 0) + return; + if (v !== arr[i-1]+1) + throw new Error("Version list of store is not incremental"); + }) + + // version zero must exist + if (versionListSorted[0] !== 0) + throw new Error("Version 0 doesn't exist in version list of store"); + + // check storage string + if (!this._data.storageString) + throw new Error("storage key not set in store"); + + // build versioned store + return buildStoreObject(this._data); + } + } +} diff --git a/src/lib/storage/watched.js b/src/lib/storage/watched.js new file mode 100644 index 00000000..51869788 --- /dev/null +++ b/src/lib/storage/watched.js @@ -0,0 +1,14 @@ +import { versionedStoreBuilder } from './base.js'; + +export const store = versionedStoreBuilder() + .setKey('test-store') + .addVersion(0, (d) => { + d.v0 = "v0"; + }, () => ({ v0: "v0" })) + .addVersion(1, (d) => { + d.v1 = "v1"; + }, () => ({ v1: "v1" })) + .addVersion(2, (d) => { + d.v2 = "v2"; + }, () => ({ v2: "v2" })) + .build()