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

Properly handle loading custom entity in cluster-frame

Signed-off-by: Panu Horsmalahti <phorsmalahti@mirantis.com>
This commit is contained in:
Panu Horsmalahti 2022-01-18 17:32:43 +02:00
parent 11386c0da5
commit 95a5307fcd
4 changed files with 62 additions and 27 deletions

View File

@ -300,11 +300,16 @@ export class ExtensionLoader {
});
}
protected autoInitExtensions(register: (ext: LensExtension) => Promise<Disposer[]>) {
const loadingExtensions: ExtensionLoading[] = [];
protected async loadExtensions(installedExtensions: Map<string, InstalledExtension>, register: (ext: LensExtension) => Promise<Disposer[]>) {
// Steps of the function:
// 1. require and call .activate for each Extension
// 2. Wait until every extension's onActivate has been resolved
// 3. Call .enable for each extension
// 4. Return ExtensionLoading[]
reaction(() => this.toJSON(), async installedExtensions => {
for (const [extId, extension] of installedExtensions) {
const extensions = [...installedExtensions.entries()]
.map(([extId, extension]) => {
// for (const [extId, extension] of installedExtensions) {
const alreadyInit = this.instances.has(extId) || this.nonInstancesByName.has(extension.manifest.name);
if (extension.isCompatible && extension.isEnabled && !alreadyInit) {
@ -313,7 +318,8 @@ export class ExtensionLoader {
if (!LensExtensionClass) {
this.nonInstancesByName.add(extension.manifest.name);
continue;
return null;
}
const instance = this.dependencies.createExtensionInstance(
@ -321,27 +327,49 @@ export class ExtensionLoader {
extension,
);
const loaded = instance.enable(register).catch((err) => {
logger.error(`${logModule}: failed to enable`, { ext: extension, err });
});
loadingExtensions.push({
return {
extId,
instance,
isBundled: extension.isBundled,
loaded,
});
this.instances.set(extId, instance);
activated: instance.activate()
};
} catch (err) {
logger.error(`${logModule}: activation extension error`, { ext: extension, err });
}
} else if (!extension.isEnabled && alreadyInit) {
this.removeInstance(extId);
}
}
}, {
fireImmediately: true,
});
return loadingExtensions;
return null;
})
// Remove null values
.filter(extension => Boolean(extension));
// We first need to wait until each extension's `onActivate` is resolved,
// as this might register new catalog categories. Afterwards we can safely .enable the extension.
await Promise.all(extensions.map(extension => extension.activated));
// Return ExtensionLoading[]
return extensions.map(extension => {
const loaded = extension.instance.enable(register).catch((err) => {
logger.error(`${logModule}: failed to enable`, { ext: extension, err });
});
this.instances.set(extension.extId, extension.instance);
return {
isBundled: extension.isBundled,
loaded,
};
});
}
protected autoInitExtensions(register: (ext: LensExtension) => Promise<Disposer[]>) {
// Setup reaction to load extensions on JSON changes
reaction(() => this.toJSON(), installedExtensions => this.loadExtensions(installedExtensions, register));
// Load initial extensions
return this.loadExtensions(this.toJSON(), register);
}
protected requireExtension(extension: InstalledExtension): LensExtensionConstructor | null {

View File

@ -86,7 +86,6 @@ export class LensExtension {
}
try {
await this.onActivate();
this._isEnabled = true;
this[Disposers].push(...await register(this));
@ -113,6 +112,11 @@ export class LensExtension {
}
}
@action
activate() {
return this.onActivate();
}
protected onActivate(): Promise<void> | void {
return;
}

View File

@ -12,16 +12,19 @@ import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bu
import clusterFrameContextInjectable from "../../../cluster-frame-context/cluster-frame-context.injectable";
const initClusterFrameInjectable = getInjectable({
instantiate: (di) =>
initClusterFrame({
instantiate: (di) => {
const extensionLoader = di.inject(extensionLoaderInjectable);
return initClusterFrame({
hostedCluster: di.inject(hostedClusterInjectable),
loadExtensions: di.inject(extensionLoaderInjectable).loadOnClusterRenderer,
loadExtensions: extensionLoader.loadOnClusterRenderer.bind(extensionLoader),
catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable),
frameRoutingId: di.inject(frameRoutingIdInjectable),
emitEvent: di.inject(appEventBusInjectable).emit,
clusterFrameContext: di.inject(clusterFrameContextInjectable),
}),
});
},
lifecycle: lifecycleEnum.singleton,
});

View File

@ -11,15 +11,15 @@ import type { ExtensionLoading } from "../../../../extensions/extension-loader";
import type { CatalogEntityRegistry } from "../../../api/catalog-entity-registry";
interface Dependencies {
loadExtensions: () => ExtensionLoading[]
loadExtensions: () => Promise<ExtensionLoading[]>;
// TODO: Move usages of third party library behind abstraction
ipcRenderer: { send: (name: string) => void }
ipcRenderer: { send: (name: string) => void };
// TODO: Remove dependencies being here only for correct timing of initialization
bindProtocolAddRouteHandlers: () => void;
lensProtocolRouterRenderer: { init: () => void };
catalogEntityRegistry: CatalogEntityRegistry
catalogEntityRegistry: CatalogEntityRegistry;
}
const logPrefix = "[ROOT-FRAME]:";
@ -40,7 +40,7 @@ export const initRootFrame =
// maximum time to let bundled extensions finish loading
const timeout = delay(10000);
const loadingExtensions = loadExtensions();
const loadingExtensions = await loadExtensions();
const loadingBundledExtensions = loadingExtensions
.filter((e) => e.isBundled)