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

Fix Purge

parent 99a8d1dc
No related branches found
No related tags found
2 merge requests!60Revert "Fail a test to be sure it works",!25Fix Purge
...@@ -11,6 +11,9 @@ package storage ...@@ -11,6 +11,9 @@ package storage
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json"
jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/xxdk-wasm/utils"
"os" "os"
"strings" "strings"
"syscall/js" "syscall/js"
...@@ -26,6 +29,11 @@ const localStorageWasmPrefix = "xxdkWasmStorage/" ...@@ -26,6 +29,11 @@ const localStorageWasmPrefix = "xxdkWasmStorage/"
type LocalStorage struct { type LocalStorage struct {
// The Javascript value containing the localStorage object // The Javascript value containing the localStorage object
v js.Value 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. // jsStorage is the global that stores Javascript as window.localStorage.
...@@ -34,11 +42,19 @@ type LocalStorage struct { ...@@ -34,11 +42,19 @@ type LocalStorage struct {
// https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev // https://html.spec.whatwg.org/multipage/webstorage.html#dom-localstorage-dev
// - Documentation: // - Documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage // 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. // GetLocalStorage returns Javascript's local storage.
func GetLocalStorage() *LocalStorage { func GetLocalStorage() *LocalStorage {
return &jsStorage return jsStorage
} }
// GetItem returns a key's value from the local storage given its name. Returns // GetItem returns a key's value from the local storage given its name. Returns
...@@ -50,7 +66,7 @@ func GetLocalStorage() *LocalStorage { ...@@ -50,7 +66,7 @@ func GetLocalStorage() *LocalStorage {
// - Documentation: // - Documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem // https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem
func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) { func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
keyValue := ls.getItem(localStorageWasmPrefix + keyName) keyValue := ls.getItem(ls.prefix + keyName)
if keyValue.IsNull() { if keyValue.IsNull() {
return nil, os.ErrNotExist return nil, os.ErrNotExist
} }
...@@ -72,7 +88,7 @@ func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) { ...@@ -72,7 +88,7 @@ func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
// https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem // https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem
func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) { func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
encodedKeyValue := base64.StdEncoding.EncodeToString(keyValue) 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 // 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) { ...@@ -84,7 +100,7 @@ func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
// - Documentation: // - Documentation:
// https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem // https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
func (ls *LocalStorage) RemoveItem(keyName string) { 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 // Clear clears all the keys in storage. Underneath, it calls
...@@ -101,14 +117,14 @@ func (ls *LocalStorage) Clear() { ...@@ -101,14 +117,14 @@ func (ls *LocalStorage) Clear() {
// ClearPrefix clears all keys with the given prefix. // ClearPrefix clears all keys with the given prefix.
func (ls *LocalStorage) ClearPrefix(prefix string) { func (ls *LocalStorage) ClearPrefix(prefix string) {
// Get a copy of all key names at once // 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 // Loop through each key
for i := 0; i < keys.Length(); i++ { for i := 0; i < keys.Length(); i++ {
if v := keys.Index(i); !v.IsNull() { 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) { if strings.HasPrefix(keyName, prefix) {
ls.RemoveItem(keyName) ls.removeItem(v.String())
} }
} }
} }
...@@ -117,14 +133,14 @@ func (ls *LocalStorage) ClearPrefix(prefix string) { ...@@ -117,14 +133,14 @@ func (ls *LocalStorage) ClearPrefix(prefix string) {
// ClearWASM clears all the keys in storage created by WASM. // ClearWASM clears all the keys in storage created by WASM.
func (ls *LocalStorage) ClearWASM() { func (ls *LocalStorage) ClearWASM() {
// Get a copy of all key names at once // 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 // Loop through each key
for i := 0; i < keys.Length(); i++ { for i := 0; i < keys.Length(); i++ {
if v := keys.Index(i); !v.IsNull() { if v := keys.Index(i); !v.IsNull() {
keyName := v.String() keyName := v.String()
if strings.HasPrefix(keyName, localStorageWasmPrefix) { if strings.HasPrefix(keyName, ls.prefix) {
ls.RemoveItem(strings.TrimPrefix(keyName, localStorageWasmPrefix)) ls.RemoveItem(strings.TrimPrefix(keyName, ls.prefix))
} }
} }
} }
...@@ -145,7 +161,21 @@ func (ls *LocalStorage) Key(n int) (string, error) { ...@@ -145,7 +161,21 @@ func (ls *LocalStorage) Key(n int) (string, error) {
return "", os.ErrNotExist 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 // 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 ...@@ -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) clear() { ls.v.Call("clear") }
func (ls *LocalStorage) key(n int) js.Value { return ls.v.Call("key", n) } 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) 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) { ...@@ -88,7 +88,8 @@ func TestLocalStorage_Clear(t *testing.T) {
// Tests that LocalStorage.ClearPrefix deletes only the keys with the given // Tests that LocalStorage.ClearPrefix deletes only the keys with the given
// prefix. // prefix.
func TestLocalStorage_ClearPrefix(t *testing.T) { func TestLocalStorage_ClearPrefix(t *testing.T) {
jsStorage.clear() s := newLocalStorage("")
s.clear()
prng := rand.New(rand.NewSource(11)) prng := rand.New(rand.NewSource(11))
var yesPrefix, noPrefix []string var yesPrefix, noPrefix []string
prefix := "keyNamePrefix/" prefix := "keyNamePrefix/"
...@@ -102,18 +103,18 @@ func TestLocalStorage_ClearPrefix(t *testing.T) { ...@@ -102,18 +103,18 @@ func TestLocalStorage_ClearPrefix(t *testing.T) {
noPrefix = append(noPrefix, keyName) 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 { 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) t.Errorf("Could not get keyName %q: %+v", keyName, err)
} }
} }
for _, keyName := range yesPrefix { for _, keyName := range yesPrefix {
keyValue, err := jsStorage.GetItem(keyName) keyValue, err := s.GetItem(keyName)
if err == nil || !errors.Is(err, os.ErrNotExist) { if err == nil || !errors.Is(err, os.ErrNotExist) {
t.Errorf("Found keyName %q: %q", keyName, keyValue) t.Errorf("Found keyName %q: %q", keyName, keyValue)
} }
...@@ -235,3 +236,25 @@ func TestLocalStorage_Length(t *testing.T) { ...@@ -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() { ...@@ -53,6 +53,8 @@ func DecrementNumClientsRunning() {
func Purge(_ js.Value, args []js.Value) interface{} { func Purge(_ js.Value, args []js.Value) interface{} {
storageDirectory := args[0].String() storageDirectory := args[0].String()
userPassword := args[1].String() userPassword := args[1].String()
// Clear all EKV from local storage
GetLocalStorage().ClearPrefix("speakeasyapp")
// Check the password // Check the password
if !verifyPassword(userPassword) { if !verifyPassword(userPassword) {
...@@ -61,11 +63,11 @@ func Purge(_ js.Value, args []js.Value) interface{} { ...@@ -61,11 +63,11 @@ func Purge(_ js.Value, args []js.Value) interface{} {
} }
// Verify all Cmix followers are stopped // Verify all Cmix followers are stopped
if n := atomic.LoadUint64(&numClientsRunning); n != 0 { // if n := atomic.LoadUint64(&numClientsRunning); n != 0 {
utils.Throw(utils.TypeError, errors.Errorf( // utils.Throw(utils.TypeError, errors.Errorf(
"%d cMix followers running; all need to be stopped", n)) // "%d cMix followers running; all need to be stopped", n))
return nil // return nil
} // }
// Get all indexedDb database names // Get all indexedDb database names
databaseList, err := GetIndexedDbList() databaseList, err := GetIndexedDbList()
......
...@@ -16,19 +16,23 @@ import ( ...@@ -16,19 +16,23 @@ import (
) )
var ( 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. // errors.
Error = js.Global().Get("Error") 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. // on the Javascript layer.
JSON = js.Global().Get("JSON") 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. // promises.
Promise = js.Global().Get("Promise") 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.
Uint8Array = js.Global().Get("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