Skip to content
Snippets Groups Projects
Commit 610cd3a5 authored by Jono Wenger's avatar Jono Wenger
Browse files

Add Keys function to local storage and fix ClearPrefix

parent 99a8d1dc
No related branches found
No related tags found
1 merge request!25Fix Purge
This commit is part of merge request !25. Comments created here will be created in the context of that merge request.
......@@ -11,6 +11,9 @@ package storage
import (
"encoding/base64"
"encoding/json"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/xxdk-wasm/utils"
"os"
"strings"
"syscall/js"
......@@ -26,6 +29,11 @@ const localStorageWasmPrefix = "xxdkWasmStorage/"
type LocalStorage struct {
// The Javascript value containing the localStorage object
v js.Value
// The prefix appended to each key name. This is so that all keys created by
// this structure can be deleted without affecting other keys in local
// storage.
prefix string
}
// jsStorage is the global that stores Javascript as window.localStorage.
......@@ -34,11 +42,19 @@ type LocalStorage struct {
// https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev
// - Documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
var jsStorage = LocalStorage{js.Global().Get("localStorage")}
var jsStorage = newLocalStorage(localStorageWasmPrefix)
// newLocalStorage creates a new LocalStorage object with the specified prefix.
func newLocalStorage(prefix string) *LocalStorage {
return &LocalStorage{
v: js.Global().Get("localStorage"),
prefix: prefix,
}
}
// GetLocalStorage returns Javascript's local storage.
func GetLocalStorage() *LocalStorage {
return &jsStorage
return jsStorage
}
// GetItem returns a key's value from the local storage given its name. Returns
......@@ -50,7 +66,7 @@ func GetLocalStorage() *LocalStorage {
// - Documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem
func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
keyValue := ls.getItem(localStorageWasmPrefix + keyName)
keyValue := ls.getItem(ls.prefix + keyName)
if keyValue.IsNull() {
return nil, os.ErrNotExist
}
......@@ -72,7 +88,7 @@ func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
// https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem
func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
encodedKeyValue := base64.StdEncoding.EncodeToString(keyValue)
ls.setItem(localStorageWasmPrefix+keyName, encodedKeyValue)
ls.setItem(ls.prefix+keyName, encodedKeyValue)
}
// RemoveItem removes a key's value from local storage given its name. If there
......@@ -84,7 +100,7 @@ func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
// - Documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
func (ls *LocalStorage) RemoveItem(keyName string) {
ls.removeItem(localStorageWasmPrefix + keyName)
ls.removeItem(ls.prefix + keyName)
}
// Clear clears all the keys in storage. Underneath, it calls
......@@ -101,14 +117,14 @@ func (ls *LocalStorage) Clear() {
// ClearPrefix clears all keys with the given prefix.
func (ls *LocalStorage) ClearPrefix(prefix string) {
// Get a copy of all key names at once
keys := js.Global().Get("Object").Call("keys", ls.v)
keys := ls.keys()
// Loop through each key
for i := 0; i < keys.Length(); i++ {
if v := keys.Index(i); !v.IsNull() {
keyName := strings.TrimPrefix(v.String(), localStorageWasmPrefix)
keyName := strings.TrimPrefix(v.String(), ls.prefix)
if strings.HasPrefix(keyName, prefix) {
ls.RemoveItem(keyName)
ls.removeItem(v.String())
}
}
}
......@@ -117,14 +133,14 @@ func (ls *LocalStorage) ClearPrefix(prefix string) {
// ClearWASM clears all the keys in storage created by WASM.
func (ls *LocalStorage) ClearWASM() {
// Get a copy of all key names at once
keys := js.Global().Get("Object").Call("keys", ls.v)
keys := ls.keys()
// Loop through each key
for i := 0; i < keys.Length(); i++ {
if v := keys.Index(i); !v.IsNull() {
keyName := v.String()
if strings.HasPrefix(keyName, localStorageWasmPrefix) {
ls.RemoveItem(strings.TrimPrefix(keyName, localStorageWasmPrefix))
if strings.HasPrefix(keyName, ls.prefix) {
ls.RemoveItem(strings.TrimPrefix(keyName, ls.prefix))
}
}
}
......@@ -145,7 +161,21 @@ func (ls *LocalStorage) Key(n int) (string, error) {
return "", os.ErrNotExist
}
return strings.TrimPrefix(keyName.String(), localStorageWasmPrefix), nil
return strings.TrimPrefix(keyName.String(), ls.prefix), nil
}
// Keys returns a list of all key names in local storage.
func (ls *LocalStorage) Keys() []string {
keyNamesJson := utils.JSON.Call("stringify", ls.keys())
var keyNames []string
err := json.Unmarshal([]byte(keyNamesJson.String()), &keyNames)
if err != nil {
jww.FATAL.Panicf(
"Failed to JSON unmarshal localStorage key name list: %+v", err)
}
return keyNames
}
// Length returns the number of keys in localStorage. Underneath, it accesses
......@@ -166,3 +196,4 @@ func (ls *LocalStorage) removeItem(keyName string) { ls.v.Call("removeIte
func (ls *LocalStorage) clear() { ls.v.Call("clear") }
func (ls *LocalStorage) key(n int) js.Value { return ls.v.Call("key", n) }
func (ls *LocalStorage) length() js.Value { return ls.v.Get("length") }
func (ls *LocalStorage) keys() js.Value { return utils.Object.Call("keys", ls.v) }
......@@ -88,7 +88,8 @@ func TestLocalStorage_Clear(t *testing.T) {
// Tests that LocalStorage.ClearPrefix deletes only the keys with the given
// prefix.
func TestLocalStorage_ClearPrefix(t *testing.T) {
jsStorage.clear()
s := newLocalStorage("")
s.clear()
prng := rand.New(rand.NewSource(11))
var yesPrefix, noPrefix []string
prefix := "keyNamePrefix/"
......@@ -102,18 +103,18 @@ func TestLocalStorage_ClearPrefix(t *testing.T) {
noPrefix = append(noPrefix, keyName)
}
jsStorage.SetItem(keyName, []byte(strconv.Itoa(i)))
s.SetItem(keyName, []byte(strconv.Itoa(i)))
}
jsStorage.ClearPrefix(prefix)
s.ClearPrefix(prefix)
for _, keyName := range noPrefix {
if _, err := jsStorage.GetItem(keyName); err != nil {
if _, err := s.GetItem(keyName); err != nil {
t.Errorf("Could not get keyName %q: %+v", keyName, err)
}
}
for _, keyName := range yesPrefix {
keyValue, err := jsStorage.GetItem(keyName)
keyValue, err := s.GetItem(keyName)
if err == nil || !errors.Is(err, os.ErrNotExist) {
t.Errorf("Found keyName %q: %q", keyName, keyValue)
}
......@@ -235,3 +236,25 @@ func TestLocalStorage_Length(t *testing.T) {
}
}
}
// Tests that LocalStorage.Keys return a list that contains all the added keys.
func TestLocalStorage_Keys(t *testing.T) {
s := newLocalStorage("")
s.clear()
values := map[string][]byte{
"key1": []byte("key value"),
"key2": {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
"key3": {0, 49, 0, 0, 0, 38, 249, 93},
}
for keyName, keyValue := range values {
s.SetItem(keyName, keyValue)
}
keys := s.Keys()
for i, keyName := range keys {
if _, exists := values[keyName]; !exists {
t.Errorf("Key %q does not exist (%d).", keyName, i)
}
}
}
......@@ -53,6 +53,8 @@ func DecrementNumClientsRunning() {
func Purge(_ js.Value, args []js.Value) interface{} {
storageDirectory := args[0].String()
userPassword := args[1].String()
// Clear all EKV from local storage
GetLocalStorage().ClearPrefix("speakeasyapp")
// Check the password
if !verifyPassword(userPassword) {
......@@ -61,11 +63,11 @@ func Purge(_ js.Value, args []js.Value) interface{} {
}
// Verify all Cmix followers are stopped
if n := atomic.LoadUint64(&numClientsRunning); n != 0 {
utils.Throw(utils.TypeError, errors.Errorf(
"%d cMix followers running; all need to be stopped", n))
return nil
}
// if n := atomic.LoadUint64(&numClientsRunning); n != 0 {
// utils.Throw(utils.TypeError, errors.Errorf(
// "%d cMix followers running; all need to be stopped", n))
// return nil
// }
// Get all indexedDb database names
databaseList, err := GetIndexedDbList()
......
......@@ -16,19 +16,23 @@ import (
)
var (
// Error is the Javascript Error object. It used to create new Javascript
// Error is the Javascript Error type. It used to create new Javascript
// errors.
Error = js.Global().Get("Error")
// JSON is the Javascript JSON object. It is used to perform JSON operations
// JSON is the Javascript JSON type. It is used to perform JSON operations
// on the Javascript layer.
JSON = js.Global().Get("JSON")
// Promise is the Javascript Promise object. It is used to generate new
// Object is the Javascript Object type. It is used to perform Object
// operations on the Javascript layer.
Object = js.Global().Get("Object")
// Promise is the Javascript Promise type. It is used to generate new
// promises.
Promise = js.Global().Get("Promise")
// Uint8Array is the Javascript Uint8Array object. It is used to create new
// Uint8Array is the Javascript Uint8Array type. It is used to create new
// Uint8Array.
Uint8Array = js.Global().Get("Uint8Array")
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment