versioned store implementation

This commit is contained in:
mrjvs 2021-10-24 23:10:47 +02:00
parent 7bd4b8a665
commit b6f27f6350
3 changed files with 168 additions and 0 deletions

View File

@ -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 (
<MovieProvider>
<div>
<p>Testing</p>
<button onClick={() => {
const data = store.get();
data.save();
}}>Click me</button>
</div>
<Router />
</MovieProvider>
);

146
src/lib/storage/base.js Normal file
View File

@ -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);
}
}
}

View File

@ -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()