mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introduce clearer boundry between extensions
- Bundled extensions are always enabled, and are always compatible - Have bundled extensions be loaded asyncronously to support typescript dynamic import (which is typed) as opposed to require Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
6df01ba468
commit
e3c0bc34fd
26 files changed
+231
-278
No files matched your search
@@ -24,7 +24,7 @@ const createExtensionInstanceInjectable = getInjectable({
|
||||
};
|
||||
|
||||
return (ExtensionClass, extension) => {
|
||||
const instance = new ExtensionClass(extension) as LensMainExtension;
|
||||
const instance = new ExtensionClass(extension as any) as LensMainExtension;
|
||||
|
||||
(instance as Writable<LensMainExtension>)[lensExtensionDependencies] = deps;
|
||||
|
||||
|
||||
@@ -6,23 +6,18 @@
|
||||
import * as uuid from "uuid";
|
||||
|
||||
import { ProtocolHandlerExtension, ProtocolHandlerInternal, ProtocolHandlerInvalid } from "../../../common/protocol-handler";
|
||||
import { delay, noop } from "../../../common/utils";
|
||||
import type { ExtensionsStore, IsEnabledExtensionDescriptor } from "../../../extensions/extensions-store/extensions-store";
|
||||
import { noop } from "../../../common/utils";
|
||||
import type { LensProtocolRouterMain } from "../lens-protocol-router-main/lens-protocol-router-main";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import lensProtocolRouterMainInjectable from "../lens-protocol-router-main/lens-protocol-router-main.injectable";
|
||||
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
|
||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
||||
import { LensExtension } from "../../../extensions/lens-extension";
|
||||
import type { LensExtensionId } from "../../../extensions/lens-extension";
|
||||
import type { ObservableMap } from "mobx";
|
||||
import { runInAction } from "mobx";
|
||||
import extensionInstancesInjectable from "../../../extensions/extension-loader/extension-instances.injectable";
|
||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||
import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable";
|
||||
import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable";
|
||||
import pathExistsInjectable from "../../../common/fs/path-exists.injectable";
|
||||
import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable";
|
||||
import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable";
|
||||
|
||||
function throwIfDefined(val: any): void {
|
||||
if (val != null) {
|
||||
@@ -39,20 +34,13 @@ describe("protocol router tests", () => {
|
||||
beforeEach(async () => {
|
||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||
|
||||
di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); });
|
||||
di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); });
|
||||
di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); });
|
||||
di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); });
|
||||
|
||||
enabledExtensions = new Set();
|
||||
|
||||
di.override(extensionsStoreInjectable, () => ({
|
||||
isEnabled: ({ id, isBundled }: IsEnabledExtensionDescriptor) => isBundled || enabledExtensions.has(id),
|
||||
} as unknown as ExtensionsStore));
|
||||
isEnabled: (id) => enabledExtensions.has(id),
|
||||
}));
|
||||
|
||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||
|
||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
||||
|
||||
broadcastMessageMock = jest.fn();
|
||||
di.override(broadcastMessageInjectable, () => broadcastMessageMock);
|
||||
@@ -60,7 +48,9 @@ describe("protocol router tests", () => {
|
||||
extensionInstances = di.inject(extensionInstancesInjectable);
|
||||
lpr = di.inject(lensProtocolRouterMainInjectable);
|
||||
|
||||
lpr.rendererLoaded = true;
|
||||
runInAction(() => {
|
||||
lpr.rendererLoaded.set(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should broadcast invalid protocol on non-lens URLs", async () => {
|
||||
@@ -73,7 +63,19 @@ describe("protocol router tests", () => {
|
||||
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInvalid, "invalid host", "lens://foobar");
|
||||
});
|
||||
|
||||
it("should not throw when has valid host", async () => {
|
||||
it("should broadcast internal route when called with valid host", async () => {
|
||||
lpr.addInternalHandler("/", noop);
|
||||
|
||||
try {
|
||||
expect(await lpr.route("lens://app")).toBeUndefined();
|
||||
} catch (error) {
|
||||
expect(throwIfDefined(error)).not.toThrow();
|
||||
}
|
||||
|
||||
expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched");
|
||||
});
|
||||
|
||||
it("should broadcast external route when called with valid host", async () => {
|
||||
const extId = uuid.v4();
|
||||
const ext = new LensExtension({
|
||||
id: extId,
|
||||
@@ -97,22 +99,12 @@ describe("protocol router tests", () => {
|
||||
extensionInstances.set(extId, ext);
|
||||
enabledExtensions.add(extId);
|
||||
|
||||
lpr.addInternalHandler("/", noop);
|
||||
|
||||
try {
|
||||
expect(await lpr.route("lens://app")).toBeUndefined();
|
||||
} catch (error) {
|
||||
expect(throwIfDefined(error)).not.toThrow();
|
||||
}
|
||||
|
||||
try {
|
||||
expect(await lpr.route("lens://extension/@mirantis/minikube")).toBeUndefined();
|
||||
} catch (error) {
|
||||
expect(throwIfDefined(error)).not.toThrow();
|
||||
}
|
||||
|
||||
await delay(50);
|
||||
expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched");
|
||||
expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerExtension, "lens://extension/@mirantis/minikube", "matched");
|
||||
});
|
||||
|
||||
@@ -183,7 +175,6 @@ describe("protocol router tests", () => {
|
||||
expect(throwIfDefined(error)).not.toThrow();
|
||||
}
|
||||
|
||||
await delay(50);
|
||||
expect(called).toBe("foob");
|
||||
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerExtension, "lens://extension/@foobar/icecream/page/foob", "matched");
|
||||
});
|
||||
@@ -252,7 +243,6 @@ describe("protocol router tests", () => {
|
||||
expect(throwIfDefined(error)).not.toThrow();
|
||||
}
|
||||
|
||||
await delay(50);
|
||||
|
||||
expect(called).toBe(1);
|
||||
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerExtension, "lens://extension/icecream/page", "matched");
|
||||
|
||||
+16
-8
@@ -6,7 +6,7 @@
|
||||
import * as proto from "../../../common/protocol-handler";
|
||||
import URLParse from "url-parse";
|
||||
import type { LensExtension } from "../../../extensions/lens-extension";
|
||||
import { observable, when, makeObservable } from "mobx";
|
||||
import { observable, when } from "mobx";
|
||||
import type { LensProtocolRouterDependencies, RouteAttempt } from "../../../common/protocol-handler";
|
||||
import { ProtocolHandlerInvalid } from "../../../common/protocol-handler";
|
||||
import { disposer, noop } from "../../../common/utils";
|
||||
@@ -39,17 +39,15 @@ export interface LensProtocolRouterMainDependencies extends LensProtocolRouterDe
|
||||
}
|
||||
|
||||
export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
private missingExtensionHandlers: FallbackHandler[] = [];
|
||||
private readonly missingExtensionHandlers: FallbackHandler[] = [];
|
||||
|
||||
// TODO: This is used to solve out-of-place temporal dependency. Remove, and solve otherwise.
|
||||
@observable rendererLoaded = false;
|
||||
readonly rendererLoaded = observable.box(false);
|
||||
|
||||
protected disposers = disposer();
|
||||
protected readonly disposers = disposer();
|
||||
|
||||
constructor(protected readonly dependencies: LensProtocolRouterMainDependencies) {
|
||||
super(dependencies);
|
||||
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
public cleanup() {
|
||||
@@ -118,8 +116,13 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
protected _routeToInternal(url: URLParse<Record<string, string | undefined>>): RouteAttempt {
|
||||
const rawUrl = url.toString(); // for sending to renderer
|
||||
const attempt = super._routeToInternal(url);
|
||||
const broadcastToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerInternal, rawUrl, attempt);
|
||||
|
||||
this.disposers.push(when(() => this.rendererLoaded, () => this.dependencies.broadcastMessage(proto.ProtocolHandlerInternal, rawUrl, attempt)));
|
||||
if (this.rendererLoaded.get()) {
|
||||
broadcastToRenderer();
|
||||
} else {
|
||||
this.disposers.push(when(() => this.rendererLoaded.get(), broadcastToRenderer));
|
||||
}
|
||||
|
||||
return attempt;
|
||||
}
|
||||
@@ -135,8 +138,13 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
|
||||
* argument.
|
||||
*/
|
||||
const attempt = await super._routeToExtension(new URLParse(url.toString(), true));
|
||||
const broadcastToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt);
|
||||
|
||||
this.disposers.push(when(() => this.rendererLoaded, () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt)));
|
||||
if (this.rendererLoaded.get()) {
|
||||
broadcastToRenderer();
|
||||
} else {
|
||||
this.disposers.push(when(() => this.rendererLoaded.get(), broadcastToRenderer));
|
||||
}
|
||||
|
||||
return attempt;
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ const flagRendererAsLoadedInjectable = getInjectable({
|
||||
run: () => {
|
||||
runInAction(() => {
|
||||
// Todo: remove this kludge which enables out-of-place temporal dependency.
|
||||
lensProtocolRouterMain.rendererLoaded = true;
|
||||
lensProtocolRouterMain.rendererLoaded.set(true);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ const flagRendererAsNotLoadedInjectable = getInjectable({
|
||||
run: () => {
|
||||
runInAction(() => {
|
||||
// Todo: remove this kludge which enables out-of-place temporal dependency.
|
||||
lensProtocolRouterMain.rendererLoaded = false;
|
||||
lensProtocolRouterMain.rendererLoaded.set(false);
|
||||
});
|
||||
|
||||
return undefined;
|
||||
|
||||
Reference in new issue
Block a user