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[]>) { protected async loadExtensions(installedExtensions: Map<string, InstalledExtension>, register: (ext: LensExtension) => Promise<Disposer[]>) {
const loadingExtensions: ExtensionLoading[] = []; // 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 => { const extensions = [...installedExtensions.entries()]
for (const [extId, extension] of installedExtensions) { .map(([extId, extension]) => {
// for (const [extId, extension] of installedExtensions) {
const alreadyInit = this.instances.has(extId) || this.nonInstancesByName.has(extension.manifest.name); const alreadyInit = this.instances.has(extId) || this.nonInstancesByName.has(extension.manifest.name);
if (extension.isCompatible && extension.isEnabled && !alreadyInit) { if (extension.isCompatible && extension.isEnabled && !alreadyInit) {
@ -313,7 +318,8 @@ export class ExtensionLoader {
if (!LensExtensionClass) { if (!LensExtensionClass) {
this.nonInstancesByName.add(extension.manifest.name); this.nonInstancesByName.add(extension.manifest.name);
continue;
return null;
} }
const instance = this.dependencies.createExtensionInstance( const instance = this.dependencies.createExtensionInstance(
@ -321,27 +327,49 @@ export class ExtensionLoader {
extension, extension,
); );
const loaded = instance.enable(register).catch((err) => { return {
logger.error(`${logModule}: failed to enable`, { ext: extension, err }); extId,
}); instance,
loadingExtensions.push({
isBundled: extension.isBundled, isBundled: extension.isBundled,
loaded, activated: instance.activate()
}); };
this.instances.set(extId, instance);
} catch (err) { } catch (err) {
logger.error(`${logModule}: activation extension error`, { ext: extension, err }); logger.error(`${logModule}: activation extension error`, { ext: extension, err });
} }
} else if (!extension.isEnabled && alreadyInit) { } else if (!extension.isEnabled && alreadyInit) {
this.removeInstance(extId); 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 { protected requireExtension(extension: InstalledExtension): LensExtensionConstructor | null {

View File

@ -86,7 +86,6 @@ export class LensExtension {
} }
try { try {
await this.onActivate();
this._isEnabled = true; this._isEnabled = true;
this[Disposers].push(...await register(this)); this[Disposers].push(...await register(this));
@ -113,6 +112,11 @@ export class LensExtension {
} }
} }
@action
activate() {
return this.onActivate();
}
protected onActivate(): Promise<void> | void { protected onActivate(): Promise<void> | void {
return; 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"; import clusterFrameContextInjectable from "../../../cluster-frame-context/cluster-frame-context.injectable";
const initClusterFrameInjectable = getInjectable({ const initClusterFrameInjectable = getInjectable({
instantiate: (di) => instantiate: (di) => {
initClusterFrame({ const extensionLoader = di.inject(extensionLoaderInjectable);
return initClusterFrame({
hostedCluster: di.inject(hostedClusterInjectable), hostedCluster: di.inject(hostedClusterInjectable),
loadExtensions: di.inject(extensionLoaderInjectable).loadOnClusterRenderer, loadExtensions: extensionLoader.loadOnClusterRenderer.bind(extensionLoader),
catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable),
frameRoutingId: di.inject(frameRoutingIdInjectable), frameRoutingId: di.inject(frameRoutingIdInjectable),
emitEvent: di.inject(appEventBusInjectable).emit, emitEvent: di.inject(appEventBusInjectable).emit,
clusterFrameContext: di.inject(clusterFrameContextInjectable), clusterFrameContext: di.inject(clusterFrameContextInjectable),
}), });
},
lifecycle: lifecycleEnum.singleton, lifecycle: lifecycleEnum.singleton,
}); });

View File

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