Make base store non Singleton (#6690)

* 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>
This commit is contained in:
Sebastian Malton authored and GitHub committed 2022-12-15 17:07:19 +02:00
1 parent 7c5faaaf1d
commit 25f37ac1d1
364 files changed
+10035 -5246

No files matched your search

@@ -6,7 +6,7 @@
import { observable, reaction } from "mobx";
import { StorageHelper } from "../storageHelper";
import { delay } from "../../../common/utils/delay";
import { toJS } from "../../../common/utils";
import { noop, toJS } from "../../../common/utils";
interface StorageModel {
[prop: string]: any /*json-serializable*/;
@@ -26,7 +26,15 @@ describe("renderer/utils/StorageHelper", () => {
message: "saved-before", // pretending as previously saved data
});
storageHelper = new StorageHelper<StorageModel>(storageKey, {
storageHelper = new StorageHelper<StorageModel>({
logger: {
debug: noop,
error: noop,
info: noop,
silly: noop,
warn: noop,
},
}, storageKey, {
autoInit: false,
defaultValue: {
message: "blabla",
@@ -48,7 +56,15 @@ describe("renderer/utils/StorageHelper", () => {
},
});
storageHelperAsync = new StorageHelper(storageKey, {
storageHelperAsync = new StorageHelper({
logger: {
debug: noop,
error: noop,
info: noop,
silly: noop,
warn: noop,
},
}, storageKey, {
autoInit: false,
defaultValue: storageHelper.defaultValue,
storage: {
@@ -118,7 +134,15 @@ describe("renderer/utils/StorageHelper", () => {
beforeEach(() => {
observedChanges.length = 0;
storageHelper = new StorageHelper<typeof defaultValue>("some-key", {
storageHelper = new StorageHelper<typeof defaultValue>({
logger: {
debug: noop,
error: noop,
info: noop,
silly: noop,
warn: noop,
},
}, "some-key", {
autoInit: true,
defaultValue,
storage: {
@@ -3,22 +3,20 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeFrameStartsInjectionToken } from "../../../before-frame-starts/before-frame-starts-injection-token";
import { beforeFrameStartsInjectionToken } from "../../../before-frame-starts/tokens";
import listeningOnMessageChannelsInjectable from "../../../../common/utils/channel/listening-on-message-channels.injectable";
const startListeningOfChannelsInjectable = getInjectable({
id: "start-listening-of-channels-renderer",
instantiate: (di) => {
const listeningOfChannels = di.inject(listeningOnMessageChannelsInjectable);
instantiate: (di) => ({
id: "start-listening-of-channels-renderer",
run: () => {
const listeningOfChannels = di.inject(listeningOnMessageChannelsInjectable);
return {
id: "start-listening-of-channels-renderer",
run: async () => {
await listeningOfChannels.start();
},
};
},
listeningOfChannels.start();
},
}),
injectionToken: beforeFrameStartsInjectionToken,
});
@@ -7,17 +7,19 @@
// Because app creates random port between restarts => storage session wiped out each time.
import { comparer, reaction, toJS, when } from "mobx";
import type { StorageLayer } from "../storageHelper";
import { StorageHelper } from "../storageHelper";
import type { JsonObject, JsonValue } from "type-fest";
import { storageHelperLogPrefix, StorageHelper } from "../storageHelper";
import type { JsonObject } from "type-fest";
import type { Logger } from "../../../common/logger";
import type { JoinPaths } from "../../../common/path/join-paths.injectable";
import type { WriteJson } from "../../../common/fs/write-json-file.injectable";
import type { ReadJson } from "../../../common/fs/read-json-file.injectable";
interface Dependencies {
storage: { initialized: boolean; loaded: boolean; data: Record<string, any> };
storage: { initialized: boolean; loaded: boolean; data: Partial<Record<string, unknown>> };
logger: Logger;
directoryForLensLocalStorage: string;
readJsonFile: (filePath: string) => Promise<JsonValue>;
writeJsonFile: (filePath: string, contentObject: JsonObject) => Promise<void>;
readJsonFile: ReadJson;
writeJsonFile: WriteJson;
joinPaths: JoinPaths;
hostedClusterId: string | undefined;
saveDelay: number;
@@ -37,9 +39,7 @@ export const createStorage = ({
writeJsonFile,
hostedClusterId,
saveDelay,
}: Dependencies): CreateStorage => (key, defaultValue) => {
const { logPrefix } = StorageHelper;
}: Dependencies): CreateStorage => <T>(key: string, defaultValue: T) => {
if (!storage.initialized) {
storage.initialized = true;
@@ -51,7 +51,7 @@ export const createStorage = ({
} catch {
// do nothing
} finally {
logger.info(`${logPrefix} loading finished for ${filePath}`);
logger.info(`${storageHelperLogPrefix} loading finished for ${filePath}`);
storage.loaded = true;
}
@@ -62,30 +62,32 @@ export const createStorage = ({
});
async function saveFile(state: Record<string, any> = {}) {
logger.info(`${logPrefix} saving ${filePath}`);
logger.info(`${storageHelperLogPrefix} saving ${filePath}`);
try {
await writeJsonFile(filePath, state);
} catch (error) {
logger.error(`${logPrefix} saving failed: ${error}`, {
logger.error(`${storageHelperLogPrefix} saving failed: ${error}`, {
json: state, jsonFilePath: filePath,
});
}
}
})()
.catch(error => logger.error(`${logPrefix} Failed to initialize storage: ${error}`));
.catch(error => logger.error(`${storageHelperLogPrefix} Failed to initialize storage: ${error}`));
}
return new StorageHelper(key, {
return new StorageHelper({
logger,
}, key, {
autoInit: true,
defaultValue,
storage: {
async getItem(key: string) {
await when(() => storage.loaded);
return storage.data[key];
return storage.data[key] as T;
},
setItem(key: string, value: any) {
setItem(key: string, value: T) {
storage.data[key] = value;
},
removeItem(key: string) {
+14 -9
View File
@@ -8,8 +8,8 @@ import { action, comparer, computed, makeObservable, observable, observe, toJS,
import type { Draft } from "immer";
import { produce, isDraft } from "immer";
import { isEqual, isPlainObject } from "lodash";
import logger from "../../main/logger";
import assert from "assert";
import type { Logger } from "../../common/logger";
export interface StorageChange<T> {
key: string;
@@ -26,9 +26,9 @@ export interface StorageAdapter<T> {
}
export interface StorageHelperOptions<T> {
autoInit?: boolean; // start preloading data immediately, default: true
storage: StorageAdapter<T>;
defaultValue: T;
readonly autoInit?: boolean; // start preloading data immediately, default: true
readonly storage: StorageAdapter<T>;
readonly defaultValue: T;
}
export interface StorageLayer<T> {
@@ -41,11 +41,16 @@ export interface StorageLayer<T> {
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> {
static logPrefix = "[StorageHelper]:";
readonly storage: StorageAdapter<T>;
private data = observable.box<T | undefined>(undefined, {
private readonly data = observable.box<T | undefined>(undefined, {
deep: true,
equals: comparer.structural,
});
@@ -61,7 +66,7 @@ export class StorageHelper<T> implements StorageLayer<T> {
return this.options.defaultValue;
}
constructor(readonly key: string, private options: StorageHelperOptions<T>) {
constructor(private readonly dependencies: Dependencies, readonly key: string, private readonly options: StorageHelperOptions<T>) {
makeObservable(this);
const { storage, autoInit = true } = options;
@@ -89,7 +94,7 @@ export class StorageHelper<T> implements StorageLayer<T> {
};
private onError = (error: any): void => {
logger.error(`${StorageHelper.logPrefix} loading error: ${error}`, this);
this.dependencies.logger.error(`${storageHelperLogPrefix} loading error: ${error}`, this);
};
@action
@@ -127,7 +132,7 @@ export class StorageHelper<T> implements StorageLayer<T> {
this.storage.onChange?.({ value, oldValue, key: this.key });
} catch (error) {
logger.error(`${StorageHelper.logPrefix} updating storage: ${error}`, this, { value, oldValue });
this.dependencies.logger.error(`${storageHelperLogPrefix} updating storage: ${error}`, this, { value, oldValue });
}
}
@@ -3,43 +3,34 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/before-frame-starts-injection-token";
import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/tokens";
import { syncBoxInitialValueChannel } from "../../../common/utils/sync-box/channels";
import createSyncBoxStateInjectable from "../../../common/utils/sync-box/sync-box-state.injectable";
import { requestFromChannelInjectionToken } from "../../../common/utils/channel/request-from-channel-injection-token";
import { runInAction } from "mobx";
import type { SyncBox } from "../../../common/utils/sync-box/sync-box-injection-token";
import { syncBoxInjectionToken } from "../../../common/utils/sync-box/sync-box-injection-token";
import assert from "assert";
const provideInitialValuesForSyncBoxesInjectable = getInjectable({
id: "provide-initial-values-for-sync-boxes",
instantiate: (di) => {
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
instantiate: (di) => ({
id: "provide-initial-values-for-sync-boxes",
run: async () => {
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
const syncBoxes = di.injectMany(syncBoxInjectionToken);
const initialValues = await requestFromChannel(syncBoxInitialValueChannel);
const syncBoxes = di.injectMany(syncBoxInjectionToken);
runInAction(() => {
for (const { id, value } of initialValues) {
const syncBox = syncBoxes.find((box) => box.id === id);
const setSyncBoxState = (syncBox: SyncBox<any>, state: any) =>
di.inject(createSyncBoxStateInjectable, syncBox.id).set(state);
return {
id: "provide-initial-values-for-sync-boxes",
run: async () => {
const initialValues = await requestFromChannel(syncBoxInitialValueChannel);
runInAction(() => {
initialValues.forEach(({ id, value }) => {
const syncBox = syncBoxes.find((box) => box.id === id);
assert(syncBox);
setSyncBoxState(syncBox, value);
});
});
},
};
},
assert(syncBox);
di.inject(createSyncBoxStateInjectable, syncBox.id).set(value);
}
});
},
}),
injectionToken: beforeFrameStartsInjectionToken,
});