mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
* Remove Singleton from BaseStore to remove global shared state Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove more usages of Singleton Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace use of legacy global execHelm with injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove last use of legacy global execHelm Signed-off-by: Sebastian Malton <sebastian@malton.name> * Extract BaseStore deps into constructor argument Signed-off-by: Sebastian Malton <sebastian@malton.name> * Introduce method to make store migrations injectable - Use it for ClusterStore Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch HotbarStore to injectable migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch UserStore to injectable migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move migration utils into common/utils/ Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch WeblinkStore to injectable migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error in base-store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove tests that reference lastSeenVersion - That value is not used anywhere in code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove usage of legacy global .getInstance Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove usage of legacy global ClusterStore.getInstance Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add simple migrations dependency for stores without any preexisting migrations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix messed up import Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add typing to transient injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup formatting Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix typing in tests to satisfy requirement to have cacheFile Signed-off-by: Sebastian Malton <sebastian@malton.name> * More consistent use of BaseStore.displayName Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add catching of error while starting main application Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move initializing sentry to runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove unneeded appPathsInjectionToken - Only had once impl, which was in common anyway Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add support for multiple "runAfter" runnables - Needed so that several dependencies can be declared Signed-off-by: Sebastian Malton <sebastian@malton.name> * Use multiple runAfter support to fix crash on renderer Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove traces Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add global override to fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix base store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix runManyFor tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix hotbar store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix user store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add global override for getConfigurationFileModel to fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove overrides for configuration stores - Now that there is an override for getConfiguration Signed-off-by: Sebastian Malton <sebastian@malton.name> * Overhaul FS fakes with full in-memory filesystem - This increases our confidence in fs related logic Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove use of global shared Electron.App Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add fake access support Signed-off-by: Sebastian Malton <sebastian@malton.name> * Handle copy as part of fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add ensureDir/Sync support to fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * Use pathExistsSync instead of fsInjectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add createReadStream to fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add stat to fake FS Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test failures due to incomplete overrides Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully injectable-ize BaseStore so that ApplicationBuilder tests work Signed-off-by: Sebastian Malton <sebastian@malton.name> * Consolidate more bootstrapping into startFrame Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move initializing CatalogCategories to runnable in bootstrap Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert contextMenuOpen initializers into runnables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert navigateForExtension init to runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make cluster state sync fully injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move init hotbar store into runnables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make LensTheme fully injectable and runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup old code from missed from previous commit Signed-off-by: Sebastian Malton <sebastian@malton.name> * Manually split out terminal color names and fully type LensTheme Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix old imports Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove unnecessart awaits Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove dead code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully cherry pick injectablizing custom monaco themes Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix duplicate mock warning Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix incorrectly fully cherry picking new runnable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Complete cherry-pick of current cluster injcetablization Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix override file name Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix injecting before app paths are set up Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix injecting before app paths are set up Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix ordering of runnable and order of injection Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert all renderer runnables to late-inject style - To help fix issues around injection time Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix react-beautiful-dnd mocks Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update and fix WriteJson(Sync) to fix error in tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix HotbarStore.load being called twice is being buggy Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update listing-active-helm-repositories-in-preferences snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix sidebar-and-tab-navigation-tests - Move enabling extensions in tests to a proper location - Fix flushing promises Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove props from dnd mock to make snapshot diffs smaller Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix import Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests by overriding things that are no longer overriden by default - NOTE: They are overridden when using ApplicationBuilder Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix hotbar store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix cluster store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix extension-loader tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix extension-discovery tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix cluster-role-dialog tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix user store tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix kubeconfig sync tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix sidebar and tab tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove unused code Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix pick paths import type error and simplify signature Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error in legacy ipc registration Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove another use of legacy requestOpenPathPicker Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace use of legacy global PathPicker.Pick Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix usage in light of changed prop names Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix catalog tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix more type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test flakiness by removing side effects from userStore preferences Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix loading Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crash Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cherry pick updated startFrameInjectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add tests to verify runMany behaviour in new possible incorrect configuration Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix init ordering during start frame Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix cluster state sync Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots after removing side-effects Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add override for technical test Signed-off-by: Sebastian Malton <sebastian@malton.name> * Correctly mark currentlyInClusterFrame as causedSideEffects Signed-off-by: Sebastian Malton <sebastian@malton.name> * Better formatting Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix behaviour regression Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add better logging Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix BaseStore sync Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update last snapshot Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add global override for randomBytes Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make startMainApplication not an injection time side effect Signed-off-by: Sebastian Malton <sebastian@malton.name> * Choose better names for start-frame runnable tokens Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove duplication of code in RunManyFor Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add unit tests and fix handling empty runAfter array Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace use of mobx from runManyFor with custom barrier Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add missing test Signed-off-by: Sebastian Malton <sebastian@malton.name> Signed-off-by: Sebastian Malton <sebastian@malton.name>
185 lines
4.6 KiB
TypeScript
Executable File
185 lines
4.6 KiB
TypeScript
Executable File
/**
|
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
*/
|
|
|
|
// Helper for working with storages (e.g. window.localStorage, NodeJS/file-system, etc.)
|
|
import { action, comparer, computed, makeObservable, observable, observe, toJS, when } from "mobx";
|
|
import type { Draft } from "immer";
|
|
import { produce, isDraft } from "immer";
|
|
import { isEqual, isPlainObject } from "lodash";
|
|
import assert from "assert";
|
|
import type { Logger } from "../../common/logger";
|
|
|
|
export interface StorageChange<T> {
|
|
key: string;
|
|
value: T | undefined;
|
|
oldValue: T | undefined;
|
|
}
|
|
|
|
export interface StorageAdapter<T> {
|
|
[metadata: string]: unknown;
|
|
getItem(key: string): T | Promise<T>;
|
|
setItem(key: string, value: T): void;
|
|
removeItem(key: string): void;
|
|
onChange?(change: StorageChange<T>): void;
|
|
}
|
|
|
|
export interface StorageHelperOptions<T> {
|
|
readonly autoInit?: boolean; // start preloading data immediately, default: true
|
|
readonly storage: StorageAdapter<T>;
|
|
readonly defaultValue: T;
|
|
}
|
|
|
|
export interface StorageLayer<T> {
|
|
isDefaultValue(val: T): boolean;
|
|
get(): T;
|
|
readonly value: T;
|
|
readonly whenReady: Promise<void>;
|
|
set(value: T): void;
|
|
reset(): void;
|
|
merge(value: Partial<T> | ((draft: Draft<T>) => Partial<T> | void)): void;
|
|
}
|
|
|
|
export const storageHelperLogPrefix = "[STORAGE-HELPER]:";
|
|
|
|
interface Dependencies {
|
|
readonly logger: Logger;
|
|
}
|
|
|
|
export class StorageHelper<T> implements StorageLayer<T> {
|
|
readonly storage: StorageAdapter<T>;
|
|
|
|
private readonly data = observable.box<T | undefined>(undefined, {
|
|
deep: true,
|
|
equals: comparer.structural,
|
|
});
|
|
|
|
@observable initialized = false;
|
|
|
|
get whenReady() {
|
|
return when(() => this.initialized);
|
|
}
|
|
|
|
get defaultValue(): T {
|
|
// return as-is since options.defaultValue might be a getter too
|
|
return this.options.defaultValue;
|
|
}
|
|
|
|
constructor(private readonly dependencies: Dependencies, readonly key: string, private readonly options: StorageHelperOptions<T>) {
|
|
makeObservable(this);
|
|
|
|
const { storage, autoInit = true } = options;
|
|
|
|
this.storage = storage;
|
|
|
|
observe(this.data, (change) => {
|
|
this.onChange(change.newValue as T | undefined, change.oldValue as T | undefined);
|
|
});
|
|
|
|
if (autoInit) {
|
|
this.init();
|
|
}
|
|
}
|
|
|
|
private onData = (data: T): void => {
|
|
const notEmpty = data != null;
|
|
const notDefault = !this.isDefaultValue(data);
|
|
|
|
if (notEmpty && notDefault) {
|
|
this.set(data);
|
|
}
|
|
|
|
this.initialized = true;
|
|
};
|
|
|
|
private onError = (error: any): void => {
|
|
this.dependencies.logger.error(`${storageHelperLogPrefix} loading error: ${error}`, this);
|
|
};
|
|
|
|
@action
|
|
init({ force = false } = {}) {
|
|
if (this.initialized && !force) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const data = this.storage.getItem(this.key);
|
|
|
|
if (data instanceof Promise) {
|
|
data.then(this.onData, this.onError);
|
|
} else {
|
|
this.onData(data);
|
|
}
|
|
} catch (error) {
|
|
this.onError(error);
|
|
}
|
|
}
|
|
|
|
isDefaultValue(value: T): boolean {
|
|
return isEqual(value, this.defaultValue);
|
|
}
|
|
|
|
protected onChange(value: T | undefined, oldValue: T | undefined) {
|
|
if (!this.initialized) return;
|
|
|
|
try {
|
|
if (value == null) {
|
|
this.storage.removeItem(this.key);
|
|
} else {
|
|
this.storage.setItem(this.key, value);
|
|
}
|
|
|
|
this.storage.onChange?.({ value, oldValue, key: this.key });
|
|
} catch (error) {
|
|
this.dependencies.logger.error(`${storageHelperLogPrefix} updating storage: ${error}`, this, { value, oldValue });
|
|
}
|
|
}
|
|
|
|
get(): T {
|
|
return this.value;
|
|
}
|
|
|
|
@computed
|
|
get value(): T {
|
|
return this.data.get() ?? this.defaultValue;
|
|
}
|
|
|
|
@action
|
|
set(value: T) {
|
|
if (this.isDefaultValue(value)) {
|
|
this.reset();
|
|
} else {
|
|
this.data.set(value);
|
|
}
|
|
}
|
|
|
|
@action
|
|
reset() {
|
|
this.data.set(undefined);
|
|
}
|
|
|
|
@action
|
|
merge(value: T extends object ? Partial<T> | ((draft: Draft<T>) => Partial<T> | void) : never) {
|
|
const nextValue = produce<T>(toJS(this.get()), (draft) => {
|
|
assert(typeof draft === "object" && draft);
|
|
|
|
if (typeof value == "function") {
|
|
const newValue = value(draft);
|
|
|
|
// merge returned plain objects from `value-as-callback` usage
|
|
// otherwise `draft` can be just modified inside a callback without returning any value (void)
|
|
if (newValue && !isDraft(newValue)) {
|
|
Object.assign(draft, newValue);
|
|
}
|
|
} else if (isPlainObject(value)) {
|
|
Object.assign(draft, value);
|
|
}
|
|
|
|
return draft;
|
|
});
|
|
|
|
this.set(nextValue);
|
|
}
|
|
}
|