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

Cleanup more type errors

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-02-21 09:27:13 -05:00
parent db9472de36
commit e946194582
18 changed files with 404 additions and 186 deletions

View File

@ -12,6 +12,7 @@ import { RoutingError, RoutingErrorType } from "./error";
import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store";
import type { LensExtension } from "../../extensions/lens-extension";
import type { RouteHandler, RouteParams } from "./registration";
import type { IComputedValue } from "mobx";
import { when } from "mobx";
import type { Logger } from "../logger";
import type { FindExtensionInstanceByName } from "../../features/extensions/loader/common/find-instance-by-name.injectable";
@ -61,13 +62,13 @@ export function foldAttemptResults(mainAttempt: RouteAttempt, rendererAttempt: R
export interface LensProtocolRouterDependencies {
readonly extensionsStore: ExtensionsStore;
readonly logger: Logger;
readonly internalRoutes: Map<string, RouteHandler>;
readonly internalRoutes: IComputedValue<Map<string, RouteHandler>>;
findExtensionInstanceByName: FindExtensionInstanceByName;
}
export const extensionUrlDeepLinkingSchema = `/:${EXTENSION_PUBLISHER_MATCH}(@[A-Za-z0-9_]+)?/:${EXTENSION_NAME_MATCH}`;
export abstract class LensProtocolRouter {
export class LensProtocolRouter {
constructor(protected readonly dependencies: LensProtocolRouterDependencies) {}
/**
@ -75,8 +76,8 @@ export abstract class LensProtocolRouter {
* @param url the parsed URL that initiated the `lens://` protocol
* @returns true if a route has been found
*/
protected _routeToInternal(url: Url<Record<string, string | undefined>>): RouteAttempt {
return this._route(this.dependencies.internalRoutes.entries(), url);
routeToInternal(url: Url<Record<string, string | undefined>>): RouteAttempt {
return this._route(this.dependencies.internalRoutes.get().entries(), url);
}
/**
@ -216,7 +217,7 @@ export abstract class LensProtocolRouter {
* Note: this function modifies its argument, do not reuse
* @param url the protocol request URI that was "open"-ed
*/
protected async _routeToExtension(url: Url<Record<string, string | undefined>>): Promise<RouteAttempt> {
async routeToExtension(url: Url<Record<string, string | undefined>>): Promise<RouteAttempt> {
const extension = await this._findMatchingExtensionByName(url);
if (typeof extension === "string") {

View File

@ -0,0 +1,26 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RouteAttempt } from "../../../common/protocol-handler";
import type { MessageChannel } from "../../../common/utils/channel/message-channel-listener-injection-token";
export interface DeepLinkingRouteAttempt {
url: string;
previous: RouteAttempt;
target: "internal" | "external";
}
export const deepLinkingRouteAttemptChannel: MessageChannel<DeepLinkingRouteAttempt> = {
id: "deep-linking-route-attempt",
};
export interface InvalidDeepLinkingAttempt {
error: string;
url: string;
}
export const invalidDeepLinkingAttemptChannel: MessageChannel<InvalidDeepLinkingAttempt> = {
id: "invalid-deep-linking-attempt",
};

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { MessageChannelSender } from "../../../common/utils/channel/message-to-channel-injection-token";
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
import { deepLinkingRouteAttemptChannel } from "../common/channels";
export type SendDeepLinkingAttempt = MessageChannelSender<typeof deepLinkingRouteAttemptChannel>;
const sendDeepLinkingAttemptInjectable = getInjectable({
id: "send-deep-linking-attempt",
instantiate: (di): SendDeepLinkingAttempt => {
const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken);
return (attempt) => sendMessageToChannel(deepLinkingRouteAttemptChannel, attempt);
},
});
export default sendDeepLinkingAttemptInjectable;

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { MessageChannelSender } from "../../../common/utils/channel/message-to-channel-injection-token";
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
import { invalidDeepLinkingAttemptChannel } from "../common/channels";
export type SendInvalidDeepLinkingAttempt = MessageChannelSender<typeof invalidDeepLinkingAttemptChannel>;
const sendInvalidDeepLinkingAttemptInjectable = getInjectable({
id: "send-invalid-deep-linking-attempt",
instantiate: (di): SendInvalidDeepLinkingAttempt => {
const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken);
return (attempt) => sendMessageToChannel(invalidDeepLinkingAttemptChannel, attempt);
},
});
export default sendInvalidDeepLinkingAttemptInjectable;

View File

@ -3,19 +3,26 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
import { computed } from "mobx";
import { pathToRegexp } from "path-to-regexp";
import { internalDeepLinkingRouteInjectionToken } from "../common/internal-handler-token";
const internalDeepLinkingRoutesInjectable = getInjectable({
id: "internal-deep-linking-routes",
instantiate: (di) => {
const registrations = di.injectMany(internalDeepLinkingRouteInjectionToken);
const computedInjectMany = di.inject(computedInjectManyInjectable);
const registrations = computedInjectMany(internalDeepLinkingRouteInjectionToken);
return new Map(registrations.map(registration => {
pathToRegexp(registration.path); // verify now that the schema is valid
return computed(() => new Map((
registrations
.get()
.map(registration => {
pathToRegexp(registration.path); // verify now that the schema is valid
return [registration.path, registration.handler];
}));
return [registration.path, registration.handler];
})
)));
},
});

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { getMessageChannelListenerInjectable } from "../../../common/utils/channel/message-channel-listener-injection-token";
import showErrorNotificationInjectable from "../../../renderer/components/notifications/show-error-notification.injectable";
import { invalidDeepLinkingAttemptChannel } from "../common/channels";
const invalidRouteAttemptListenerInjectable = getMessageChannelListenerInjectable({
channel: invalidDeepLinkingAttemptChannel,
id: "main",
handler: (di) => {
const showErrorNotification = di.inject(showErrorNotificationInjectable);
return (attempt) => void showErrorNotification((
<>
<p>
{"Failed to route "}
<code>{attempt.url}</code>
.
</p>
<p>
<b>Error:</b>
{" "}
{attempt.error}
</p>
</>
));
},
});
export default invalidRouteAttemptListenerInjectable;

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
import findExtensionInstanceByNameInjectable from "../../extensions/loader/common/find-instance-by-name.injectable";
import internalDeepLinkingRoutesInjectable from "./internal-deep-linking-routes.injectable";
import protocolHandlerLoggerInjectable from "../../../common/protocol-handler/logger.injectable";
import { LensProtocolRouter } from "../../../common/protocol-handler";
const deepLinkingRouterInjectable = getInjectable({
id: "deep-linking-router",
instantiate: (di) => new LensProtocolRouter({
extensionsStore: di.inject(extensionsStoreInjectable),
logger: di.inject(protocolHandlerLoggerInjectable),
findExtensionInstanceByName: di.inject(findExtensionInstanceByNameInjectable),
internalRoutes: di.inject(internalDeepLinkingRoutesInjectable),
}),
});
export default deepLinkingRouterInjectable;

View File

@ -0,0 +1,50 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getMessageChannelListenerInjectable } from "../../../common/utils/channel/message-channel-listener-injection-token";
import showShortInfoNotificationInjectable from "../../../renderer/components/notifications/show-short-info.injectable";
import deepLinkingRouterInjectable from "./lens-protocol-router-renderer.injectable";
import { deepLinkingRouteAttemptChannel } from "../common/channels";
import Url from "url-parse";
import React from "react";
import { foldAttemptResults, RouteAttempt } from "../../../common/protocol-handler";
const deepLinkingRouteAttemptListenerInjectable = getMessageChannelListenerInjectable({
channel: deepLinkingRouteAttemptChannel,
id: "main",
handler: (di) => {
const router = di.inject(deepLinkingRouterInjectable);
const showShortInfoNotification = di.inject(showShortInfoNotificationInjectable);
return async (attempt) => {
const url = new Url(attempt.url, true);
const currentAttempt = attempt.target === "internal"
? router.routeToInternal(url)
: await router.routeToExtension(url);
switch (foldAttemptResults(attempt.previous, currentAttempt)) {
case RouteAttempt.MISSING:
showShortInfoNotification((
<p>
{"Unknown action "}
<code>{attempt.url}</code>
{". Are you on the latest version of the extension?"}
</p>
));
break;
case RouteAttempt.MISSING_EXTENSION:
showShortInfoNotification((
<p>
{"Missing extension for action "}
<code>{attempt.url}</code>
{". Not able to find extension in our known list. Try installing it manually."}
</p>
));
break;
}
};
},
});
export default deepLinkingRouteAttemptListenerInjectable;

View File

@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app.injectable";
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
import { pipeline } from "@ogre-tools/fp";

View File

@ -5,7 +5,6 @@
import * as uuid from "uuid";
import { ProtocolHandlerExtension, ProtocolHandlerInternal, ProtocolHandlerInvalid } from "../../../common/protocol-handler";
import { noop } from "../../../common/utils";
import type { LensProtocolRouterMain } from "../lens-protocol-router-main/lens-protocol-router-main";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
@ -16,7 +15,14 @@ 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 type { SendDeepLinkingAttempt } from "../../../features/deep-linking/main/send-deep-linking-attempt.injectable";
import type { SendInvalidDeepLinkingAttempt } from "../../../features/deep-linking/main/send-invalid-deep-linking-attempt.injectable";
import sendDeepLinkingAttemptInjectable from "../../../features/deep-linking/main/send-deep-linking-attempt.injectable";
import sendInvalidDeepLinkingAttemptInjectable from "../../../features/deep-linking/main/send-invalid-deep-linking-attempt.injectable";
import type { DiContainer } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import type { InternalRouteRegistration } from "../../../features/deep-linking/common/internal-handler-token";
import { internalDeepLinkingRouteInjectionToken } from "../../../features/deep-linking/common/internal-handler-token";
import { LensMainExtension } from "../../../extensions/lens-main-extension";
function throwIfDefined(val: any): void {
@ -29,10 +35,12 @@ describe("protocol router tests", () => {
let extensionInstances: ObservableMap<LensExtensionId, LensExtension>;
let lpr: LensProtocolRouterMain;
let enabledExtensions: Set<string>;
let broadcastMessageMock: jest.Mock;
let sendDeepLinkingAttemptMock: jest.MockedFunction<SendDeepLinkingAttempt>;
let sendInvalidDeepLinkingAttemptMock: jest.MockedFunction<SendInvalidDeepLinkingAttempt>;
let di: DiContainer;
beforeEach(async () => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di = getDiForUnitTesting({ doGeneralOverrides: true });
enabledExtensions = new Set();
@ -42,8 +50,11 @@ describe("protocol router tests", () => {
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
broadcastMessageMock = jest.fn();
di.override(broadcastMessageInjectable, () => broadcastMessageMock);
sendDeepLinkingAttemptMock = jest.fn();
di.override(sendDeepLinkingAttemptInjectable, () => sendDeepLinkingAttemptMock);
sendInvalidDeepLinkingAttemptMock = jest.fn();
di.override(sendInvalidDeepLinkingAttemptInjectable, () => sendInvalidDeepLinkingAttemptMock);
extensionInstances = di.inject(extensionInstancesInjectable);
lpr = di.inject(lensProtocolRouterMainInjectable);
@ -55,16 +66,31 @@ describe("protocol router tests", () => {
it("should broadcast invalid protocol on non-lens URLs", async () => {
await lpr.route("https://google.ca");
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInvalid, "invalid protocol", "https://google.ca");
expect(sendInvalidDeepLinkingAttemptMock).toBeCalledWith({
error: "invalid protocol",
url: "https://google.ca",
});
});
it("should broadcast invalid host on non internal or non extension URLs", async () => {
await lpr.route("lens://foobar");
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInvalid, "invalid host", "lens://foobar");
expect(sendInvalidDeepLinkingAttemptMock).toBeCalledWith({
error: "invalid host",
url: "lens://foobar",
});
});
it("should broadcast internal route when called with valid host", async () => {
lpr.addInternalHandler("/", noop);
runInAction(() => {
di.register(getInjectable({
id: "some-id",
instantiate: () => ({
path: "/",
handler: noop,
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}));
});
try {
expect(await lpr.route("lens://app")).toBeUndefined();
@ -72,7 +98,11 @@ describe("protocol router tests", () => {
expect(throwIfDefined(error)).not.toThrow();
}
expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://app",
previous: "matched",
target: "internal",
});
});
it("should broadcast external route when called with valid host", async () => {
@ -105,13 +135,26 @@ describe("protocol router tests", () => {
expect(throwIfDefined(error)).not.toThrow();
}
expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerExtension, "lens://extension/@mirantis/minikube", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://extension/@mirantis/minikube",
previous: "matched",
target: "external",
});
});
it("should call handler if matches", async () => {
let called = false;
lpr.addInternalHandler("/page", () => { called = true; });
runInAction(() => {
di.register(getInjectable({
id: "some-id",
instantiate: () => ({
path: "/page",
handler: () => { called = true; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}));
});
try {
expect(await lpr.route("lens://app/page")).toBeUndefined();
@ -120,14 +163,35 @@ describe("protocol router tests", () => {
}
expect(called).toBe(true);
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInternal, "lens://app/page", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://app/page",
previous: "matched",
target: "internal",
});
});
it("should call most exact handler", async () => {
let called: any = 0;
lpr.addInternalHandler("/page", () => { called = 1; });
lpr.addInternalHandler("/page/:id", params => { called = params.pathname.id; });
runInAction(() => {
di.register(getInjectable({
id: "some-id",
instantiate: () => ({
path: "/page",
handler: () => { called = 1; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}));
di.register(getInjectable({
id: "some-other-id",
instantiate: () => ({
path: "/page/:id",
handler: params => { called = params.pathname.id; },
} as InternalRouteRegistration),
injectionToken: internalDeepLinkingRouteInjectionToken,
}));
});
try {
expect(await lpr.route("lens://app/page/foo")).toBeUndefined();
@ -136,7 +200,11 @@ describe("protocol router tests", () => {
}
expect(called).toBe("foo");
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInternal, "lens://app/page/foo", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://app/page/foo",
previous: "matched",
target: "internal",
});
});
it("should call most exact handler for an extension", async () => {
@ -176,7 +244,11 @@ describe("protocol router tests", () => {
}
expect(called).toBe("foob");
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerExtension, "lens://extension/@foobar/icecream/page/foob", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://extension/@foobar/icecream/page/foob",
previous: "matched",
target: "external",
});
});
it("should work with non-org extensions", async () => {
@ -245,20 +317,67 @@ describe("protocol router tests", () => {
expect(called).toBe(1);
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerExtension, "lens://extension/icecream/page", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://extension/icecream/page",
previous: "matched",
target: "external",
});
});
it("should throw if urlSchema is invalid", () => {
expect(() => lpr.addInternalHandler("/:@", noop)).toThrowError();
expect(() => {
runInAction(() => {
di.register(getInjectable({
id: "some-id",
instantiate: () => ({
path: "/:@",
handler: noop,
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}));
});
}).toThrowError();
});
it("should call most exact handler with 3 found handlers", async () => {
let called: any = 0;
lpr.addInternalHandler("/", () => { called = 2; });
lpr.addInternalHandler("/page", () => { called = 1; });
lpr.addInternalHandler("/page/foo", () => { called = 3; });
lpr.addInternalHandler("/page/bar", () => { called = 4; });
runInAction(() => {
di.register(
getInjectable({
id: "some-id-2",
instantiate: () => ({
path: "/",
handler: () => { called = 2; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
getInjectable({
id: "some-id-1",
instantiate: () => ({
path: "/page",
handler: () => { called = 1; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
getInjectable({
id: "some-id-3",
instantiate: () => ({
path: "/page/foo",
handler: () => { called = 3; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
getInjectable({
id: "some-id-4",
instantiate: () => ({
path: "/page/bar",
handler: () => { called = 4; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
);
});
try {
expect(await lpr.route("lens://app/page/foo/bar/bat")).toBeUndefined();
@ -267,15 +386,44 @@ describe("protocol router tests", () => {
}
expect(called).toBe(3);
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInternal, "lens://app/page/foo/bar/bat", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://app/page/foo/bar/bat",
previous: "matched",
target: "internal",
});
});
it("should call most exact handler with 2 found handlers", async () => {
let called: any = 0;
lpr.addInternalHandler("/", () => { called = 2; });
lpr.addInternalHandler("/page", () => { called = 1; });
lpr.addInternalHandler("/page/bar", () => { called = 4; });
runInAction(() => {
di.register(
getInjectable({
id: "some-id-2",
instantiate: () => ({
path: "/",
handler: () => { called = 2; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
getInjectable({
id: "some-id-1",
instantiate: () => ({
path: "/page",
handler: () => { called = 1; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
getInjectable({
id: "some-id-4",
instantiate: () => ({
path: "/page/bar",
handler: () => { called = 4; },
}),
injectionToken: internalDeepLinkingRouteInjectionToken,
}),
);
});
try {
expect(await lpr.route("lens://app/page/foo/bar/bat")).toBeUndefined();
@ -284,6 +432,10 @@ describe("protocol router tests", () => {
}
expect(called).toBe(1);
expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInternal, "lens://app/page/foo/bar/bat", "matched");
expect(sendDeepLinkingAttemptMock).toHaveBeenCalledWith({
url: "lens://app/page/foo/bar/bat",
previous: "matched",
target: "internal",
});
});
});

View File

@ -1,6 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export * from "./lens-protocol-router-main/lens-protocol-router-main";

View File

@ -6,10 +6,11 @@ import { getInjectable } from "@ogre-tools/injectable";
import { LensProtocolRouterMain } from "./lens-protocol-router-main";
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable";
import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable";
import findExtensionInstanceByNameInjectable from "../../../features/extensions/loader/common/find-instance-by-name.injectable";
import internalDeepLinkingRoutesInjectable from "../../../features/deep-linking/renderer/internal-deep-linking-routes.injectable";
import protocolHandlerLoggerInjectable from "../../../common/protocol-handler/logger.injectable";
import sendDeepLinkingAttemptInjectable from "../../../features/deep-linking/main/send-deep-linking-attempt.injectable";
import sendInvalidDeepLinkingAttemptInjectable from "../../../features/deep-linking/main/send-invalid-deep-linking-attempt.injectable";
const lensProtocolRouterMainInjectable = getInjectable({
id: "lens-protocol-router-main",
@ -17,10 +18,11 @@ const lensProtocolRouterMainInjectable = getInjectable({
instantiate: (di) => new LensProtocolRouterMain({
extensionsStore: di.inject(extensionsStoreInjectable),
showApplicationWindow: di.inject(showApplicationWindowInjectable),
broadcastMessage: di.inject(broadcastMessageInjectable),
logger: di.inject(protocolHandlerLoggerInjectable),
findExtensionInstanceByName: di.inject(findExtensionInstanceByNameInjectable),
internalRoutes: di.inject(internalDeepLinkingRoutesInjectable),
sendDeepLinkingAttempt: di.inject(sendDeepLinkingAttemptInjectable),
sendInvalidDeepLinkingAttempt: di.inject(sendInvalidDeepLinkingAttemptInjectable),
}),
});

View File

@ -8,9 +8,9 @@ import URLParse from "url-parse";
import type { LensExtension } from "../../../extensions/lens-extension";
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";
import type { BroadcastMessage } from "../../../common/ipc/broadcast-message.injectable";
import type { SendDeepLinkingAttempt } from "../../../features/deep-linking/main/send-deep-linking-attempt.injectable";
import type { SendInvalidDeepLinkingAttempt } from "../../../features/deep-linking/main/send-invalid-deep-linking-attempt.injectable";
export interface FallbackHandler {
(name: string): Promise<boolean>;
@ -35,7 +35,8 @@ function checkHost<Query>(url: URLParse<Query>): boolean {
export interface LensProtocolRouterMainDependencies extends LensProtocolRouterDependencies {
showApplicationWindow: () => Promise<void>;
broadcastMessage: BroadcastMessage;
sendDeepLinkingAttempt: SendDeepLinkingAttempt;
sendInvalidDeepLinkingAttempt: SendInvalidDeepLinkingAttempt;
}
export class LensProtocolRouterMain extends proto.LensProtocolRouter {
@ -74,12 +75,15 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
this.dependencies.logger.info(`routing ${url.toString()}`);
if (routeInternally) {
this._routeToInternal(url);
this.routeToInternal(url);
} else {
await this._routeToExtension(url);
await this.routeToExtension(url);
}
} catch (error) {
this.dependencies.broadcastMessage(ProtocolHandlerInvalid, error ? String(error) : "unknown error", rawUrl);
this.dependencies.sendInvalidDeepLinkingAttempt({
error: String(error),
url: rawUrl,
});
if (error instanceof proto.RoutingError) {
this.dependencies.logger.error(`${error}`, { url: error.url });
@ -113,10 +117,13 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
return "";
}
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);
routeToInternal(url: URLParse<Record<string, string | undefined>>): RouteAttempt {
const attempt = super.routeToInternal(url);
const broadcastToRenderer = () => this.dependencies.sendDeepLinkingAttempt({
previous: attempt,
target: "internal",
url: url.toString(),
});
if (this.rendererLoaded.get()) {
broadcastToRenderer();
@ -127,18 +134,20 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
return attempt;
}
protected async _routeToExtension(url: URLParse<Record<string, string | undefined>>): Promise<RouteAttempt> {
const rawUrl = url.toString(); // for sending to renderer
async routeToExtension(url: URLParse<Record<string, string | undefined>>): Promise<RouteAttempt> {
/**
* This needs to be done first, so that the missing extension handlers can
* be called before notifying the renderer.
*
* Note: this needs to clone the url because _routeToExtension modifies its
* Note: this needs to clone the url because routeToExtension modifies its
* argument.
*/
const attempt = await super._routeToExtension(new URLParse(url.toString(), true));
const broadcastToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt);
const attempt = await super.routeToExtension(new URLParse(url.toString(), true));
const broadcastToRenderer = () => this.dependencies.sendDeepLinkingAttempt({
previous: attempt,
target: "external",
url: url.toString(),
});
if (this.rendererLoaded.get()) {
broadcastToRenderer();

View File

@ -3,18 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import lensProtocolRouterMainInjectable from "../lens-protocol-router-main.injectable";
import lensProtocolRouterMainInjectable from "./lens-protocol-router-main.injectable";
const openDeepLinkInjectable = getInjectable({
id: "open-deep-link",
instantiate: (di) => {
const getProtocolRouter = () => di.inject(lensProtocolRouterMainInjectable);
return async (url: string) => {
await getProtocolRouter().route(url);
};
},
instantiate: (di) => async (url: string) => di.inject(lensProtocolRouterMainInjectable).route(url),
});
export default openDeepLinkInjectable;

View File

@ -4,7 +4,7 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import shouldStartHiddenInjectable from "../../electron-app/features/should-start-hidden.injectable";
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link.injectable";
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
import createFirstApplicationWindowInjectable from "../lens-window/application-window/create-first-application-window.injectable";
import splashWindowInjectable from "../lens-window/splash-window/splash-window.injectable";

View File

@ -3,7 +3,6 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import lensProtocolRouterRendererInjectable from "../../protocol-handler/lens-protocol-router-renderer.injectable";
import registerIpcListenersInjectable from "../../ipc/register-ipc-listeners.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import unmountRootComponentInjectable from "../../window/unmount-root-component.injectable";
@ -12,13 +11,10 @@ const initRootFrameInjectable = getInjectable({
id: "init-root-frame",
instantiate: (di) => {
const registerIpcListeners = di.inject(registerIpcListenersInjectable);
const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable);
const logger = di.inject(loggerInjectable);
const unmountRootComponent = di.inject(unmountRootComponentInjectable);
return async () => {
lensProtocolRouterRenderer.init();
registerIpcListeners();
window.addEventListener("beforeunload", () => {

View File

@ -1,27 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { LensProtocolRouterRenderer } from "./lens-protocol-router-renderer";
import extensionsStoreInjectable from "../../extensions/extensions-store/extensions-store.injectable";
import showErrorNotificationInjectable from "../components/notifications/show-error-notification.injectable";
import showShortInfoNotificationInjectable from "../components/notifications/show-short-info.injectable";
import findExtensionInstanceByNameInjectable from "../../features/extensions/loader/common/find-instance-by-name.injectable";
import internalDeepLinkingRoutesInjectable from "../../features/deep-linking/renderer/internal-deep-linking-routes.injectable";
import protocolHandlerLoggerInjectable from "../../common/protocol-handler/logger.injectable";
const lensProtocolRouterRendererInjectable = getInjectable({
id: "lens-protocol-router-renderer",
instantiate: (di) => new LensProtocolRouterRenderer({
extensionsStore: di.inject(extensionsStoreInjectable),
logger: di.inject(protocolHandlerLoggerInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable),
showShortInfoNotification: di.inject(showShortInfoNotificationInjectable),
findExtensionInstanceByName: di.inject(findExtensionInstanceByNameInjectable),
internalRoutes: di.inject(internalDeepLinkingRoutesInjectable),
}),
});
export default lensProtocolRouterRendererInjectable;

View File

@ -1,82 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import React from "react";
import { ipcRenderer } from "electron";
import * as proto from "../../common/protocol-handler";
import Url from "url-parse";
import type { LensProtocolRouterDependencies } from "../../common/protocol-handler";
import { foldAttemptResults, ProtocolHandlerInvalid, RouteAttempt } from "../../common/protocol-handler";
import type { ShowNotification } from "../components/notifications";
interface Dependencies extends LensProtocolRouterDependencies {
showShortInfoNotification: ShowNotification;
showErrorNotification: ShowNotification;
}
export class LensProtocolRouterRenderer extends proto.LensProtocolRouter {
constructor(protected readonly dependencies: Dependencies) {
super(dependencies);
}
/**
* This function is needed to be called early on in the renderers lifetime.
*/
public init(): void {
ipcRenderer.on(proto.ProtocolHandlerInternal, (event, rawUrl: string, mainAttemptResult: RouteAttempt) => {
const rendererAttempt = this._routeToInternal(new Url(rawUrl, true));
if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) {
this.dependencies.showShortInfoNotification((
<p>
{"Unknown action "}
<code>{rawUrl}</code>
{". Are you on the latest version?"}
</p>
));
}
});
ipcRenderer.on(proto.ProtocolHandlerExtension, async (event, rawUrl: string, mainAttemptResult: RouteAttempt) => {
const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true));
switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) {
case RouteAttempt.MISSING:
this.dependencies.showShortInfoNotification((
<p>
{"Unknown action "}
<code>{rawUrl}</code>
{". Are you on the latest version of the extension?"}
</p>
));
break;
case RouteAttempt.MISSING_EXTENSION:
this.dependencies.showShortInfoNotification((
<p>
{"Missing extension for action "}
<code>{rawUrl}</code>
{". Not able to find extension in our known list. Try installing it manually."}
</p>
));
break;
}
});
ipcRenderer.on(ProtocolHandlerInvalid, (event, error: string, rawUrl: string) => {
this.dependencies.showErrorNotification((
<>
<p>
{"Failed to route "}
<code>{rawUrl}</code>
.
</p>
<p>
<b>Error:</b>
{" "}
{error}
</p>
</>
));
});
}
}