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:
parent
db9472de36
commit
e946194582
@ -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") {
|
||||
|
||||
26
packages/core/src/features/deep-linking/common/channels.ts
Normal file
26
packages/core/src/features/deep-linking/common/channels.ts
Normal 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",
|
||||
};
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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];
|
||||
})
|
||||
)));
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -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";
|
||||
|
||||
@ -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",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -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";
|
||||
@ -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),
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
@ -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";
|
||||
|
||||
@ -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", () => {
|
||||
|
||||
@ -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;
|
||||
@ -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>
|
||||
</>
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user