2023-03-14 13:18:55 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) HashiCorp, Inc.
|
2023-08-11 13:12:13 +00:00
|
|
|
* SPDX-License-Identifier: BUSL-1.1
|
2023-03-14 13:18:55 +00:00
|
|
|
*/
|
|
|
|
|
2022-10-18 15:40:47 +00:00
|
|
|
import Service from '@ember/service';
|
|
|
|
import { getOwner } from '@ember/application';
|
|
|
|
import ENV from 'consul-ui/config/environment';
|
|
|
|
|
|
|
|
export function storageFor(key) {
|
|
|
|
return function () {
|
|
|
|
return {
|
|
|
|
get() {
|
|
|
|
const owner = getOwner(this);
|
|
|
|
|
|
|
|
const localStorageService = owner.lookup('service:localStorage');
|
|
|
|
|
|
|
|
return localStorageService.getBucket(key);
|
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An in-memory stub of window.localStorage. Ideally this would
|
|
|
|
* implement the [Storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage)-interface that localStorage implements
|
|
|
|
* as well.
|
|
|
|
*
|
|
|
|
* We use this implementation during testing to not pollute `window.localStorage`
|
|
|
|
*/
|
|
|
|
class MemoryStorage {
|
|
|
|
constructor() {
|
|
|
|
this.data = new Map();
|
|
|
|
}
|
|
|
|
|
|
|
|
getItem(key) {
|
|
|
|
return this.data.get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
setItem(key, value) {
|
|
|
|
return this.data.set(key, value.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A function to seed data into MemoryStorage. This expects an object to be
|
|
|
|
* passed. The passed values will be persisted as a string - i.e. the values
|
|
|
|
* passed will call their `toString()`-method before writing to storage. You need
|
|
|
|
* to take this into account when you want to persist complex values, like arrays
|
|
|
|
* or objects:
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* const storage = new MemoryStorage();
|
|
|
|
* storage.seed({ notices: ['notice-a', 'notice-b']});
|
|
|
|
*
|
|
|
|
* storage.getItem('notices') // => 'notice-a,notice-b'
|
|
|
|
*
|
|
|
|
* // won't work
|
|
|
|
* storage.seed({
|
|
|
|
* user: { name: 'Tomster' }
|
|
|
|
* })
|
|
|
|
*
|
|
|
|
* storage.getItem('user') // => '[object Object]'
|
|
|
|
*
|
|
|
|
* // this works
|
|
|
|
* storage.seed({
|
|
|
|
* . user: JSON.stringify({name: 'Tomster'})
|
|
|
|
* })
|
|
|
|
*
|
|
|
|
* storage.getItem('user') // => '{ "name": "Tomster" }'
|
|
|
|
* ```
|
|
|
|
* @param {object} data - the data to seed
|
|
|
|
*/
|
|
|
|
seed(data) {
|
|
|
|
const newData = new Map();
|
|
|
|
|
|
|
|
const keys = Object.keys(data);
|
|
|
|
|
|
|
|
keys.forEach((key) => {
|
|
|
|
newData.set(key, data[key].toString());
|
|
|
|
});
|
|
|
|
|
|
|
|
this.data = newData;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* There might be better ways to do this but this is good enough for now.
|
|
|
|
* During testing we want to use MemoryStorage not window.localStorage.
|
|
|
|
*/
|
|
|
|
function initStorage() {
|
|
|
|
if (ENV.environment === 'test') {
|
|
|
|
return new MemoryStorage();
|
|
|
|
} else {
|
|
|
|
return window.localStorage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A service that wraps access to local-storage. We wrap
|
|
|
|
* local-storage to not pollute local-storage during testing.
|
|
|
|
*/
|
|
|
|
export default class LocalStorageService extends Service {
|
|
|
|
constructor() {
|
|
|
|
super(...arguments);
|
|
|
|
|
|
|
|
this.storage = initStorage();
|
|
|
|
this.buckets = new Map();
|
|
|
|
}
|
|
|
|
|
|
|
|
getBucket(key) {
|
|
|
|
const bucket = this.buckets.get(key);
|
|
|
|
|
|
|
|
if (bucket) {
|
|
|
|
return bucket;
|
|
|
|
} else {
|
|
|
|
return this._setupBucket(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_setupBucket(key) {
|
|
|
|
const owner = getOwner(this);
|
|
|
|
const Klass = owner.factoryFor(`storage:${key}`).class;
|
|
|
|
const storage = new Klass(key, this.storage);
|
|
|
|
|
|
|
|
this.buckets.set(key, storage);
|
|
|
|
|
|
|
|
return storage;
|
|
|
|
}
|
|
|
|
}
|