From c4c4bc58452d10a985179706e1667589cb373e0d Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Wed, 10 Nov 2021 14:01:02 +0200 Subject: [PATCH 1/8] Introduce a way to use injectables for UI Signed-off-by: Janne Savolainen --- package.json | 2 + src/renderer/cluster-frame.tsx | 73 ++++++++++--------- src/renderer/components/getDi.tsx | 31 ++++++++ .../components/getDiForUnitTesting.tsx | 53 ++++++++++++++ types/ogre-tools-injectable-react.d.ts | 45 ++++++++++++ types/ogre-tools-injectable.d.ts | 69 ++++++++++++++++++ yarn.lock | 24 ++++++ 7 files changed, 263 insertions(+), 34 deletions(-) create mode 100644 src/renderer/components/getDi.tsx create mode 100644 src/renderer/components/getDiForUnitTesting.tsx create mode 100644 types/ogre-tools-injectable-react.d.ts create mode 100644 types/ogre-tools-injectable.d.ts diff --git a/package.json b/package.json index 8bc2f1e99e..10e4d8acae 100644 --- a/package.json +++ b/package.json @@ -188,6 +188,8 @@ "@kubernetes/client-node": "^0.15.1", "@sentry/electron": "^2.5.4", "@sentry/integrations": "^6.15.0", + "@ogre-tools/injectable": "^1.0.2", + "@ogre-tools/injectable-react": "^1.0.3", "abort-controller": "^3.0.0", "auto-bind": "^4.0.0", "autobind-decorator": "^2.4.0", diff --git a/src/renderer/cluster-frame.tsx b/src/renderer/cluster-frame.tsx index 0786ca18a5..e4e2b148d6 100755 --- a/src/renderer/cluster-frame.tsx +++ b/src/renderer/cluster-frame.tsx @@ -22,6 +22,7 @@ import React from "react"; import { observable, makeObservable } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { Redirect, Route, Router, Switch } from "react-router"; +import { DiContextProvider } from "@ogre-tools/injectable-react"; import { history } from "./navigation"; import { NotFound } from "./components/+404"; import { UserManagement } from "./components/+user-management/user-management"; @@ -73,12 +74,14 @@ import { watchHistoryState } from "./remote-helpers/history-updater"; import { unmountComponentAtNode } from "react-dom"; import { PortForwardDialog } from "./port-forward"; import { DeleteClusterDialog } from "./components/delete-cluster-dialog"; +import { getDi } from "./components/getDi"; @observer export class ClusterFrame extends React.Component { static clusterId: ClusterId; static readonly logPrefix = "[CLUSTER-FRAME]:"; static displayName = "ClusterFrame"; + diContainer = getDi(); constructor(props: {}) { super(props); @@ -193,40 +196,42 @@ export class ClusterFrame extends React.Component { render() { return ( - - - } footer={}> - - - - - - - - - - - - - {this.renderExtensionTabLayoutRoutes()} - {this.renderExtensionRoutes()} - - - - - - - - - - - - - - - - - + + + + } footer={}> + + + + + + + + + + + + + {this.renderExtensionTabLayoutRoutes()} + {this.renderExtensionRoutes()} + + + + + + + + + + + + + + + + + + ); } } diff --git a/src/renderer/components/getDi.tsx b/src/renderer/components/getDi.tsx new file mode 100644 index 0000000000..745573e6ea --- /dev/null +++ b/src/renderer/components/getDi.tsx @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { createContainer } from "@ogre-tools/injectable"; +import type { IConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; + +export const getDi = () => { + const di: IConfigurableDependencyInjectionContainer = createContainer( + () => require.context("./", true, /\.injectable\.(ts|tsx)$/), + ); + + return di; +}; diff --git a/src/renderer/components/getDiForUnitTesting.tsx b/src/renderer/components/getDiForUnitTesting.tsx new file mode 100644 index 0000000000..0ce9b6c9b0 --- /dev/null +++ b/src/renderer/components/getDiForUnitTesting.tsx @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import glob from "glob"; +import { memoize } from "lodash/fp"; + +import { + createContainer, + IConfigurableDependencyInjectionContainer, +} from "@ogre-tools/injectable"; + +export const getDiForUnitTesting = () => { + const di: IConfigurableDependencyInjectionContainer = createContainer(); + + getInjectableFilePaths() + .map(key => { + const injectable = require(key).default; + + return { + id: key, + ...injectable, + aliases: [injectable, ...(injectable.aliases || [])], + }; + }) + + .forEach(injectable => di.register(injectable)); + + di.preventSideEffects(); + + return di; +}; + +const getInjectableFilePaths = memoize(() => + glob.sync("./**/*.injectable.{ts,tsx}", { cwd: __dirname }), +); diff --git a/types/ogre-tools-injectable-react.d.ts b/types/ogre-tools-injectable-react.d.ts new file mode 100644 index 0000000000..3624dec706 --- /dev/null +++ b/types/ogre-tools-injectable-react.d.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/// +declare module "@ogre-tools/injectable-react" { + import type { IDependencyInjectionContainer } from "@ogre-tools/injectable"; + + interface IDependencyInjectionContainerProviderProps { + di: IDependencyInjectionContainer; + } + + export const DiContextProvider: React.Provider; + + export const Inject: < + TComponentInjectable extends IComponentInjectable, + >({ + Component, + injectableKey, + getPlaceholder, + ...props + }: Omit< + React.ComponentProps, + keyof ReturnType + > & { + injectableKey: TComponentInjectable; + getPlaceholder?: () => JSX.Element | null; + }) => JSX.Element; +} diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts new file mode 100644 index 0000000000..08c5b3f286 --- /dev/null +++ b/types/ogre-tools-injectable.d.ts @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +declare module "@ogre-tools/injectable" { + export interface IDependencyInjectionContainer { + inject: < + TInjectable extends IInjectable, + TInstance, + TDependencies, + >( + injectableKey: TInjectable, + ) => ReturnType; + } + + export interface IConfigurableDependencyInjectionContainer + extends IDependencyInjectionContainer { + register: ( + injectable: IInjectable | IComponentInjectable, + ) => void; + preventSideEffects: () => void; + + override: , TInstance>( + injectable: TInjectable, + overrider: ReturnType, + ) => void; + } + + interface ICommonInjectable { + id?: string; + getDependencies: (di?: IDependencyInjectionContainer) => TDependencies; + lifecycle?: lifecycleEnum; + } + + export interface IInjectable + extends ICommonInjectable { + instantiate: (dependencies: TDependencies) => TInstance; + } + + export interface IComponentInjectable + extends ICommonInjectable { + instantiate: TInstance; + } + + export enum lifecycleEnum { + singleton, + transient, + scopedTransient, + } + + export const createContainer = (...getRequireContexts: any[]) => + IConfigurableDependencyInjectionContainer; +} diff --git a/yarn.lock b/yarn.lock index a427e9f745..c78e8fed10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -946,6 +946,30 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@ogre-tools/fp@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@ogre-tools/fp/-/fp-1.0.2.tgz#26c2c5cf60aa01cc94763cc68beba7052fdadfd9" + integrity sha512-ftvi/aoi5PaojWnuhHzp0YiecUd22HzW5gErsSiKyO2bps90WI4WjgY6d9hWdlzM9eukVmwM+dC6rGNlltNHNw== + dependencies: + lodash "^4.17.21" + +"@ogre-tools/injectable-react@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.0.3.tgz#e30ea455cc4ccf24fbad831b460023492832c2f9" + integrity sha512-CFJeuezdJIZD0o0rp1MnahzItzZ7eSixSo0v0+002kq8T7HZb1ycD8d/zvXOBnkEF34K6GGibXicEw1q46SZzw== + dependencies: + "@ogre-tools/fp" "^1.0.2" + "@ogre-tools/injectable" "^1.0.2" + lodash "^4.17.21" + +"@ogre-tools/injectable@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.0.2.tgz#b081fefb2026c57fe47e27c268efa73d43934546" + integrity sha512-NZ7FHxKLfr+8o4aL51UQ212w1wD3QIsEN/JNtjtEq0TgLjd2Qo3zSniL4hCEj5M49K2qe3CxEb6ezyf2vFTb5g== + dependencies: + "@ogre-tools/fp" "^1.0.2" + lodash "^4.17.21" + "@panva/asn1.js@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" From 3bc01dc50f0fd01328411c5db4d462f464ff5d39 Mon Sep 17 00:00:00 2001 From: Mikko Aspiala Date: Mon, 15 Nov 2021 13:37:09 +0200 Subject: [PATCH 2/8] Make Inject-component usable in both LensApp and "App" Note: this will need some rehaul, as current design with "bootstrap" and iframe make single root for injectables somewhat challenging. Co-authored-by: Janne Savolainen Signed-off-by: Janne Savolainen --- src/renderer/bootstrap.tsx | 28 +++++++++---- src/renderer/cluster-frame.tsx | 73 ++++++++++++++++------------------ 2 files changed, 54 insertions(+), 47 deletions(-) diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 90e8c7fa95..bcae5a84a3 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -50,6 +50,9 @@ import { SentryInit } from "../common/sentry"; import { TerminalStore } from "./components/dock/terminal.store"; import { AppPaths } from "../common/app-paths"; import { registerCustomThemes } from "./components/monaco-editor"; +import { getDi } from "./components/getDi"; +import { DiContextProvider } from "@ogre-tools/injectable-react"; +import type { IDependencyInjectionContainer } from "@ogre-tools/injectable"; if (process.isMainFrame) { SentryInit(); @@ -73,7 +76,7 @@ type AppComponent = React.ComponentType & { init(rootElem: HTMLElement): Promise; }; -export async function bootstrap(comp: () => Promise) { +export async function bootstrap(comp: () => Promise, di: IDependencyInjectionContainer) { const rootElem = document.getElementById("app"); const logPrefix = `[BOOTSTRAP-${process.isMainFrame ? "ROOT" : "CLUSTER"}-FRAME]:`; @@ -147,17 +150,26 @@ export async function bootstrap(comp: () => Promise) { await App.init(rootElem); - render(<> - {isMac &&
} - {DefaultProps(App)} - , rootElem); + render( + + {isMac &&
} + + {DefaultProps(App)} + , + + rootElem, + ); } +const di = getDi(); + // run bootstrap( - async () => process.isMainFrame - ? (await import("./root-frame")).RootFrame - : (await import("./cluster-frame")).ClusterFrame, + async () => + process.isMainFrame + ? (await import("./root-frame")).RootFrame + : (await import("./cluster-frame")).ClusterFrame, + di, ); diff --git a/src/renderer/cluster-frame.tsx b/src/renderer/cluster-frame.tsx index e4e2b148d6..0786ca18a5 100755 --- a/src/renderer/cluster-frame.tsx +++ b/src/renderer/cluster-frame.tsx @@ -22,7 +22,6 @@ import React from "react"; import { observable, makeObservable } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { Redirect, Route, Router, Switch } from "react-router"; -import { DiContextProvider } from "@ogre-tools/injectable-react"; import { history } from "./navigation"; import { NotFound } from "./components/+404"; import { UserManagement } from "./components/+user-management/user-management"; @@ -74,14 +73,12 @@ import { watchHistoryState } from "./remote-helpers/history-updater"; import { unmountComponentAtNode } from "react-dom"; import { PortForwardDialog } from "./port-forward"; import { DeleteClusterDialog } from "./components/delete-cluster-dialog"; -import { getDi } from "./components/getDi"; @observer export class ClusterFrame extends React.Component { static clusterId: ClusterId; static readonly logPrefix = "[CLUSTER-FRAME]:"; static displayName = "ClusterFrame"; - diContainer = getDi(); constructor(props: {}) { super(props); @@ -196,42 +193,40 @@ export class ClusterFrame extends React.Component { render() { return ( - - - - } footer={}> - - - - - - - - - - - - - {this.renderExtensionTabLayoutRoutes()} - {this.renderExtensionRoutes()} - - - - - - - - - - - - - - - - - - + + + } footer={}> + + + + + + + + + + + + + {this.renderExtensionTabLayoutRoutes()} + {this.renderExtensionRoutes()} + + + + + + + + + + + + + + + + + ); } } From 0af1d0829932858d0d9fa9fc54c1597be540ffb1 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Mon, 15 Nov 2021 15:44:20 +0200 Subject: [PATCH 3/8] Fix linting in type definition Signed-off-by: Janne Savolainen --- types/ogre-tools-injectable.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts index 08c5b3f286..b04cf02740 100644 --- a/types/ogre-tools-injectable.d.ts +++ b/types/ogre-tools-injectable.d.ts @@ -64,6 +64,7 @@ declare module "@ogre-tools/injectable" { scopedTransient, } + // eslint-disable-next-line unused-imports/no-unused-vars-ts export const createContainer = (...getRequireContexts: any[]) => IConfigurableDependencyInjectionContainer; } From 2e5e54995794fe02139140fb9e41bbf05471bd1a Mon Sep 17 00:00:00 2001 From: Mikko Aspiala Date: Wed, 17 Nov 2021 14:34:47 +0200 Subject: [PATCH 4/8] Update version of injectable for disambiguous typing Signed-off-by: Mikko Aspiala Co-authored-by: Janne Savolainen --- package.json | 4 ++-- types/ogre-tools-injectable-react.d.ts | 9 +++------ types/ogre-tools-injectable.d.ts | 17 +++++++++-------- yarn.lock | 18 +++++++++--------- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 10e4d8acae..09fa5f212a 100644 --- a/package.json +++ b/package.json @@ -188,8 +188,8 @@ "@kubernetes/client-node": "^0.15.1", "@sentry/electron": "^2.5.4", "@sentry/integrations": "^6.15.0", - "@ogre-tools/injectable": "^1.0.2", - "@ogre-tools/injectable-react": "^1.0.3", + "@ogre-tools/injectable": "^1.1.0", + "@ogre-tools/injectable-react": "^1.1.0", "abort-controller": "^3.0.0", "auto-bind": "^4.0.0", "autobind-decorator": "^2.4.0", diff --git a/types/ogre-tools-injectable-react.d.ts b/types/ogre-tools-injectable-react.d.ts index 3624dec706..38599d909d 100644 --- a/types/ogre-tools-injectable-react.d.ts +++ b/types/ogre-tools-injectable-react.d.ts @@ -20,7 +20,7 @@ */ /// declare module "@ogre-tools/injectable-react" { - import type { IDependencyInjectionContainer } from "@ogre-tools/injectable"; + import type { IDependencyInjectionContainer, IInjectable } from "@ogre-tools/injectable"; interface IDependencyInjectionContainerProviderProps { di: IDependencyInjectionContainer; @@ -29,16 +29,13 @@ declare module "@ogre-tools/injectable-react" { export const DiContextProvider: React.Provider; export const Inject: < - TComponentInjectable extends IComponentInjectable, + TComponentInjectable extends IInjectable, >({ Component, injectableKey, getPlaceholder, ...props - }: Omit< - React.ComponentProps, - keyof ReturnType - > & { + }: Parameters[1] & { injectableKey: TComponentInjectable; getPlaceholder?: () => JSX.Element | null; }) => JSX.Element; diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts index b04cf02740..2f22e592e3 100644 --- a/types/ogre-tools-injectable.d.ts +++ b/types/ogre-tools-injectable.d.ts @@ -48,14 +48,15 @@ declare module "@ogre-tools/injectable" { lifecycle?: lifecycleEnum; } - export interface IInjectable - extends ICommonInjectable { - instantiate: (dependencies: TDependencies) => TInstance; - } - - export interface IComponentInjectable - extends ICommonInjectable { - instantiate: TInstance; + export interface IInjectable< + TInstance, + TDependencies extends object = {}, + TInstantiationParameter extends object = {}, + > extends ICommonInjectable { + instantiate: ( + dependencies: TDependencies, + instantiationParameter: TInstantiationParameter, + ) => TInstance; } export enum lifecycleEnum { diff --git a/yarn.lock b/yarn.lock index c78e8fed10..0b0eda05b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -953,19 +953,19 @@ dependencies: lodash "^4.17.21" -"@ogre-tools/injectable-react@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.0.3.tgz#e30ea455cc4ccf24fbad831b460023492832c2f9" - integrity sha512-CFJeuezdJIZD0o0rp1MnahzItzZ7eSixSo0v0+002kq8T7HZb1ycD8d/zvXOBnkEF34K6GGibXicEw1q46SZzw== +"@ogre-tools/injectable-react@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.1.0.tgz#32757e250b5701c9b467331e99baf9702984106a" + integrity sha512-qsC1Igi55SSiH0/oVuH1QcMFhUouEjhfZG5924PG+hqk2Hzsgi3gN1cnPMTpH8vXJiskNQ0tSrgklPb4w5J8KA== dependencies: "@ogre-tools/fp" "^1.0.2" - "@ogre-tools/injectable" "^1.0.2" + "@ogre-tools/injectable" "^1.1.0" lodash "^4.17.21" -"@ogre-tools/injectable@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.0.2.tgz#b081fefb2026c57fe47e27c268efa73d43934546" - integrity sha512-NZ7FHxKLfr+8o4aL51UQ212w1wD3QIsEN/JNtjtEq0TgLjd2Qo3zSniL4hCEj5M49K2qe3CxEb6ezyf2vFTb5g== +"@ogre-tools/injectable@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.1.0.tgz#095c9d12001d03dd96c98d8986392b0fee05262c" + integrity sha512-ViuFC2iHM86os87W2qubUCAlPYzdxmZwtPBAExb2SLNMHk8Z8dRx9BGDk8AcfCd8aPUw5GHIgf7wxCAMWcyvlw== dependencies: "@ogre-tools/fp" "^1.0.2" lodash "^4.17.21" From d07d9f9fd23e92c73e268c10ca7e99ca979910e2 Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Thu, 18 Nov 2021 15:30:50 +0200 Subject: [PATCH 5/8] Update version of injectable library Signed-off-by: Janne Savolainen --- package.json | 2 +- types/ogre-tools-injectable.d.ts | 16 +++++++++------- yarn.lock | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 09fa5f212a..b7507cf0ad 100644 --- a/package.json +++ b/package.json @@ -189,7 +189,7 @@ "@sentry/electron": "^2.5.4", "@sentry/integrations": "^6.15.0", "@ogre-tools/injectable": "^1.1.0", - "@ogre-tools/injectable-react": "^1.1.0", + "@ogre-tools/injectable-react": "^1.2.0", "abort-controller": "^3.0.0", "auto-bind": "^4.0.0", "autobind-decorator": "^2.4.0", diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts index 2f22e592e3..93d59c37a4 100644 --- a/types/ogre-tools-injectable.d.ts +++ b/types/ogre-tools-injectable.d.ts @@ -42,17 +42,19 @@ declare module "@ogre-tools/injectable" { ) => void; } - interface ICommonInjectable { - id?: string; - getDependencies: (di?: IDependencyInjectionContainer) => TDependencies; - lifecycle?: lifecycleEnum; - } - export interface IInjectable< TInstance, TDependencies extends object = {}, TInstantiationParameter extends object = {}, - > extends ICommonInjectable { + > { + id?: string; + + getDependencies: ( + di?: IDependencyInjectionContainer, + ) => TDependencies | Promise; + + lifecycle?: lifecycleEnum; + instantiate: ( dependencies: TDependencies, instantiationParameter: TInstantiationParameter, diff --git a/yarn.lock b/yarn.lock index 0b0eda05b4..b2f1e93d8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -953,10 +953,10 @@ dependencies: lodash "^4.17.21" -"@ogre-tools/injectable-react@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.1.0.tgz#32757e250b5701c9b467331e99baf9702984106a" - integrity sha512-qsC1Igi55SSiH0/oVuH1QcMFhUouEjhfZG5924PG+hqk2Hzsgi3gN1cnPMTpH8vXJiskNQ0tSrgklPb4w5J8KA== +"@ogre-tools/injectable-react@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.2.0.tgz#1634f702bf017f934d06e4332505c2c4aa0d87d6" + integrity sha512-T4GEuK0HBIHgE5B2WoibDaeQvhn1jgwwsR1K6fy5sYkNnU5Qa4LlciCuxHU7j7dKy6cDVwNWQVg/OXocb0h0lA== dependencies: "@ogre-tools/fp" "^1.0.2" "@ogre-tools/injectable" "^1.1.0" From 0c0dfe596645c95fff49ab821a9988d71759943b Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Fri, 19 Nov 2021 10:13:50 +0200 Subject: [PATCH 6/8] Make overriding injectable with a mock supported Signed-off-by: Janne Savolainen --- types/ogre-tools-injectable.d.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts index 93d59c37a4..7322708ee6 100644 --- a/types/ogre-tools-injectable.d.ts +++ b/types/ogre-tools-injectable.d.ts @@ -38,7 +38,12 @@ declare module "@ogre-tools/injectable" { override: , TInstance>( injectable: TInjectable, - overrider: ReturnType, + overrider: + | ReturnType + | jest.MockInstance< + ReturnType, + ReturnType + >, ) => void; } From c9503c1d14b4f0f43f0966f6d4e3e07e016e751c Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Fri, 19 Nov 2021 14:13:53 +0200 Subject: [PATCH 7/8] Allow asynchronous instances in injectable typing Signed-off-by: Janne Savolainen --- src/renderer/bootstrap.tsx | 4 +-- src/renderer/components/getDi.tsx | 4 +-- .../components/getDiForUnitTesting.tsx | 4 +-- types/ogre-tools-injectable-react.d.ts | 10 +++--- types/ogre-tools-injectable.d.ts | 33 +++++++++++-------- 5 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index bcae5a84a3..edc4ff90f7 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -52,7 +52,7 @@ import { AppPaths } from "../common/app-paths"; import { registerCustomThemes } from "./components/monaco-editor"; import { getDi } from "./components/getDi"; import { DiContextProvider } from "@ogre-tools/injectable-react"; -import type { IDependencyInjectionContainer } from "@ogre-tools/injectable"; +import type { DependencyInjectionContainer } from "@ogre-tools/injectable"; if (process.isMainFrame) { SentryInit(); @@ -76,7 +76,7 @@ type AppComponent = React.ComponentType & { init(rootElem: HTMLElement): Promise; }; -export async function bootstrap(comp: () => Promise, di: IDependencyInjectionContainer) { +export async function bootstrap(comp: () => Promise, di: DependencyInjectionContainer) { const rootElem = document.getElementById("app"); const logPrefix = `[BOOTSTRAP-${process.isMainFrame ? "ROOT" : "CLUSTER"}-FRAME]:`; diff --git a/src/renderer/components/getDi.tsx b/src/renderer/components/getDi.tsx index 745573e6ea..65367a8c77 100644 --- a/src/renderer/components/getDi.tsx +++ b/src/renderer/components/getDi.tsx @@ -20,10 +20,10 @@ */ import { createContainer } from "@ogre-tools/injectable"; -import type { IConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; +import type { ConfigurableDependencyInjectionContainer } from "@ogre-tools/injectable"; export const getDi = () => { - const di: IConfigurableDependencyInjectionContainer = createContainer( + const di: ConfigurableDependencyInjectionContainer = createContainer( () => require.context("./", true, /\.injectable\.(ts|tsx)$/), ); diff --git a/src/renderer/components/getDiForUnitTesting.tsx b/src/renderer/components/getDiForUnitTesting.tsx index 0ce9b6c9b0..4e94d20dae 100644 --- a/src/renderer/components/getDiForUnitTesting.tsx +++ b/src/renderer/components/getDiForUnitTesting.tsx @@ -24,11 +24,11 @@ import { memoize } from "lodash/fp"; import { createContainer, - IConfigurableDependencyInjectionContainer, + ConfigurableDependencyInjectionContainer, } from "@ogre-tools/injectable"; export const getDiForUnitTesting = () => { - const di: IConfigurableDependencyInjectionContainer = createContainer(); + const di: ConfigurableDependencyInjectionContainer = createContainer(); getInjectableFilePaths() .map(key => { diff --git a/types/ogre-tools-injectable-react.d.ts b/types/ogre-tools-injectable-react.d.ts index 38599d909d..27e3fdbdfa 100644 --- a/types/ogre-tools-injectable-react.d.ts +++ b/types/ogre-tools-injectable-react.d.ts @@ -20,16 +20,16 @@ */ /// declare module "@ogre-tools/injectable-react" { - import type { IDependencyInjectionContainer, IInjectable } from "@ogre-tools/injectable"; + import type { DependencyInjectionContainer, Injectable } from "@ogre-tools/injectable"; - interface IDependencyInjectionContainerProviderProps { - di: IDependencyInjectionContainer; + interface DependencyInjectionContainerProviderProps { + di: DependencyInjectionContainer; } - export const DiContextProvider: React.Provider; + export const DiContextProvider: React.Provider; export const Inject: < - TComponentInjectable extends IInjectable, + TComponentInjectable extends Injectable, >({ Component, injectableKey, diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts index 7322708ee6..f8d6a27ebf 100644 --- a/types/ogre-tools-injectable.d.ts +++ b/types/ogre-tools-injectable.d.ts @@ -19,24 +19,29 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ declare module "@ogre-tools/injectable" { - export interface IDependencyInjectionContainer { + type Awaited = TMaybePromise extends PromiseLike + ? TValue + : TMaybePromise; + + export interface DependencyInjectionContainer { inject: < - TInjectable extends IInjectable, + TInjectable extends Injectable, TInstance, TDependencies, + TMaybePromiseInstance = ReturnType, >( injectableKey: TInjectable, - ) => ReturnType; + ) => TMaybePromiseInstance extends PromiseLike + ? Awaited + : TMaybePromiseInstance; } - export interface IConfigurableDependencyInjectionContainer - extends IDependencyInjectionContainer { - register: ( - injectable: IInjectable | IComponentInjectable, - ) => void; + export interface ConfigurableDependencyInjectionContainer + extends DependencyInjectionContainer { + register: (injectable: Injectable) => void; preventSideEffects: () => void; - override: , TInstance>( + override: , TInstance>( injectable: TInjectable, overrider: | ReturnType @@ -47,7 +52,7 @@ declare module "@ogre-tools/injectable" { ) => void; } - export interface IInjectable< + export interface Injectable< TInstance, TDependencies extends object = {}, TInstantiationParameter extends object = {}, @@ -55,7 +60,7 @@ declare module "@ogre-tools/injectable" { id?: string; getDependencies: ( - di?: IDependencyInjectionContainer, + di?: DependencyInjectionContainer, ) => TDependencies | Promise; lifecycle?: lifecycleEnum; @@ -63,7 +68,9 @@ declare module "@ogre-tools/injectable" { instantiate: ( dependencies: TDependencies, instantiationParameter: TInstantiationParameter, - ) => TInstance; + ) => Promise | TInstance; + + causesSideEffects?: boolean; } export enum lifecycleEnum { @@ -74,5 +81,5 @@ declare module "@ogre-tools/injectable" { // eslint-disable-next-line unused-imports/no-unused-vars-ts export const createContainer = (...getRequireContexts: any[]) => - IConfigurableDependencyInjectionContainer; + ConfigurableDependencyInjectionContainer; } From 7b6a7059bf0f4125da48acba1fd35ecb2f1c0d5c Mon Sep 17 00:00:00 2001 From: Janne Savolainen Date: Fri, 19 Nov 2021 15:38:24 +0200 Subject: [PATCH 8/8] Move types of injectable and injectable-react to the library Signed-off-by: Janne Savolainen --- package.json | 4 +- types/ogre-tools-injectable-react.d.ts | 42 ------------- types/ogre-tools-injectable.d.ts | 85 -------------------------- yarn.lock | 18 +++--- 4 files changed, 11 insertions(+), 138 deletions(-) delete mode 100644 types/ogre-tools-injectable-react.d.ts delete mode 100644 types/ogre-tools-injectable.d.ts diff --git a/package.json b/package.json index b7507cf0ad..9cf989ca96 100644 --- a/package.json +++ b/package.json @@ -188,8 +188,8 @@ "@kubernetes/client-node": "^0.15.1", "@sentry/electron": "^2.5.4", "@sentry/integrations": "^6.15.0", - "@ogre-tools/injectable": "^1.1.0", - "@ogre-tools/injectable-react": "^1.2.0", + "@ogre-tools/injectable": "^1.2.1", + "@ogre-tools/injectable-react": "^1.2.1", "abort-controller": "^3.0.0", "auto-bind": "^4.0.0", "autobind-decorator": "^2.4.0", diff --git a/types/ogre-tools-injectable-react.d.ts b/types/ogre-tools-injectable-react.d.ts deleted file mode 100644 index 27e3fdbdfa..0000000000 --- a/types/ogre-tools-injectable-react.d.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -/// -declare module "@ogre-tools/injectable-react" { - import type { DependencyInjectionContainer, Injectable } from "@ogre-tools/injectable"; - - interface DependencyInjectionContainerProviderProps { - di: DependencyInjectionContainer; - } - - export const DiContextProvider: React.Provider; - - export const Inject: < - TComponentInjectable extends Injectable, - >({ - Component, - injectableKey, - getPlaceholder, - ...props - }: Parameters[1] & { - injectableKey: TComponentInjectable; - getPlaceholder?: () => JSX.Element | null; - }) => JSX.Element; -} diff --git a/types/ogre-tools-injectable.d.ts b/types/ogre-tools-injectable.d.ts deleted file mode 100644 index f8d6a27ebf..0000000000 --- a/types/ogre-tools-injectable.d.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Copyright (c) 2021 OpenLens Authors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -declare module "@ogre-tools/injectable" { - type Awaited = TMaybePromise extends PromiseLike - ? TValue - : TMaybePromise; - - export interface DependencyInjectionContainer { - inject: < - TInjectable extends Injectable, - TInstance, - TDependencies, - TMaybePromiseInstance = ReturnType, - >( - injectableKey: TInjectable, - ) => TMaybePromiseInstance extends PromiseLike - ? Awaited - : TMaybePromiseInstance; - } - - export interface ConfigurableDependencyInjectionContainer - extends DependencyInjectionContainer { - register: (injectable: Injectable) => void; - preventSideEffects: () => void; - - override: , TInstance>( - injectable: TInjectable, - overrider: - | ReturnType - | jest.MockInstance< - ReturnType, - ReturnType - >, - ) => void; - } - - export interface Injectable< - TInstance, - TDependencies extends object = {}, - TInstantiationParameter extends object = {}, - > { - id?: string; - - getDependencies: ( - di?: DependencyInjectionContainer, - ) => TDependencies | Promise; - - lifecycle?: lifecycleEnum; - - instantiate: ( - dependencies: TDependencies, - instantiationParameter: TInstantiationParameter, - ) => Promise | TInstance; - - causesSideEffects?: boolean; - } - - export enum lifecycleEnum { - singleton, - transient, - scopedTransient, - } - - // eslint-disable-next-line unused-imports/no-unused-vars-ts - export const createContainer = (...getRequireContexts: any[]) => - ConfigurableDependencyInjectionContainer; -} diff --git a/yarn.lock b/yarn.lock index b2f1e93d8f..4be6fb2c11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -953,19 +953,19 @@ dependencies: lodash "^4.17.21" -"@ogre-tools/injectable-react@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.2.0.tgz#1634f702bf017f934d06e4332505c2c4aa0d87d6" - integrity sha512-T4GEuK0HBIHgE5B2WoibDaeQvhn1jgwwsR1K6fy5sYkNnU5Qa4LlciCuxHU7j7dKy6cDVwNWQVg/OXocb0h0lA== +"@ogre-tools/injectable-react@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable-react/-/injectable-react-1.2.1.tgz#886fbb9f9816d68daf41b6fd7ff5def6eae833b4" + integrity sha512-kr9Q2T/VyhtUG8EbfzpFPk2ndwKQl9WHzqEfp8fasAXMNmUfUnyWs6iPNoJiuy2gh4/CNBvlFB8c647ls6/jUA== dependencies: "@ogre-tools/fp" "^1.0.2" - "@ogre-tools/injectable" "^1.1.0" + "@ogre-tools/injectable" "^1.2.1" lodash "^4.17.21" -"@ogre-tools/injectable@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.1.0.tgz#095c9d12001d03dd96c98d8986392b0fee05262c" - integrity sha512-ViuFC2iHM86os87W2qubUCAlPYzdxmZwtPBAExb2SLNMHk8Z8dRx9BGDk8AcfCd8aPUw5GHIgf7wxCAMWcyvlw== +"@ogre-tools/injectable@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@ogre-tools/injectable/-/injectable-1.2.1.tgz#f3eb481806dd6e53af8d9d37f8b20f3c0d875a60" + integrity sha512-bfTlnT08uDydE0i5GxJ9SIoRKfNYVabQRrZfBraZi2rs3zx+DOpcZrJjhjDoSCzIr6C2azySuyxn1h8x8CMUPw== dependencies: "@ogre-tools/fp" "^1.0.2" lodash "^4.17.21"