Simplify logic for injecting many instances which can reactively change e.g. based on extension being enabled or disabled (#5662)

* Reorganize stuff to prevent circular dependencies

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Install injectable extension to allow injecting reactively many implementations

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using computedInjectMany over filtering injectMany based on enabled extensions for simplicity

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove await for not being needed

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Sort dependencies alphabetically

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen authored and GitHub committed 2022-06-20 10:19:20 +03:00
1 parent d0ed4e46b1
commit f87f5f245c
35 files changed
+245 -333

No files matched your search

@@ -8,10 +8,10 @@ import crdListRouteInjectable from "../../../common/front-end-routing/routes/clu
import customResourceDefinitionsInjectable from "./custom-resources.injectable";
import { groupBy, matches, noop, some, toPairs } from "lodash/fp";
import customResourcesRouteInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/custom-resources-route.injectable";
import currentPathParametersInjectable from "../../routes/current-path-parameters.injectable";
import type { SidebarItemRegistration } from "../layout/sidebar-items.injectable";
import routeIsActiveInjectable from "../../routes/route-is-active.injectable";
import navigateToCustomResourcesInjectable from "../../../common/front-end-routing/routes/cluster/custom-resources/custom-resources/navigate-to-custom-resources.injectable";
import routePathParametersInjectable from "../../routes/route-path-parameters.injectable";
const sidebarItemsForDefinitionGroupsInjectable = getInjectable({
id: "sidebar-items-for-definition-groups",
@@ -24,7 +24,7 @@ const sidebarItemsForDefinitionGroupsInjectable = getInjectable({
const crdRoute = di.inject(customResourcesRouteInjectable);
const crdRouteIsActive = di.inject(routeIsActiveInjectable, crdRoute);
const crdListRoute = di.inject(crdListRouteInjectable);
const pathParameters = di.inject(currentPathParametersInjectable);
const pathParameters = di.inject(routePathParametersInjectable, crdRoute);
const navigateToCustomResources = di.inject(navigateToCustomResourcesInjectable);
return computed((): SidebarItemRegistration[] => {
@@ -13,10 +13,11 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({
id: "extension-preference-item-registrator",
instantiate:
(di) =>
(ext, extensionInstallationCount) => {
() =>
(ext) => {
const extension = ext as LensRendererExtension;
const injectables = pipeline(
return pipeline(
extension.appPreferences,
filter(
@@ -27,13 +28,12 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({
const id = `extension-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`;
return getInjectable({
id: `${id}-for-instance-${extensionInstallationCount}`,
id,
injectionToken: extensionPreferenceItemInjectionToken,
instantiate: () => ({
id: registration.id || id,
title: registration.title,
extension,
components: {
Hint: registration.components.Hint,
@@ -43,10 +43,6 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({
});
}),
);
di.register(...injectables);
return;
},
injectionToken: extensionRegistratorInjectionToken,
@@ -2,19 +2,11 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { pipeline } from "@ogre-tools/fp";
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import { filter, overSome } from "lodash/fp";
import { computed } from "mobx";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import type { RegisteredAppPreference } from "./app-preferences/app-preference-registration";
interface ExtensionPreferenceItem extends RegisteredAppPreference {
extension: LensRendererExtension;
}
export const extensionPreferenceItemInjectionToken = getInjectionToken<ExtensionPreferenceItem>({
export const extensionPreferenceItemInjectionToken = getInjectionToken<RegisteredAppPreference>({
id: "extension-preference-item-injection-token",
});
@@ -22,29 +14,10 @@ const extensionsPreferenceItemsInjectable = getInjectable({
id: "extension-preference-items",
instantiate: (di) => {
const extensions = di.inject(rendererExtensionsInjectable);
const computedInjectMany = di.inject(computedInjectManyInjectable);
return computed(() => {
const enabledExtensions = extensions.get();
return pipeline(
di.injectMany(extensionPreferenceItemInjectionToken),
filter((item) =>
overSome([
isNonExtensionItem,
isEnabledExtensionItemFor(enabledExtensions),
])(item),
),
);
});
return computedInjectMany(extensionPreferenceItemInjectionToken);
},
});
const isNonExtensionItem = (item: ExtensionPreferenceItem) => !item.extension;
const isEnabledExtensionItemFor =
(enabledExtensions: LensRendererExtension[]) => (item: ExtensionPreferenceItem) =>
!!enabledExtensions.find((extension) => extension === item.extension);
export default extensionsPreferenceItemsInjectable;
@@ -13,10 +13,11 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({
id: "extension-telemetry-preference-item-registrator",
instantiate:
(di) =>
(ext, extensionInstallationCount) => {
() =>
(ext) => {
const extension = ext as LensRendererExtension;
const injectables = pipeline(
return pipeline(
extension.appPreferences,
filter(
@@ -27,13 +28,12 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({
const id = `telemetry-preferences-item-${registration.id}-for-extension-${extension.sanitizedExtensionId}`;
return getInjectable({
id: `${id}-for-instance-${extensionInstallationCount}`,
id,
injectionToken: telemetryPreferenceItemInjectionToken,
instantiate: () => ({
id: registration.id || id,
title: registration.title,
extension,
components: {
Hint: registration.components.Hint,
@@ -43,10 +43,6 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({
});
}),
);
di.register(...injectables);
return;
},
injectionToken: extensionRegistratorInjectionToken,
@@ -3,48 +3,22 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
import { pipeline } from "@ogre-tools/fp";
import { filter, overSome } from "lodash/fp";
import { computed } from "mobx";
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
export interface ExtensionTelemetryPreferenceRegistration extends AppPreferenceRegistration {
extension?: LensRendererExtension;
}
export const telemetryPreferenceItemInjectionToken = getInjectionToken<ExtensionTelemetryPreferenceRegistration>({
export const telemetryPreferenceItemInjectionToken = getInjectionToken<AppPreferenceRegistration>({
id: "telemetry-preference-item-injection-token",
});
const telemetryPreferenceItemsInjectable = getInjectable({
id: "telemetry-preference-items",
instantiate: (di) => {
const extensions = di.inject(rendererExtensionsInjectable);
const computedInjectMany = di.inject(computedInjectManyInjectable);
return computed(() => {
const enabledExtensions = extensions.get();
return pipeline(
di.injectMany(telemetryPreferenceItemInjectionToken),
filter((item) =>
overSome([
isNonExtensionItem,
isEnabledExtensionItemFor(enabledExtensions),
])(item),
),
);
});
return computedInjectMany(telemetryPreferenceItemInjectionToken);
},
});
const isNonExtensionItem = (item: ExtensionTelemetryPreferenceRegistration) => !item.extension;
const isEnabledExtensionItemFor =
(enabledExtensions: LensRendererExtension[]) => (item: ExtensionTelemetryPreferenceRegistration) =>
!!enabledExtensions.find((extension) => extension === item.extension);
export default telemetryPreferenceItemsInjectable;
@@ -11,13 +11,13 @@ import { ExtensionSettings } from "./extension-settings";
import type { IComputedValue } from "mobx";
import { withInjectables } from "@ogre-tools/injectable-react";
import { Preferences } from "./preferences";
import type { ExtensionTelemetryPreferenceRegistration } from "./telemetry-preference-items.injectable";
import telemetryPreferenceItemsInjectable from "./telemetry-preference-items.injectable";
import sentryDnsUrlInjectable from "./sentry-dns-url.injectable";
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
import type { AppPreferenceRegistration } from "./app-preferences/app-preference-registration";
interface Dependencies {
telemetryPreferenceItems: IComputedValue<ExtensionTelemetryPreferenceRegistration[]>;
telemetryPreferenceItems: IComputedValue<AppPreferenceRegistration[]>;
sentryDnsUrl: string;
userStore: UserStore;
}
@@ -18,17 +18,16 @@ import type { LensRendererExtension } from "../../../extensions/lens-renderer-ex
const extensionSidebarItemRegistratorInjectable = getInjectable({
id: "extension-sidebar-item-registrator",
instantiate: (di) => (ext, extensionInstallationCount) => {
instantiate: (di) => (ext) => {
const extension = ext as LensRendererExtension;
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
const routes = di.inject(routesInjectable);
const sidebarItemsForExtensionInjectable = getInjectable({
id: `sidebar-items-for-extension-${extension.sanitizedExtensionId}-instance-${extensionInstallationCount}`,
id: `sidebar-items-for-extension-${extension.sanitizedExtensionId}`,
injectionToken: sidebarItemsInjectionToken,
instantiate: (di) => {
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
const routes = di.inject(routesInjectable);
return computed(() => {
const extensionRoutes = routes.get().filter(matches({ extension }));
@@ -44,7 +43,6 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({
const res: SidebarItemRegistration = {
id: `${extension.sanitizedExtensionId}-${registration.id}`,
extension,
orderNumber: 9999,
parentId: registration.parentId
@@ -73,7 +71,9 @@ const extensionSidebarItemRegistratorInjectable = getInjectable({
},
});
di.register(sidebarItemsForExtensionInjectable);
return [
sidebarItemsForExtensionInjectable,
];
},
injectionToken: extensionRegistratorInjectionToken,
@@ -6,7 +6,7 @@ import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import type { IComputedValue } from "mobx";
import { computed } from "mobx";
import { pipeline } from "@ogre-tools/fp";
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import {
filter,
flatMap,
@@ -15,11 +15,8 @@ import {
isEmpty,
map,
orderBy,
overSome,
some,
} from "lodash/fp";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
export interface SidebarItemRegistration {
id: string;
@@ -30,7 +27,6 @@ export interface SidebarItemRegistration {
isActive?: IComputedValue<boolean>;
isVisible?: IComputedValue<boolean>;
orderNumber: number;
extension?: LensRendererExtension;
}
export const sidebarItemsInjectionToken = getInjectionToken<
@@ -47,25 +43,14 @@ const sidebarItemsInjectable = getInjectable({
id: "sidebar-items",
instantiate: (di) => {
const extensions = di.inject(rendererExtensionsInjectable);
const computedInjectMany = di.inject(computedInjectManyInjectable);
const sidebarItemRegistrations = computedInjectMany(sidebarItemsInjectionToken);
return computed((): HierarchicalSidebarItem[] => {
const enabledExtensions = extensions.get();
const sidebarItemRegistrations = di.injectMany(
sidebarItemsInjectionToken,
);
const registrations = pipeline(
sidebarItemRegistrations,
sidebarItemRegistrations.get(),
flatMap(dereference),
filter(
overSome([
isNonExtensionSidebarItem,
isEnabledExtensionSidebarItemFor(enabledExtensions),
]),
),
);
const getSidebarItemsHierarchy = (registrations: SidebarItemRegistration[]) => {
@@ -114,14 +99,6 @@ const sidebarItemsInjectable = getInjectable({
},
});
const isNonExtensionSidebarItem = (sidebarItem: SidebarItemRegistration) =>
!sidebarItem.extension;
const isEnabledExtensionSidebarItemFor =
(enabledExtensions: LensRendererExtension[]) =>
(sidebarItem: SidebarItemRegistration) =>
!!enabledExtensions.find((x) => x === sidebarItem.extension);
const dereference = (items: IComputedValue<SidebarItemRegistration[]>) =>
items.get();
@@ -5,7 +5,6 @@
import type { LensRendererExtension } from "../../../extensions/lens-renderer-extension";
import rendererExtensionsInjectable from "../../../extensions/renderer-extensions.injectable";
import currentlyInClusterFrameInjectable from "../../routes/currently-in-cluster-frame.injectable";
import { extensionRegistratorInjectionToken } from "../../../extensions/extension-loader/extension-registrator-injection-token";
import type { IObservableArray, ObservableSet } from "mobx";
import { computed, observable, runInAction } from "mobx";
import { renderFor } from "./renderFor";
@@ -60,9 +59,11 @@ import type { LensMainExtension } from "../../../extensions/lens-main-extension"
import trayMenuItemsInjectable from "../../../main/tray/tray-menu-item/tray-menu-items.injectable";
import type { LensExtension } from "../../../extensions/lens-extension";
import extensionInjectable from "../../../extensions/extension-loader/extension/extension.injectable";
type Callback = (dis: DiContainers) => void | Promise<void>;
type EnableExtensions<T> = (...extensions: T[]) => Promise<void>;
type EnableExtensions<T> = (...extensions: T[]) => void;
type DisableExtensions<T> = (...extensions: T[]) => void;
export interface ApplicationBuilder {
@@ -222,33 +223,26 @@ export const getApplicationBuilder = () => {
extensionState: T,
di: DiContainer,
) => {
let index = 0;
const getExtension = (extension: LensExtension) =>
di.inject(extensionInjectable, extension);
return async (...extensions: LensExtension[]) => {
const extensionRegistrators = di.injectMany(
extensionRegistratorInjectionToken,
);
return (...extensionInstances: LensExtension[]) => {
const addAndEnableExtensions = () => {
extensionInstances.forEach(instance => {
const extension = getExtension(instance);
const addAndEnableExtensions = async () => {
index++;
const registratorPromises = extensions.flatMap((extension) =>
extensionRegistrators.map((registrator) =>
registrator(extension, index),
),
);
await Promise.all(registratorPromises);
extension.register();
});
runInAction(() => {
extensions.forEach((extension) => {
extensionInstances.forEach((extension) => {
extensionState.add(extension);
});
});
};
if (rendered) {
await addAndEnableExtensions();
addAndEnableExtensions();
} else {
builder.beforeRender(addAndEnableExtensions);
}
@@ -407,12 +401,12 @@ export const getApplicationBuilder = () => {
extensions: {
renderer: {
enable: enableExtensionsFor(rendererExtensionsState, rendererDi),
disable: disableExtensionsFor(rendererExtensionsState),
disable: disableExtensionsFor(rendererExtensionsState, rendererDi),
},
main: {
enable: enableExtensionsFor(mainExtensionsState, mainDi),
disable: disableExtensionsFor(mainExtensionsState),
disable: disableExtensionsFor(mainExtensionsState, mainDi),
},
},
@@ -538,13 +532,22 @@ function toFlatChildren(parentId: string | null | undefined): ToFlatChildren {
];
}
const disableExtensionsFor =
<T extends ObservableSet>(extensionState: T) =>
const disableExtensionsFor = <T extends ObservableSet>(
extensionState: T,
di: DiContainer,
) => {
const getExtension = (instance: LensExtension) =>
di.inject(extensionInjectable, instance);
(...extensions: LensExtension[]) => {
extensions.forEach((extension) => {
runInAction(() => {
extensionState.delete(extension);
});
return (...extensionInstances: LensExtension[]) => {
extensionInstances.forEach((instance) => {
const extension = getExtension(instance);
extension.deregister();
runInAction(() => {
extensionState.delete(extension);
});
};
});
};
};