1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Move box into injectable within InitializableState to fix tests

- The slight difference in the unit testing where we pretend that there
  are multiple environments but actually on one caused a problem that
  doesn't exist in the actual running program.

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-11-01 14:52:56 -04:00
parent 4f3b9cb326
commit a70992e005
15 changed files with 155 additions and 133 deletions

View File

@ -4,7 +4,7 @@
*/
import directoryForUserDataInjectable, { initDirectoryForUserDataOnMainInjectable, initDirectoryForUserDataOnRendererInjectable } from "./directory-for-user-data.injectable";
import joinPathsInjectable from "../path/join-paths.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
const {
value: directoryForBinariesInjectable,

View File

@ -4,7 +4,7 @@
*/
import { initAppPathsOnMainInjectable } from "../../main/app-paths/impl.injectable";
import { initAppPathsOnRendererInjectable } from "../../renderer/app-paths/impl.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
import { appPathsInjectionToken } from "./token";
const {

View File

@ -4,7 +4,7 @@
*/
import { initAppPathsOnMainInjectable } from "../../main/app-paths/impl.injectable";
import { initAppPathsOnRendererInjectable } from "../../renderer/app-paths/impl.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
import { appPathsInjectionToken } from "./token";
const {

View File

@ -4,7 +4,7 @@
*/
import directoryForUserDataInjectable, { initDirectoryForUserDataOnMainInjectable, initDirectoryForUserDataOnRendererInjectable } from "./directory-for-user-data.injectable";
import joinPathsInjectable from "../path/join-paths.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
const {
value: directoryForKubeConfigsInjectable,

View File

@ -4,7 +4,7 @@
*/
import directoryForBinariesInjectable, { initDirectoryForBinariesOnMainInjectable, initDirectoryForBinariesOnRendererInjectable } from "./directory-for-binaries.injectable";
import joinPathsInjectable from "../path/join-paths.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
const {
value: directoryForKubectlBinariesInjectable,

View File

@ -4,7 +4,7 @@
*/
import { initAppPathsOnMainInjectable } from "../../main/app-paths/impl.injectable";
import { initAppPathsOnRendererInjectable } from "../../renderer/app-paths/impl.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
import { appPathsInjectionToken } from "./token";
const {

View File

@ -4,7 +4,7 @@
*/
import { initAppPathsOnMainInjectable } from "../../main/app-paths/impl.injectable";
import { initAppPathsOnRendererInjectable } from "../../renderer/app-paths/impl.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
import { appPathsInjectionToken } from "./token";
const {

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import directoryForUserDataInjectable, { initDirectoryForUserDataOnMainInjectable, initDirectoryForUserDataOnRendererInjectable } from "../app-paths/directory-for-user-data.injectable";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
import joinPathsInjectable from "../path/join-paths.injectable";
const {

View File

@ -1,85 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import type { Runnable } from "../runnable/run-many-for";
import type { InitializableState, InitializableStateValue } from "./create";
export interface CreateDependentInitializableStateArgs<T> {
id: string;
init: (di: DiContainerForInjection) => Promise<T> | T;
injectionToken?: InjectionToken<InitializableState<T>, void>;
initAfter: Injectable<Runnable<void>, Runnable<void>, void>[];
}
export interface CreateDependentInitializableStateResult<T> {
value: Injectable<InitializableState<T>, unknown, void>;
initializers: Injectable<Runnable<void>, Runnable<void>, void>[];
}
export function createDependentInitializableState<T>(args: CreateDependentInitializableStateArgs<T>): CreateDependentInitializableStateResult<T> {
const { id, init, injectionToken, initAfter } = args;
let box: InitializableStateValue<T> = {
set: false,
};
let initCalled = false;
const valueInjectable = getInjectable({
id,
instantiate: (): InitializableState<T> => ({
get: () => {
if (!initCalled) {
throw new Error(`InitializableState(${id}) has not been initialized yet`);
}
if (box.set === false) {
throw new Error(`InitializableState(${id}) has not finished initializing`);
}
return box.value;
},
}),
injectionToken,
});
const initializers = initAfter.map(runnableInjectable => getInjectable({
id: `initialize-${id}-during-${runnableInjectable.injectionToken?.id}`,
instantiate: (di) => ({
id: `initialize-${id}`,
run: (): void | Promise<void> => {
if (initCalled) {
throw new Error(`Cannot initialize InitializableState(${id}) more than once`);
}
initCalled = true;
const potentialValue = init(di);
if (potentialValue instanceof Promise) {
// This is done because we have to run syncronously if `init` is syncronous to prevent ordering issues
return (async () => {
box = {
set: true,
value: await potentialValue,
};
})();
} else {
box = {
set: true,
value: potentialValue,
};
}
},
runAfter: di.inject(runnableInjectable),
}),
injectionToken: runnableInjectable.injectionToken,
}));
return {
value: valueInjectable,
initializers,
};
}

View File

@ -6,6 +6,7 @@
import type { DiContainerForInjection, Injectable, InjectionToken } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import type { Runnable } from "../runnable/run-many-for";
import type { Discriminable } from "../utils/composable-responsibilities/discriminable/discriminable";
export interface CreateInitializableStateArgs<T> {
id: string;
@ -14,13 +15,23 @@ export interface CreateInitializableStateArgs<T> {
when: InjectionToken<Runnable<void>, void>;
}
const setInitializing = Symbol("set-initializing");
const initialize = Symbol("initialize");
export interface InitializableState<T> {
get: () => T;
[setInitializing]: () => void;
[initialize]: (value: T) => void;
}
export type UnsetValue = Discriminable<"uninitialized">;
export type InitializingValue = Discriminable<"initializing">;
export type InitializedValue<T> = Discriminable<"initialized"> & { value: T };
export type InitializableStateValue<T> =
| { set: false }
| { set: true; value: T };
| UnsetValue
| InitializingValue
| InitializedValue<T>;
export interface CreateInitializableStateResult<T> {
value: Injectable<InitializableState<T>, unknown, void>;
@ -30,54 +41,62 @@ export interface CreateInitializableStateResult<T> {
export function createInitializableState<T>(args: CreateInitializableStateArgs<T>): CreateInitializableStateResult<T> {
const { id, init, injectionToken, when } = args;
let box: InitializableStateValue<T> = {
set: false,
};
let initCalled = false;
const valueInjectable = getInjectable({
id,
instantiate: (): InitializableState<T> => ({
get: () => {
if (!initCalled) {
throw new Error(`InitializableState(${id}) has not been initialized yet`);
}
instantiate: (): InitializableState<T> => {
let box: InitializableStateValue<T> = {
kind: "uninitialized",
};
if (box.set === false) {
throw new Error(`InitializableState(${id}) has not finished initializing`);
}
return {
get: () => {
if (box.kind !== "initialized") {
throw new Error(`Cannot get value from "${id}"; it is currently in state=${box.kind}`);
}
return box.value;
},
}),
return box.value;
},
[setInitializing]: () => {
if (box.kind !== "uninitialized") {
throw new Error(`Cannot start initializing value for "${id}"; it is currently in state=${box.kind}`);
}
box = {
kind: "initializing",
};
},
[initialize]: (value) => {
if (box.kind !== "initializing") {
throw new Error(`Cannot initialize value for "${id}"; it is currently in state=${box.kind}`);
}
box = {
kind: "initialized",
value,
};
},
};
},
injectionToken,
});
const subId = `initialize id="${id}" during id="${when.id}"`;
const initializer = getInjectable({
id: `initialize-${id}`,
id: subId,
instantiate: (di) => ({
id: `initialize-${id}`,
run: async () => {
if (initCalled) {
throw new Error(`Cannot initialize InitializableState(${id}) more than once`);
}
id: subId,
run: (): void | Promise<void> => {
const value = di.inject(valueInjectable);
value[setInitializing]();
initCalled = true;
const potentialValue = init(di);
if (potentialValue instanceof Promise) {
// This is done because we have to run syncronously if `init` is syncronous to prevent ordering issues
return (async () => {
box = {
set: true,
value: await potentialValue,
};
})();
return potentialValue.then(value[initialize]);
} else {
box = {
set: true,
value: potentialValue,
};
value[initialize](potentialValue);
}
},
}),
@ -89,3 +108,91 @@ export function createInitializableState<T>(args: CreateInitializableStateArgs<T
initializer,
};
}
export interface CreateDependentInitializableStateArgs<T> {
id: string;
init: (di: DiContainerForInjection) => Promise<T> | T;
injectionToken?: InjectionToken<InitializableState<T>, void>;
initAfter: Injectable<Runnable<void>, Runnable<void>, void>[];
}
export interface CreateDependentInitializableStateResult<T> {
value: Injectable<InitializableState<T>, unknown, void>;
initializers: Injectable<Runnable<void>, Runnable<void>, void>[];
}
export function createDependentInitializableState<T>(args: CreateDependentInitializableStateArgs<T>): CreateDependentInitializableStateResult<T> {
const { id, init, injectionToken, initAfter } = args;
const valueInjectable = getInjectable({
id,
instantiate: (): InitializableState<T> => {
let box: InitializableStateValue<T> = {
kind: "uninitialized",
};
return {
get: () => {
if (box.kind !== "initialized") {
throw new Error(`Cannot get value from "${id}"; it is currently in state=${box.kind}`);
}
return box.value;
},
[setInitializing]: () => {
if (box.kind !== "uninitialized") {
throw new Error(`Cannot start initializing value for "${id}"; it is currently in state=${box.kind}`);
}
box = {
kind: "initializing",
};
},
[initialize]: (value) => {
if (box.kind !== "initializing") {
throw new Error(`Cannot initialize value for "${id}"; it is currently in state=${box.kind}`);
}
box = {
kind: "initialized",
value,
};
},
};
},
injectionToken,
});
const initializers = initAfter.map(runnableInjectable => {
const subId = `initialize "${id}" after "${runnableInjectable.id}"`;
return getInjectable({
id: subId,
instantiate: (di) => ({
id: subId,
run: (): void | Promise<void> => {
const value = di.inject(valueInjectable);
value[setInitializing]();
const potentialValue = init(di);
if (potentialValue instanceof Promise) {
// This is done because we have to run syncronously if `init` is syncronous to prevent ordering issues
return potentialValue.then(value[initialize]);
} else {
value[initialize](potentialValue);
}
},
runAfter: di.inject(runnableInjectable),
}),
injectionToken: runnableInjectable.injectionToken,
});
});
return {
value: valueInjectable,
initializers,
};
}

View File

@ -6,7 +6,7 @@
import { SemVer } from "semver";
import { initializeBuildVersionOnMainInjectable } from "../../../main/vars/build-version/build-version.injectable";
import { initializeBuildVersionOnRendererInjectable } from "../../../renderer/vars/build-version.injectable";
import { createDependentInitializableState } from "../../initializable-state/create-dependent";
import { createDependentInitializableState } from "../../initializable-state/create";
import { buildVersionInjectionToken } from "./token";
const {

View File

@ -4,7 +4,7 @@
*/
import buildSemanticVersionInjectable, { initBuildSemanticVersionOnMainInjectable, initBuildSemanticVersionOnRendererInjectable } from "./build-version/semantic-version.injectable";
import type { ReleaseChannel } from "../../features/application-update/common/update-channels";
import { createDependentInitializableState } from "../initializable-state/create-dependent";
import { createDependentInitializableState } from "../initializable-state/create";
const {
value: releaseChannelInjectable,

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import directoryForUserDataInjectable, { initDirectoryForUserDataOnMainInjectable, initDirectoryForUserDataOnRendererInjectable } from "../../common/app-paths/directory-for-user-data.injectable";
import { createDependentInitializableState } from "../../common/initializable-state/create-dependent";
import { createDependentInitializableState } from "../../common/initializable-state/create";
const {
value: extensionPackageRootDirectoryInjectable,

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import directoryForUserDataInjectable, { initDirectoryForUserDataOnMainInjectable, initDirectoryForUserDataOnRendererInjectable } from "../../../common/app-paths/directory-for-user-data.injectable";
import { createDependentInitializableState } from "../../../common/initializable-state/create-dependent";
import { createDependentInitializableState } from "../../../common/initializable-state/create";
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
const {

View File

@ -2,7 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { createDependentInitializableState } from "../../../../common/initializable-state/create-dependent";
import { createDependentInitializableState } from "../../../../common/initializable-state/create";
import releaseChannelInjectable, { initReleaseChannelOnMainInjectable, initReleaseChannelOnRendererInjectable } from "../../../../common/vars/release-channel.injectable";
import { updateChannels } from "../update-channels";