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

Add more injectables

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-07-06 11:39:57 -04:00
parent a733641f62
commit 1b700491f9
6 changed files with 57 additions and 44 deletions

View File

@ -8,6 +8,8 @@ import hostedClusterIdInjectable from "../cluster-frame-context/hosted-cluster-i
import getShellAuthTokenInjectable from "../../common/shell-authentication/get-auth-token.injectable";
import type { TerminalApiQuery } from "./terminal-api";
import { TerminalApi } from "./terminal-api";
import createWebsocketInjectable from "./create-websocket.injectable";
import defaultWebsocketParamsInjectable from "./default-websocket-params.injectable";
export type CreateTerminalApi = (query: TerminalApiQuery) => TerminalApi;
@ -16,6 +18,8 @@ const createTerminalApiInjectable = getInjectable({
instantiate: (di): CreateTerminalApi => {
const hostedClusterId = di.inject(hostedClusterIdInjectable);
const getShellAuthToken = di.inject(getShellAuthTokenInjectable);
const createWebsocket = di.inject(createWebsocketInjectable);
const defaultParams = di.inject(defaultWebsocketParamsInjectable);
return (query) => {
assert(hostedClusterId, "Can only create terminal APIs within a cluster frame");
@ -23,6 +27,8 @@ const createTerminalApiInjectable = getInjectable({
return new TerminalApi({
hostedClusterId,
getShellAuthToken,
createWebsocket,
defaultParams,
}, query);
};
},

View File

@ -0,0 +1,14 @@
/**
* 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";
export type CreateWebsocket = (url: string) => WebSocket;
const createWebsocketInjectable = getInjectable({
id: "create-websocket",
instantiate: (): CreateWebsocket => (url) => new WebSocket(url),
});
export default createWebsocketInjectable;

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 type { Injectable } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import { TerminalChannels, type TerminalMessage } from "../../common/terminal/channels";
import isDevelopmentInjectable from "../../common/vars/is-development.injectable";
export type DefaultWebsocketParams = typeof defaultWebsocketParamsInjectable extends Injectable<infer T, any, any> ? T : never;
const defaultWebsocketParamsInjectable = getInjectable({
id: "default-websocket-params",
instantiate: (di) => ({
logging: di.inject(isDevelopmentInjectable),
reconnectDelay: 10,
flushOnOpen: true,
pingMessage: JSON.stringify({ type: TerminalChannels.PING } as TerminalMessage),
}),
});
export default defaultWebsocketParamsInjectable;

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { WebSocketEvents } from "./websocket-api";
import type { WebSocketApiDependencies, WebSocketEvents } from "./websocket-api";
import { WebSocketApi } from "./websocket-api";
import isEqual from "lodash/isEqual";
import url from "url";
@ -36,7 +36,7 @@ export interface TerminalEvents extends WebSocketEvents {
connected: () => void;
}
export interface TerminalApiDependencies {
export interface TerminalApiDependencies extends WebSocketApiDependencies {
readonly hostedClusterId: string;
getShellAuthToken: GetShellAuthToken;
}
@ -47,7 +47,7 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
@observable public isReady = false;
constructor(protected readonly dependencies: TerminalApiDependencies, protected readonly query: TerminalApiQuery) {
super({
super(dependencies, {
flushOnOpen: false,
pingInterval: 30,
});

View File

@ -7,9 +7,9 @@ import { observable, makeObservable } from "mobx";
import EventEmitter from "events";
import type TypedEventEmitter from "typed-emitter";
import type { Arguments } from "typed-emitter";
import { isDevelopment } from "../../common/vars";
import type { Defaulted } from "../utils";
import { TerminalChannels, type TerminalMessage } from "../../common/terminal/channels";
import type { CreateWebsocket } from "./create-websocket.injectable";
import type { DefaultWebsocketParams } from "./default-websocket-params.injectable";
interface WebsocketApiParams {
/**
@ -64,27 +64,25 @@ export interface WebSocketEvents {
close: () => void;
}
export interface WebSocketApiDependencies {
createWebsocket: CreateWebsocket;
readonly defaultParams: DefaultWebsocketParams;
}
export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter as { new<T>(): TypedEventEmitter<T> })<Events> {
protected socket: WebSocket | null = null;
protected pendingCommands: string[] = [];
protected reconnectTimer?: number;
protected pingTimer?: number;
protected params: Defaulted<WebsocketApiParams, keyof typeof WebSocketApi["defaultParams"]>;
protected readonly params: Defaulted<WebsocketApiParams, keyof DefaultWebsocketParams>;
@observable readyState = WebSocketApiState.PENDING;
private static readonly defaultParams = {
logging: isDevelopment,
reconnectDelay: 10,
flushOnOpen: true,
pingMessage: JSON.stringify({ type: TerminalChannels.PING } as TerminalMessage),
};
constructor(params: WebsocketApiParams) {
constructor(protected readonly dependencies: WebSocketApiDependencies, params: WebsocketApiParams) {
super();
makeObservable(this);
this.params = {
...WebSocketApi.defaultParams,
...this.dependencies.defaultParams,
...params,
};
const { pingInterval } = this.params;
@ -111,7 +109,7 @@ export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter
this.socket?.close();
// start new connection
this.socket = new WebSocket(url);
this.socket = this.dependencies.createWebsocket(url);
this.socket.addEventListener("open", ev => this._onOpen(ev));
this.socket.addEventListener("message", ev => this._onMessage(ev));
this.socket.addEventListener("error", ev => this._onError(ev));

View File

@ -4,43 +4,21 @@
*/
import type { RenderResult } from "@testing-library/react";
import { waitFor } from "@testing-library/react";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import getShellAuthTokenChannelHandlerInjectable from "../../main/lens-proxy/proxy-functions/shell/auth-token-channel-handler.injectable";
import type { GetShellAuthToken } from "../../common/shell-authentication/get-auth-token.injectable";
import type { SpawnPty } from "../../main/shell-session/spawn-pty.injectable";
import spawnPtyInjectable from "../../main/shell-session/spawn-pty.injectable";
import type { IPty } from "node-pty";
describe("local shell session techincal tests", () => {
let builder: ApplicationBuilder;
let result: RenderResult;
let authenticationSpy: jest.SpyInstance<Uint8Array | Promise<Uint8Array>, Parameters<GetShellAuthToken>>;
let spawnPtyMock: jest.MockedFunction<SpawnPty>;
let ptyMock: jest.MockedObject<IPty>;
beforeEach(async () => {
builder = getApplicationBuilder()
.beforeApplicationStart(() => {
spawnPtyMock = jest.fn();
builder.dis.mainDi.override(spawnPtyInjectable, () => spawnPtyMock);
spawnPtyMock.mockImplementation(() => ptyMock = {
cols: 80,
rows: 40,
pid: 12346,
process: "my-mocked-shell",
handleFlowControl: true,
kill: jest.fn(),
onData: jest.fn(),
onExit: jest.fn(),
on: jest.fn(),
resize: jest.fn(),
write: jest.fn(),
pause: jest.fn(),
resume: jest.fn(),
});
builder.dis.mainDi.override(spawnPtyInjectable, () => jest.fn());
})
.beforeRender(() => {
const shellAuthentication = builder.dis.mainDi.inject(getShellAuthTokenChannelHandlerInjectable);
@ -60,9 +38,4 @@ describe("local shell session techincal tests", () => {
it("should call the authentication function", () => {
expect(authenticationSpy).toBeCalled();
});
it("should create a pty instance", async () => {
await waitFor(() => expect(spawnPtyMock).toBeCalled());
void ptyMock;
});
});