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

Make IPC usage injectable

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-06-03 10:09:10 -04:00
parent ac42d7e365
commit 78ead4f2b9
15 changed files with 156 additions and 80 deletions

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 { getInjectable } from "@ogre-tools/injectable";
import type { ClusterId } from "../cluster-types";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
export type GetShellAuthTokenChannel = RequestChannel<
{
clusterId: ClusterId;
tabId: string;
},
Uint8Array
>;
const getShellAuthTokenChannelInjectable = getInjectable({
id: "get-shell-auth-token-channel",
instantiate: (): GetShellAuthTokenChannel => ({
id: "get-shell-auth-token-channel",
}),
injectionToken: requestChannelInjectionToken,
});
export default getShellAuthTokenChannelInjectable;

View File

@ -0,0 +1,23 @@
/**
* 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 { ChannelRequest } from "../utils/channel/request-channel-injection-token";
import { requestFromChannelInjectionToken } from "../utils/channel/request-from-channel-injection-token";
import type { GetShellAuthTokenChannel } from "./get-auth-token-channel.injectable";
import getShellAuthTokenChannelInjectable from "./get-auth-token-channel.injectable";
export type GetShellAuthToken = ChannelRequest<GetShellAuthTokenChannel>;
const getShellAuthTokenInjectable = getInjectable({
id: "get-shell-auth-token",
instantiate: (di): GetShellAuthToken => {
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
const getShellAuthTokenChannel = di.inject(getShellAuthTokenChannelInjectable);
return (arg) => requestFromChannel(getShellAuthTokenChannel, arg);
},
});
export default getShellAuthTokenInjectable;

View File

@ -0,0 +1,12 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { TypedArray } from "type-fest";
export type IpcPrimative = string | boolean | number | bigint | null | undefined;
export type IpcClasses = Date | RegExp | TypedArray;
export type IpcValue = IpcPrimative | IpcObject | IpcArray | IpcClasses;
export type IpcObject = { [Key in string]?: IpcValue };
export type IpcArray = IpcValue[];

View File

@ -1,12 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export interface Channel<MessageTemplate = void, ReturnTemplate = void> {
id: string;
_messageTemplate?: MessageTemplate;
_returnTemplate?: ReturnTemplate;
}

View File

@ -16,7 +16,7 @@ import type { MessageChannel } from "./message-channel-injection-token";
import type { RequestFromChannel } from "./request-from-channel-injection-token";
import { requestFromChannelInjectionToken } from "./request-from-channel-injection-token";
import type { RequestChannel } from "./request-channel-injection-token";
import { requestChannelListenerInjectionToken } from "./request-channel-listener-injection-token";
import { requestChannelHandlerInjectionToken } from "./request-channel-listener-injection-token";
import type { AsyncFnMock } from "@async-fn/jest";
import asyncFn from "@async-fn/jest";
import { getPromiseStatus } from "../../test-utils/get-promise-status";
@ -183,7 +183,7 @@ describe("channel", () => {
handler: requestListenerInMainMock,
}),
injectionToken: requestChannelListenerInjectionToken,
injectionToken: requestChannelHandlerInjectionToken,
});
mainDi.register(testChannelListenerInMainInjectable);

View File

@ -4,11 +4,11 @@
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { RequestChannel } from "./request-channel-injection-token";
import type { RequestChannelListener } from "./request-channel-listener-injection-token";
import type { RequestChannelHandlerDescriptor } from "./request-channel-listener-injection-token";
export type EnlistRequestChannelListener = <
TChannel extends RequestChannel<any, any>,
>(listener: RequestChannelListener<TChannel>) => () => void;
>(listener: RequestChannelHandlerDescriptor<TChannel>) => () => void;
export const enlistRequestChannelListenerInjectionToken =
getInjectionToken<EnlistRequestChannelListener>({

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import { getStartableStoppable } from "../get-startable-stoppable";
import { disposer } from "../index";
import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token";
import { requestChannelListenerInjectionToken } from "./request-channel-listener-injection-token";
import { requestChannelHandlerInjectionToken } from "./request-channel-listener-injection-token";
import { enlistMessageChannelListenerInjectionToken } from "./enlist-message-channel-listener-injection-token";
import { enlistRequestChannelListenerInjectionToken } from "./enlist-request-channel-listener-injection-token";
@ -17,7 +17,7 @@ const listeningOfChannelsInjectable = getInjectable({
const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken);
const enlistRequestChannelListener = di.inject(enlistRequestChannelListenerInjectionToken);
const messageChannelListeners = di.injectMany(messageChannelListenerInjectionToken);
const requestChannelListeners = di.injectMany(requestChannelListenerInjectionToken);
const requestChannelListeners = di.injectMany(requestChannelHandlerInjectionToken);
return getStartableStoppable("listening-of-channels", () => {
const messageChannelDisposers = messageChannelListeners.map(enlistMessageChannelListener);

View File

@ -4,17 +4,19 @@
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { JsonValue } from "type-fest";
import type { IpcValue } from "./allowed-types";
export interface RequestChannel<
Request extends JsonValue | void = void,
Response extends JsonValue | void = void,
Request extends IpcValue | void = void,
Response extends IpcValue | void = void,
> {
id: string;
_requestSignature?: Request;
_responseSignature?: Response;
id: Request | Response extends boolean ? string : string;
}
export type ChannelRequest<Channel> = Channel extends RequestChannel<infer Request, infer Response>
? (arg: Request) => Promise<Response>
: never;
export const requestChannelInjectionToken = getInjectionToken<RequestChannel<any, any>>({
id: "request-channel",
});

View File

@ -2,24 +2,42 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { SetRequired } from "type-fest";
import type { DiContainerForInjection, Injectable } from "@ogre-tools/injectable";
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import type { RequestChannel } from "./request-channel-injection-token";
export interface RequestChannelListener<TChannel extends RequestChannel<any, any>> {
channel: TChannel;
export type RequestChannelHandler<Channel> = Channel extends RequestChannel<infer Request, infer Response>
? (request: Request) => Response | Promise<Response>
: never;
handler: (
request: SetRequired<TChannel, "_requestSignature">["_requestSignature"]
) =>
| SetRequired<TChannel, "_responseSignature">["_responseSignature"]
| Promise<
SetRequired<TChannel, "_responseSignature">["_responseSignature"]
>;
export interface RequestChannelHandlerDescriptor<Channel> {
channel: Channel;
handler: RequestChannelHandler<Channel>;
}
export const requestChannelListenerInjectionToken = getInjectionToken<RequestChannelListener<RequestChannel<any, any>>>(
export const requestChannelHandlerInjectionToken = getInjectionToken<RequestChannelHandlerDescriptor<RequestChannel<any, any>>>(
{
id: "request-channel-listener",
id: "request-channel-handler",
},
);
export function getRequestChannelHandlerInjectable<
ChannelInjectionToken,
Channel = ChannelInjectionToken extends Injectable<infer Channel, RequestChannel<any, any>, void>
? Channel
: never,
>(
channelInjectionToken: ChannelInjectionToken,
instantiate: (di: DiContainerForInjection) => RequestChannelHandler<Channel>,
) {
const token = channelInjectionToken as unknown as Injectable<RequestChannel<any, any>, RequestChannel<any, any>, void>;
return getInjectable({
id: `${token.id}-handler`,
instantiate: (di) => ({
channel: di.inject(token),
handler: instantiate(di),
}),
injectionToken: requestChannelHandlerInjectionToken,
});
}

View File

@ -3,19 +3,19 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { SetRequired } from "type-fest";
import type { RequestChannel } from "./request-channel-injection-token";
export type RequestFromChannel = <
TChannel extends RequestChannel<any, any>,
Channel,
Request = Channel extends RequestChannel<infer Request, any> ? Request : never,
Response = Channel extends RequestChannel<any, infer Response> ? Response : never,
>(
channel: TChannel,
...request: TChannel["_requestSignature"] extends void
channel: Channel,
...request: Request extends void
? []
: [TChannel["_requestSignature"]]
) => Promise<SetRequired<TChannel, "_responseSignature">["_responseSignature"]>;
: [Request]
) => Promise<Response>;
export const requestFromChannelInjectionToken =
getInjectionToken<RequestFromChannel>({
id: "request-from-request-channel",
});
export const requestFromChannelInjectionToken = getInjectionToken<RequestFromChannel>({
id: "request-from-request-channel",
});

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { RequestChannelListener } from "../../common/utils/channel/request-channel-listener-injection-token";
import { requestChannelListenerInjectionToken } from "../../common/utils/channel/request-channel-listener-injection-token";
import type { RequestChannelHandlerDescriptor } from "../../common/utils/channel/request-channel-listener-injection-token";
import { requestChannelHandlerInjectionToken } from "../../common/utils/channel/request-channel-listener-injection-token";
import type { AppPathsChannel } from "../../common/app-paths/app-paths-channel.injectable";
import appPathsChannelInjectable from "../../common/app-paths/app-paths-channel.injectable";
import appPathsInjectable from "../../common/app-paths/app-paths.injectable";
@ -12,7 +12,7 @@ import appPathsInjectable from "../../common/app-paths/app-paths.injectable";
const appPathsRequestChannelListenerInjectable = getInjectable({
id: "app-paths-request-channel-listener",
instantiate: (di): RequestChannelListener<AppPathsChannel> => {
instantiate: (di): RequestChannelHandlerDescriptor<AppPathsChannel> => {
const channel = di.inject(appPathsChannelInjectable);
const appPaths = di.inject(appPathsInjectable);
@ -21,7 +21,7 @@ const appPathsRequestChannelListenerInjectable = getInjectable({
handler: () => appPaths,
};
},
injectionToken: requestChannelListenerInjectionToken,
injectionToken: requestChannelHandlerInjectionToken,
});
export default appPathsRequestChannelListenerInjectable;

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import getShellAuthTokenChannelInjectable from "../../../../common/shell-authentication/get-auth-token-channel.injectable";
import { getRequestChannelHandlerInjectable } from "../../../../common/utils/channel/request-channel-listener-injection-token";
import shellRequestAuthenticatorInjectable from "./request-authenticator.injectable";
const getShellAuthTokenChannelHandlerInjectable = getRequestChannelHandlerInjectable(
getShellAuthTokenChannelInjectable,
(di) => {
const authenticator = di.inject(shellRequestAuthenticatorInjectable);
return ({ clusterId, tabId }) => authenticator.getTokenFor(clusterId, tabId);
},
);
export default getShellAuthTokenChannelHandlerInjectable;

View File

@ -2,30 +2,20 @@
* 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 syncBoxInitialValueChannelInjectable from "../../../common/utils/sync-box/sync-box-initial-value-channel.injectable";
import { syncBoxInjectionToken } from "../../../common/utils/sync-box/sync-box-injection-token";
import { requestChannelListenerInjectionToken } from "../../../common/utils/channel/request-channel-listener-injection-token";
import { getRequestChannelHandlerInjectable } from "../../../common/utils/channel/request-channel-listener-injection-token";
const syncBoxInitialValueChannelListenerInjectable = getInjectable({
id: "sync-box-initial-value-channel-listener",
instantiate: (di) => {
const channel = di.inject(syncBoxInitialValueChannelInjectable);
const syncBoxInitialValueChannelListenerInjectable = getRequestChannelHandlerInjectable(
syncBoxInitialValueChannelInjectable,
(di) => {
const syncBoxes = di.injectMany(syncBoxInjectionToken);
return {
channel,
handler: () =>
syncBoxes.map((box) => ({
id: box.id,
value: box.value.get(),
})),
};
return () => syncBoxes.map((box) => ({
id: box.id,
value: box.value.get(),
}));
},
injectionToken: requestChannelListenerInjectionToken,
});
);
export default syncBoxInitialValueChannelListenerInjectable;

View File

@ -8,10 +8,10 @@ import { WebSocketApi } from "./websocket-api";
import isEqual from "lodash/isEqual";
import url from "url";
import { makeObservable, observable } from "mobx";
import { ipcRenderer } from "electron";
import logger from "../../common/logger";
import { once } from "lodash";
import { type TerminalMessage, TerminalChannels } from "../../common/terminal/channels";
import type { GetShellAuthToken } from "../../common/shell-authentication/get-auth-token.injectable";
enum TerminalColor {
RED = "\u001b[31m",
@ -38,6 +38,7 @@ export interface TerminalEvents extends WebSocketEvents {
export interface TerminalApiDependencies {
readonly hostedClusterId: string;
getShellAuthToken: GetShellAuthToken;
}
export class TerminalApi extends WebSocketApi<TerminalEvents> {
@ -66,12 +67,10 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
this.emitStatus("Connecting ...");
}
const authTokenArray = await ipcRenderer.invoke("cluster:shell-api", this.dependencies.hostedClusterId, this.query.id);
if (!(authTokenArray instanceof Uint8Array)) {
throw new TypeError("ShellApi token is not a Uint8Array");
}
const authTokenArray = await this.dependencies.getShellAuthToken({
clusterId: this.dependencies.hostedClusterId,
tabId: this.query.id,
});
const { hostname, protocol, port } = location;
const socketUrl = url.format({
protocol: protocol.includes("https") ? "wss" : "ws",

View File

@ -4,14 +4,14 @@
*/
import type { DiContainer } from "@ogre-tools/injectable";
import type { RequestChannel } from "../../common/utils/channel/request-channel-injection-token";
import type { RequestChannelListener } from "../../common/utils/channel/request-channel-listener-injection-token";
import type { RequestChannelHandlerDescriptor } from "../../common/utils/channel/request-channel-listener-injection-token";
import enlistRequestChannelListenerInjectableInMain from "../../main/utils/channel/channel-listeners/enlist-request-channel-listener.injectable";
import requestFromChannelInjectable from "../../renderer/utils/channel/request-from-channel.injectable";
export const overrideRequestingFromWindowToMain = (mainDi: DiContainer) => {
const requestChannelListenerFakesForMain = new Map<
string,
RequestChannelListener<RequestChannel<any, any>>
RequestChannelHandlerDescriptor<RequestChannel<any, any>>
>();
mainDi.override(
@ -28,7 +28,7 @@ export const overrideRequestingFromWindowToMain = (mainDi: DiContainer) => {
listener.channel.id,
// TODO: Figure out typing
listener as unknown as RequestChannelListener<
listener as unknown as RequestChannelHandlerDescriptor<
RequestChannel<any, any>
>,
);