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

Merge branch 'master' into app-modal-registry

This commit is contained in:
Alex Andreev 2022-10-22 17:12:31 +03:00
commit 136823ce18
238 changed files with 3382 additions and 2991 deletions

View File

@ -0,0 +1,35 @@
name: Bump Version on master
on:
pull_request:
types:
- closed
branches:
- master
workflow_dispatch:
jobs:
release:
name: Release
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release') }}
steps:
- name: Checkout Release from lens
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Install dependencies
run: |
npm i --location=global semver
- name: Bump version to first alpha of next minor version
run: |
NEW_VERSION=$(cat package.json | jq .version --raw-output| xargs semver -i preminor --preid alpha)
cat package.json | jq --arg new_version "$NEW_VERSION" '.version = $new_version' > new-package.json
mv new-package.json package.json
- uses: peter-evans/create-pull-request@v4
with:
add-paths: package.json
commit-message: Update package.json version to next preminor because of recent release
signoff: true
delete-branch: true
title: Update version to next preminor
labels: chore

View File

@ -53,9 +53,9 @@ jobs:
rm -fr ./docs/clusters ./docs/contributing ./docs/faq ./docs/getting-started ./docs/helm ./docs/support ./docs/supporting
sed -i '/Protocol Handlers/d' ./mkdocs.yml
sed -i '/IPC/d' ./mkdocs.yml
sed -i 's#../../clusters/adding-clusters.md#https://docs.k8slens.dev/latest/clusters/adding-clusters/#g' ./docs/extensions/get-started/your-first-extension.md
sed -i 's#clusters/adding-clusters.md#https://docs.k8slens.dev/latest/clusters/adding-clusters/#g' ./docs/README.md
sed -i 's#../../contributing/README.md#https://docs.k8slens.dev/latest/contributing/#g' ./docs/extensions/guides/generator.md
sed -i 's#../../clusters/adding-clusters.md#https://docs.k8slens.dev/getting-started/add-cluster/#g' ./docs/extensions/get-started/your-first-extension.md
sed -i 's#clusters/adding-clusters.md#https://docs.k8slens.dev//getting-started/adding-clusters/#g' ./docs/README.md
sed -i 's#../../contributing/README.md#https://docs.k8slens.dev/contributing/#g' ./docs/extensions/guides/generator.md
- name: git config
run: |

14
.github/workflows/require-milestone.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Require Milestone
on:
pull_request:
types: [opened, edited, synchronize]
jobs:
milestone:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Require Milestone
run: |
exit $(gh pr view ${{ github.event.pull_request.number }} --json milestone | jq 'if .milestone == null then 1 else 0 end')
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}

View File

@ -1,3 +1,3 @@
# Contributing to Lens
See [Contributing to Lens](https://docs.k8slens.dev/latest/contributing/) documentation.
See [Contributing to Lens](https://docs.k8slens.dev/contributing/) documentation.

View File

@ -19,15 +19,15 @@ Lens IDE a standalone application for MacOS, Windows and Linux operating systems
## Installation
See [Getting Started](https://docs.k8slens.dev/main/getting-started/install-lens/) page.
See [Getting Started](https://docs.k8slens.dev/getting-started/install-lens/) page.
## Development
See [Development](https://docs.k8slens.dev/latest/contributing/development/) page.
See [Development](https://docs.k8slens.dev/contributing/development/) page.
## Contributing
See [Contributing](https://docs.k8slens.dev/latest/contributing/) page.
See [Contributing](https://docs.k8slens.dev/contributing/) page.
## License

View File

@ -78,7 +78,7 @@ npm run dev
You must restart Lens for the extension to load.
After this initial restart, reload Lens and it will automatically pick up changes any time the extension rebuilds.
With Lens running, either connect to an existing cluster or create a new one - refer to the latest [Lens Documentation](https://docs.k8slens.dev/main/catalog/) for details on how to add a cluster in Lens IDE.
With Lens running, either connect to an existing cluster or create a new one - refer to the latest [Lens Documentation](https://docs.k8slens.dev/getting-started/add-cluster/) for details on how to add a cluster in Lens IDE.
You will see the "Hello World" page in the left-side cluster menu.
## Develop the Extension

View File

@ -46,14 +46,14 @@ Open `my-first-lens-ext/renderer.tsx` and change the value of `title` from `"Hel
```typescript
clusterPageMenus = [
{
target: { pageId: "hello" },
title: "Hello Lens",
components: {
Icon: ExampleIcon,
}
}
]
{
target: { pageId: "hello" },
title: "Hello Lens",
components: {
Icon: ExampleIcon,
},
},
];
```
Reload Lens and you will see that the menu item text has changed to "Hello Lens".
@ -70,6 +70,6 @@ To debug your extension, please see our instructions on [Testing Extensions](../
To dive deeper, consider looking at [Common Capabilities](../capabilities/common-capabilities.md), [Styling](../capabilities/styling.md), or [Extension Anatomy](anatomy.md).
If you find problems with the Lens Extension Generator, or have feature requests, you are welcome to raise an [issue](https://github.com/lensapp/generator-lens-ext/issues).
You can find the latest Lens contribution guidelines [here](https://docs.k8slens.dev/latest/contributing).
You can find the latest Lens contribution guidelines [here](https://docs.k8slens.dev/contributing).
The Generator source code is hosted at [GitHub](https://github.com/lensapp/generator-lens-ext).

View File

@ -771,7 +771,7 @@ Construct the table using the `Renderer.Component.Table` and related elements.
For each pod the name, age, and status are obtained using the `Renderer.K8sApi.Pod` methods.
The table is constructed using the `Renderer.Component.Table` and related elements.
See [Component documentation](https://docs.k8slens.dev/latest/extensions/api/modules/_renderer_api_components_/) for further details.
See [Component documentation](https://api-docs.k8slens.dev/latest/extensions/api/modules/Renderer.Component/) for further details.
### `kubeObjectStatusTexts`

View File

@ -24,7 +24,7 @@
},
},
},
prometheus+:: {
kubernetesControlPlane+:: {
serviceMonitorKubelet+: {
spec+: {
endpoints: std.map(function(endpoint)

View File

@ -3,7 +3,7 @@
"productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes",
"homepage": "https://github.com/lensapp/lens",
"version": "6.1.0",
"version": "6.2.0-alpha.0",
"main": "static/build/main.js",
"copyright": "© 2022 OpenLens Authors",
"license": "MIT",
@ -246,7 +246,7 @@
"history": "^4.10.1",
"http-proxy": "^1.18.1",
"immer": "^9.0.15",
"joi": "^17.6.2",
"joi": "^17.6.3",
"js-yaml": "^4.1.0",
"jsdom": "^16.7.0",
"lodash": "^4.17.15",
@ -259,7 +259,7 @@
"mobx-utils": "^6.0.4",
"mock-fs": "^5.1.4",
"moment": "^2.29.4",
"moment-timezone": "^0.5.37",
"moment-timezone": "^0.5.38",
"monaco-editor": "^0.29.1",
"monaco-editor-webpack-plugin": "^5.0.0",
"node-fetch": "^2.6.7",
@ -298,10 +298,10 @@
"@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.8",
"@sentry/types": "^6.19.7",
"@swc/cli": "^0.1.57",
"@swc/core": "^1.3.5",
"@swc/core": "^1.3.9",
"@swc/jest": "^0.2.23",
"@testing-library/dom": "^7.31.2",
"@testing-library/jest-dom": "^5.16.5",
@ -331,7 +331,7 @@
"@types/md5-file": "^4.0.2",
"@types/mini-css-extract-plugin": "^2.4.0",
"@types/mock-fs": "^4.13.1",
"@types/node": "^16.11.64",
"@types/node": "^16.11.68",
"@types/node-fetch": "^2.6.2",
"@types/npm": "^2.0.32",
"@types/proper-lockfile": "^4.1.2",
@ -350,7 +350,7 @@
"@types/semver": "^7.3.12",
"@types/sharp": "^0.31.0",
"@types/spdy": "^3.4.5",
"@types/tar": "^6.1.2",
"@types/tar": "^6.1.3",
"@types/tar-stream": "^2.2.2",
"@types/tcp-port-used": "^1.0.1",
"@types/tempy": "^0.3.0",
@ -361,8 +361,8 @@
"@types/webpack-dev-server": "^4.7.2",
"@types/webpack-env": "^1.18.0",
"@types/webpack-node-externals": "^2.5.3",
"@typescript-eslint/eslint-plugin": "^5.39.0",
"@typescript-eslint/parser": "^5.39.0",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"adr": "^1.4.3",
"ansi_up": "^5.1.0",
"chart.js": "^2.9.4",
@ -374,18 +374,17 @@
"css-loader": "^6.7.1",
"deepdash": "^5.3.9",
"dompurify": "^2.4.0",
"electron": "^19.1.1",
"electron-builder": "^23.3.3",
"electron": "^19.1.3",
"electron-builder": "^23.6.0",
"electron-notarize": "^0.3.0",
"esbuild": "^0.15.10",
"esbuild": "^0.15.12",
"esbuild-loader": "^2.20.0",
"eslint": "^8.24.0",
"eslint": "^8.25.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-react": "7.31.8",
"eslint-plugin-react": "7.31.10",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unused-imports": "^2.0.0",
"flex.box": "^3.4.4",
"fork-ts-checker-webpack-plugin": "^6.5.2",
"gunzip-maybe": "^1.4.2",
"html-webpack-plugin": "^5.5.0",
@ -402,8 +401,8 @@
"node-gyp": "^8.3.0",
"node-loader": "^2.0.0",
"nodemon": "^2.0.20",
"playwright": "^1.26.1",
"postcss": "^8.4.17",
"playwright": "^1.27.1",
"postcss": "^8.4.18",
"postcss-loader": "^6.2.1",
"query-string": "^7.1.1",
"randomcolor": "^0.6.2",
@ -411,7 +410,7 @@
"react-refresh": "^0.14.0",
"react-refresh-typescript": "^2.0.7",
"react-router-dom": "^5.3.4",
"react-select": "^5.4.0",
"react-select": "^5.5.4",
"react-select-event": "^5.5.1",
"react-table": "^7.8.0",
"react-window": "^1.8.7",
@ -419,13 +418,13 @@
"sass-loader": "^12.6.0",
"sharp": "^0.31.1",
"style-loader": "^3.3.1",
"tailwindcss": "^3.1.8",
"tailwindcss": "^3.2.0",
"tar-stream": "^2.2.0",
"ts-loader": "^9.4.1",
"ts-node": "^10.9.1",
"type-fest": "^2.14.0",
"typed-emitter": "^1.4.0",
"typedoc": "0.23.15",
"typedoc": "0.23.17",
"typedoc-plugin-markdown": "^3.13.6",
"typescript": "^4.8.4",
"typescript-plugin-css-modules": "^3.4.0",

View File

@ -1,22 +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 type { AppPaths } from "./app-path-injection-token";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
export type AppPathsChannel = RequestChannel<void, AppPaths>;
const appPathsChannelInjectable = getInjectable({
id: "app-paths-channel",
instantiate: (): AppPathsChannel => ({
id: "app-paths",
}),
injectionToken: messageChannelInjectionToken,
});
export default appPathsChannelInjectable;

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { AppPaths } from "./app-path-injection-token";
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
export type AppPathsChannel = RequestChannel<void, AppPaths>;
export const appPathsChannel: AppPathsChannel = {
id: "app-paths",
};

View File

@ -0,0 +1,11 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
export type RestartAndInstallUpdateChannel = MessageChannel<void>;
export const restartAndInstallUpdateChannel: RestartAndInstallUpdateChannel = {
id: "restart-and-install-update-channel",
};

View File

@ -1,21 +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 type { MessageChannel } from "../../utils/channel/message-channel-injection-token";
import { messageChannelInjectionToken } from "../../utils/channel/message-channel-injection-token";
export type RestartAndInstallUpdateChannel = MessageChannel;
const restartAndInstallUpdateChannel = getInjectable({
id: "restart-and-install-update-channel",
instantiate: (): RestartAndInstallUpdateChannel => ({
id: "restart-and-install-update-channel",
}),
injectionToken: messageChannelInjectionToken,
});
export default restartAndInstallUpdateChannel;

View File

@ -19,7 +19,7 @@ const selectedUpdateChannelInjectable = getInjectable({
instantiate: (di): SelectedUpdateChannel => {
const defaultUpdateChannel = di.inject(defaultUpdateChannelInjectable);
const state = observable.box<UpdateChannel>();
const state = observable.box<UpdateChannel>(undefined, { deep: false });
return {
value: computed(() => state.get() ?? defaultUpdateChannel.get()),

View File

@ -0,0 +1,56 @@
/**
* 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 { RequestInit, Response } from "node-fetch";
import type { AsyncResult } from "../utils/async-result";
import fetchInjectable from "./fetch.injectable";
export interface DownloadBinaryOptions {
signal?: AbortSignal | null | undefined;
}
export type DownloadBinary = (url: string, opts?: DownloadBinaryOptions) => Promise<AsyncResult<Buffer, string>>;
const downloadBinaryInjectable = getInjectable({
id: "download-binary",
instantiate: (di): DownloadBinary => {
const fetch = di.inject(fetchInjectable);
return async (url, opts) => {
let result: Response;
try {
// TODO: upgrade node-fetch once we switch to ESM
result = await fetch(url, opts as RequestInit);
} catch (error) {
return {
callWasSuccessful: false,
error: String(error),
};
}
if (result.status < 200 || 300 <= result.status) {
return {
callWasSuccessful: false,
error: result.statusText,
};
}
try {
return {
callWasSuccessful: true,
response: await result.buffer(),
};
} catch (error) {
return {
callWasSuccessful: false,
error: String(error),
};
}
};
},
});
export default downloadBinaryInjectable;

View File

@ -0,0 +1,56 @@
/**
* 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 { RequestInit, Response } from "node-fetch";
import type { JsonValue } from "type-fest";
import type { AsyncResult } from "../utils/async-result";
import fetchInjectable from "./fetch.injectable";
export interface DownloadJsonOptions {
signal?: AbortSignal | null | undefined;
}
export type DownloadJson = (url: string, opts?: DownloadJsonOptions) => Promise<AsyncResult<JsonValue, string>>;
const downloadJsonInjectable = getInjectable({
id: "download-json",
instantiate: (di): DownloadJson => {
const fetch = di.inject(fetchInjectable);
return async (url, opts) => {
let result: Response;
try {
result = await fetch(url, opts as RequestInit);
} catch (error) {
return {
callWasSuccessful: false,
error: String(error),
};
}
if (result.status < 200 || 300 <= result.status) {
return {
callWasSuccessful: false,
error: result.statusText,
};
}
try {
return {
callWasSuccessful: true,
response: await result.json(),
};
} catch (error) {
return {
callWasSuccessful: false,
error: String(error),
};
}
};
},
});
export default downloadJsonInjectable;

View File

@ -3,10 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { RequestInfo, RequestInit, Response } from "node-fetch";
import fetch from "node-fetch";
import type { RequestInit, Response } from "node-fetch";
export type Fetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>;
export type Fetch = (url: string, init?: RequestInit) => Promise<Response>;
const fetchInjectable = getInjectable({
id: "fetch",

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
/**
* Creates an AbortController with an associated timeout
* @param timeout The number of milliseconds before this controller will auto abort
*/
export function withTimeout(timeout: number): AbortController {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
controller.signal.addEventListener("abort", () => clearTimeout(id));
return controller;
}

View File

@ -1,22 +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 { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
import type { MessageChannel } from "../utils/channel/message-channel-injection-token";
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
export type AppNavigationChannel = MessageChannel<string>;
const appNavigationChannelInjectable = getInjectable({
id: "app-navigation-channel",
instantiate: (): AppNavigationChannel => ({
id: IpcRendererNavigationEvents.NAVIGATE_IN_APP,
}),
injectionToken: messageChannelInjectionToken,
});
export default appNavigationChannelInjectable;

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 { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
export type AppNavigationChannel = MessageChannel<string>;
export const appNavigationChannel: AppNavigationChannel = {
id: IpcRendererNavigationEvents.NAVIGATE_IN_APP,
};

View File

@ -1,22 +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 { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
import type { MessageChannel } from "../utils/channel/message-channel-injection-token";
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
export type ClusterFrameNavigationChannel = MessageChannel<string>;
const clusterFrameNavigationChannelInjectable = getInjectable({
id: "cluster-frame-navigation-channel",
instantiate: (): ClusterFrameNavigationChannel => ({
id: IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER,
}),
injectionToken: messageChannelInjectionToken,
});
export default clusterFrameNavigationChannelInjectable;

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 { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
export type ClusterFrameNavigationChannel = MessageChannel<string>;
export const clusterFrameNavigationChannel: ClusterFrameNavigationChannel = {
id: IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER,
};

View File

@ -0,0 +1,25 @@
/**
* 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 isAllowedResourceInjectable from "../../../../../utils/is-allowed-resource.injectable";
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
const leasesRouteInjectable = getInjectable({
id: "leases",
instantiate: (di) => {
const isAllowedResource = di.inject(isAllowedResourceInjectable, "leases");
return {
path: "/leases",
clusterFrame: true,
isEnabled: isAllowedResource,
};
},
injectionToken: frontEndRouteInjectionToken,
});
export default leasesRouteInjectable;

View File

@ -0,0 +1,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 leasesRouteInjectable from "./leases-route.injectable";
import { navigateToRouteInjectionToken } from "../../../../navigate-to-route-injection-token";
const navigateToLeasesInjectable = getInjectable({
id: "navigate-to-leases",
instantiate: (di) => {
const navigateToRoute = di.inject(navigateToRouteInjectionToken);
const route = di.inject(leasesRouteInjectable);
return () => navigateToRoute(route);
},
});
export default navigateToLeasesInjectable;

View File

@ -5,22 +5,33 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { ExecFileOptions } from "child_process";
import { execFile } from "child_process";
import { promisify } from "util";
import type { AsyncResult } from "../utils/async-result";
export type ExecFile = (filePath: string, args: string[], options: ExecFileOptions) => Promise<string>;
export interface ExecFile {
(filePath: string, args: string[], options: ExecFileOptions): Promise<AsyncResult<string, { stderr: string; error: Error }>>;
}
const execFileInjectable = getInjectable({
id: "exec-file",
instantiate: (): ExecFile => {
const asyncExecFile = promisify(execFile);
return async (filePath, args, options) => {
const result = await asyncExecFile(filePath, args, options);
return result.stdout;
};
},
instantiate: (): ExecFile => (filePath, args, options) => new Promise((resolve) => {
execFile(filePath, args, options, (error, stdout, stderr) => {
if (error) {
resolve({
callWasSuccessful: false,
error: {
error,
stderr,
},
});
} else {
resolve({
callWasSuccessful: true,
response: stdout,
});
}
});
}),
causesSideEffects: true,
});

View File

@ -2,22 +2,12 @@
* 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 { HelmRepo } from "./helm-repo";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
import type { AsyncResult } from "../utils/async-result";
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
export type AddHelmRepositoryChannel = RequestChannel<HelmRepo, AsyncResult<string>>;
const addHelmRepositoryChannelInjectable = getInjectable({
export const addHelmRepositoryChannel: AddHelmRepositoryChannel = {
id: "add-helm-repository-channel",
instantiate: (): AddHelmRepositoryChannel => ({
id: "add-helm-repository-channel",
}),
injectionToken: requestChannelInjectionToken,
});
export default addHelmRepositoryChannelInjectable;
};

View File

@ -1,23 +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 type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import type { HelmRepo } from "./helm-repo";
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
import type { AsyncResult } from "../utils/async-result";
export type GetHelmRepositoriesChannel = RequestChannel<void, AsyncResult<HelmRepo[]>>;
const getActiveHelmRepositoriesChannelInjectable = getInjectable({
id: "get-active-helm-repositories-channel",
instantiate: (): GetHelmRepositoriesChannel => ({
id: "get-helm-active-list-repositories",
}),
injectionToken: requestChannelInjectionToken,
});
export default getActiveHelmRepositoriesChannelInjectable;

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { HelmRepo } from "./helm-repo";
import type { AsyncResult } from "../utils/async-result";
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
export type GetActiveHelmRepositoriesChannel = RequestChannel<void, AsyncResult<HelmRepo[]>>;
export const getActiveHelmRepositoriesChannel: GetActiveHelmRepositoriesChannel = {
id: "get-helm-active-list-repositories",
};

View File

@ -1,22 +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 type { HelmRepo } from "./helm-repo";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../utils/channel/request-channel-injection-token";
export type RemoveHelmRepositoryChannel = RequestChannel<HelmRepo>;
const removeHelmRepositoryChannelInjectable = getInjectable({
id: "remove-helm-repository-channel",
instantiate: (): RemoveHelmRepositoryChannel => ({
id: "remove-helm-repository-channel",
}),
injectionToken: requestChannelInjectionToken,
});
export default removeHelmRepositoryChannelInjectable;

View File

@ -0,0 +1,13 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { AsyncResult } from "../utils/async-result";
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
import type { HelmRepo } from "./helm-repo";
export type RemoveHelmRepositoryChannel = RequestChannel<HelmRepo, AsyncResult<string, string>>;
export const removeHelmRepositoryChannel: RemoveHelmRepositoryChannel = {
id: "remove-helm-repository-channel",
};

View File

@ -19,6 +19,7 @@ export * from "./events.api";
export * from "./horizontal-pod-autoscaler.api";
export * from "./ingress.api";
export * from "./job.api";
export * from "./lease.api";
export * from "./limit-range.api";
export * from "./namespace.api";
export * from "./network-policy.api";

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 assert from "assert";
import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import { LeaseApi } from "./lease.api";
import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
const leaseApiInjectable = getInjectable({
id: "lease-api",
instantiate: (di) => {
assert(di.inject(storesAndApisCanBeCreatedInjectionToken), "leaseApi is only available in certain environments");
return new LeaseApi();
},
injectionToken: kubeApiInjectionToken,
});
export default leaseApiInjectable;

View File

@ -0,0 +1,56 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DerivedKubeApiOptions, IgnoredKubeApiOptions } from "../kube-api";
import { KubeApi } from "../kube-api";
import type { NamespaceScopedMetadata } from "../kube-object";
import { KubeObject } from "../kube-object";
export interface LeaseSpec {
acquireTime?: string;
holderIdentity: string;
leaseDurationSeconds: number;
leaseTransitions?: number;
renewTime: string;
}
export class Lease extends KubeObject<
NamespaceScopedMetadata,
void,
LeaseSpec
> {
static readonly kind = "Lease";
static readonly namespaced = true;
static readonly apiBase = "/apis/coordination.k8s.io/v1/leases";
getAcquireTime(): string {
return this.spec.acquireTime || "";
}
getHolderIdentity(): string {
return this.spec.holderIdentity;
}
getLeaseDurationSeconds(): number {
return this.spec.leaseDurationSeconds;
}
getLeaseTransitions(): number | undefined {
return this.spec.leaseTransitions;
}
getRenewTime(): string {
return this.spec.renewTime;
}
}
export class LeaseApi extends KubeApi<Lease> {
constructor(opts: DerivedKubeApiOptions & IgnoredKubeApiOptions = {}) {
super({
...opts,
objectConstructor: Lease,
});
}
}

View File

@ -4,7 +4,7 @@
*/
export type KubeResource =
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" |
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" |
"secrets" | "configmaps" | "ingresses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |
"pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "jobs" | "cronjobs" |
"endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" |
@ -35,6 +35,7 @@ export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
"jobs": { kind: "Job", group: "batch" },
"namespaces": { kind: "Namespace" },
"limitranges": { kind: "LimitRange" },
"leases": { kind: "Lease" },
"networkpolicies": { kind: "NetworkPolicy", group: "networking.k8s.io" },
"nodes": { kind: "Node" },
"persistentvolumes": { kind: "PersistentVolume" },

View File

@ -1,21 +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 type { MessageChannel } from "../utils/channel/message-channel-injection-token";
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
export type RootFrameRenderedChannel = MessageChannel;
const rootFrameRenderedChannelInjectable = getInjectable({
id: "root-frame-rendered-channel",
instantiate: (): RootFrameRenderedChannel => ({
id: "root-frame-rendered",
}),
injectionToken: messageChannelInjectionToken,
});
export default rootFrameRenderedChannelInjectable;

View File

@ -0,0 +1,11 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token";
export type RootFrameHasRenderedChannel = MessageChannel<void>;
export const rootFrameHasRenderedChannel: RootFrameHasRenderedChannel = {
id: "root-frame-rendered",
};

View File

@ -0,0 +1,13 @@
/**
* 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 userStoreInjectable from "./user-store.injectable";
const resolvedShellInjectable = getInjectable({
id: "resolved-shell",
instantiate: (di) => di.inject(userStoreInjectable).resolvedShell,
});
export default resolvedShellInjectable;

View File

@ -6,7 +6,8 @@
// Convert object's keys to camelCase format
import { camelCase } from "lodash";
import type { SingleOrMany } from "./types";
import { isObject } from "./type-narrowing";
import { isObject, isString } from "./type-narrowing";
import * as object from "./objects";
export function toCamelCase<T extends Record<string, unknown>[]>(obj: T): T;
export function toCamelCase<T extends Record<string, unknown>>(obj: T): T;
@ -17,11 +18,10 @@ export function toCamelCase(obj: SingleOrMany<Record<string, unknown> | unknown>
}
if (isObject(obj)) {
return Object.fromEntries(
Object.entries(obj)
.map(([key, value]) => {
return [camelCase(key), toCamelCase(value)];
}),
return object.fromEntries(
object.entries(obj)
.filter((pair): pair is [string, unknown] => isString(pair[0]))
.map(([key, value]) => [camelCase(key), isObject(value) ? toCamelCase(value) : value]),
);
}

View File

@ -4,31 +4,31 @@
*/
import type { DiContainer } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import type { MessageToChannel } from "./message-to-channel-injection-token";
import { messageToChannelInjectionToken } from "./message-to-channel-injection-token";
import type { SendMessageToChannel } from "./message-to-channel-injection-token";
import { sendMessageToChannelInjectionToken } from "./message-to-channel-injection-token";
import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
import type { LensWindow } from "../../../main/start-main-application/lens-window/application-window/create-lens-window.injectable";
import type { MessageChannel } from "./message-channel-listener-injection-token";
import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token";
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 type { RequestChannel } 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";
import { runInAction } from "mobx";
import type { RequestChannelHandler } from "../../../main/utils/channel/channel-listeners/listener-tokens";
import { getRequestChannelListenerInjectable } from "../../../main/utils/channel/channel-listeners/listener-tokens";
type TestMessageChannel = MessageChannel<string>;
type TestRequestChannel = RequestChannel<string, string>;
describe("channel", () => {
describe("messaging from main to renderer, given listener for channel in a window and application has started", () => {
let testMessageChannel: TestMessageChannel;
let messageListenerInWindowMock: jest.Mock;
let mainDi: DiContainer;
let messageToChannel: MessageToChannel;
let messageToChannel: SendMessageToChannel;
let builder: ApplicationBuilder;
beforeEach(async () => {
@ -39,24 +39,17 @@ describe("channel", () => {
const testChannelListenerInTestWindowInjectable = getInjectable({
id: "test-channel-listener-in-test-window",
instantiate: (di) => ({
channel: di.inject(testMessageChannelInjectable),
instantiate: () => ({
channel: testMessageChannel,
handler: messageListenerInWindowMock,
}),
injectionToken: messageChannelListenerInjectionToken,
});
builder.beforeApplicationStart((mainDi) => {
runInAction(() => {
mainDi.register(testMessageChannelInjectable);
});
});
builder.beforeWindowStart((windowDi) => {
runInAction(() => {
windowDi.register(testChannelListenerInTestWindowInjectable);
windowDi.register(testMessageChannelInjectable);
});
});
@ -64,8 +57,7 @@ describe("channel", () => {
await builder.startHidden();
testMessageChannel = mainDi.inject(testMessageChannelInjectable);
messageToChannel = mainDi.inject(messageToChannelInjectionToken);
messageToChannel = mainDi.inject(sendMessageToChannelInjectionToken);
});
describe("given window is started", () => {
@ -109,9 +101,8 @@ describe("channel", () => {
});
describe("messaging from renderer to main, given listener for channel in a main and application has started", () => {
let testMessageChannel: TestMessageChannel;
let messageListenerInMainMock: jest.Mock;
let messageToChannel: MessageToChannel;
let messageToChannel: SendMessageToChannel;
beforeEach(async () => {
const applicationBuilder = getApplicationBuilder();
@ -121,9 +112,8 @@ describe("channel", () => {
const testChannelListenerInMainInjectable = getInjectable({
id: "test-channel-listener-in-main",
instantiate: (di) => ({
channel: di.inject(testMessageChannelInjectable),
instantiate: () => ({
channel: testMessageChannel,
handler: messageListenerInMainMock,
}),
@ -133,13 +123,6 @@ describe("channel", () => {
applicationBuilder.beforeApplicationStart((mainDi) => {
runInAction(() => {
mainDi.register(testChannelListenerInMainInjectable);
mainDi.register(testMessageChannelInjectable);
});
});
applicationBuilder.beforeWindowStart((windowDi) => {
runInAction(() => {
windowDi.register(testMessageChannelInjectable);
});
});
@ -147,8 +130,7 @@ describe("channel", () => {
const windowDi = applicationBuilder.applicationWindow.only.di;
testMessageChannel = windowDi.inject(testMessageChannelInjectable);
messageToChannel = windowDi.inject(messageToChannelInjectionToken);
messageToChannel = windowDi.inject(sendMessageToChannelInjectionToken);
});
it("when sending message, triggers listener in main", () => {
@ -159,8 +141,7 @@ describe("channel", () => {
});
describe("requesting from main in renderer, given listener for channel in a main and application has started", () => {
let testRequestChannel: TestRequestChannel;
let requestListenerInMainMock: AsyncFnMock<(arg: string) => string>;
let requestListenerInMainMock: AsyncFnMock<RequestChannelHandler<TestRequestChannel>>;
let requestFromChannel: RequestFromChannel;
beforeEach(async () => {
@ -168,28 +149,14 @@ describe("channel", () => {
requestListenerInMainMock = asyncFn();
const testChannelListenerInMainInjectable = getInjectable({
id: "test-channel-listener-in-main",
instantiate: (di) => ({
channel: di.inject(testRequestChannelInjectable),
handler: requestListenerInMainMock,
}),
injectionToken: requestChannelListenerInjectionToken,
const testChannelListenerInMainInjectable = getRequestChannelListenerInjectable({
channel: testRequestChannel,
handler: () => requestListenerInMainMock,
});
applicationBuilder.beforeApplicationStart((mainDi) => {
runInAction(() => {
mainDi.register(testChannelListenerInMainInjectable);
mainDi.register(testRequestChannelInjectable);
});
});
applicationBuilder.beforeWindowStart((windowDi) => {
runInAction(() => {
windowDi.register(testRequestChannelInjectable);
});
});
@ -197,8 +164,6 @@ describe("channel", () => {
const windowDi = applicationBuilder.applicationWindow.only.di;
testRequestChannel = windowDi.inject(testRequestChannelInjectable);
requestFromChannel = windowDi.inject(
requestFromChannelInjectionToken,
);
@ -230,21 +195,37 @@ describe("channel", () => {
});
});
});
it("when registering multiple handlers for the same channel, throws", async () => {
const applicationBuilder = getApplicationBuilder();
const testChannelListenerInMainInjectable = getRequestChannelListenerInjectable({
channel: testRequestChannel,
handler: () => () => "some-value",
});
const testChannelListenerInMain2Injectable = getRequestChannelListenerInjectable({
channel: testRequestChannel,
handler: () => () => "some-other-value",
});
testChannelListenerInMain2Injectable.id += "2";
applicationBuilder.beforeApplicationStart((mainDi) => {
runInAction(() => {
mainDi.register(testChannelListenerInMainInjectable);
mainDi.register(testChannelListenerInMain2Injectable);
});
});
await expect(applicationBuilder.render()).rejects.toThrow('Tried to register a multiple channel handlers for "some-request-channel-id", only one handler is supported for a request channel.');
});
});
const testMessageChannelInjectable = getInjectable({
id: "some-message-test-channel",
const testMessageChannel: TestMessageChannel = {
id: "some-message-channel-id",
};
instantiate: (): TestMessageChannel => ({
id: "some-message-channel-id",
}),
});
const testRequestChannelInjectable = getInjectable({
id: "some-request-test-channel",
instantiate: (): TestRequestChannel => ({
id: "some-request-channel-id",
}),
});
const testRequestChannel: TestRequestChannel = {
id: "some-request-channel-id",
};

View File

@ -3,14 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { MessageChannel } from "./message-channel-injection-token";
import type { MessageChannelListener } from "./message-channel-listener-injection-token";
import type { Disposer } from "../disposer";
import type { MessageChannel, MessageChannelListener } from "./message-channel-listener-injection-token";
export type EnlistMessageChannelListener = <
TChannel extends MessageChannel<any>,
>(listener: MessageChannelListener<TChannel>) => () => void;
export type EnlistMessageChannelListener = (listener: MessageChannelListener<MessageChannel<unknown>>) => Disposer;
export const enlistMessageChannelListenerInjectionToken =
getInjectionToken<EnlistMessageChannelListener>({
id: "enlist-message-channel-listener",
});
export const enlistMessageChannelListenerInjectionToken = getInjectionToken<EnlistMessageChannelListener>({
id: "enlist-message-channel-listener",
});

View File

@ -1,16 +0,0 @@
/**
* 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 { RequestChannel } from "./request-channel-injection-token";
import type { RequestChannelListener } from "./request-channel-listener-injection-token";
export type EnlistRequestChannelListener = <
TChannel extends RequestChannel<any, any>,
>(listener: RequestChannelListener<TChannel>) => () => void;
export const enlistRequestChannelListenerInjectionToken =
getInjectionToken<EnlistRequestChannelListener>({
id: "enlist-request-channel-listener",
});

View File

@ -1,32 +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 { 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 { enlistMessageChannelListenerInjectionToken } from "./enlist-message-channel-listener-injection-token";
import { enlistRequestChannelListenerInjectionToken } from "./enlist-request-channel-listener-injection-token";
const listeningOfChannelsInjectable = getInjectable({
id: "listening-of-channels",
instantiate: (di) => {
const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken);
const enlistRequestChannelListener = di.inject(enlistRequestChannelListenerInjectionToken);
const messageChannelListeners = di.injectMany(messageChannelListenerInjectionToken);
const requestChannelListeners = di.injectMany(requestChannelListenerInjectionToken);
return getStartableStoppable("listening-of-channels", () => {
const messageChannelDisposers = messageChannelListeners.map(enlistMessageChannelListener);
const requestChannelDisposers = requestChannelListeners.map(enlistRequestChannelListener);
return disposer(...messageChannelDisposers, ...requestChannelDisposers);
});
},
});
export default listeningOfChannelsInjectable;

View File

@ -0,0 +1,25 @@
/**
* 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 { getStartableStoppable } from "../get-startable-stoppable";
import { disposer } from "../index";
import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token";
import { enlistMessageChannelListenerInjectionToken } from "./enlist-message-channel-listener-injection-token";
const listeningOnMessageChannelsInjectable = getInjectable({
id: "listening-on-message-channels",
instantiate: (di) => {
const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken);
const messageChannelListeners = di.injectMany(messageChannelListenerInjectionToken);
return getStartableStoppable("listening-on-channels", () => (
disposer(messageChannelListeners.map(enlistMessageChannelListener))
));
},
});
export default listeningOnMessageChannelsInjectable;

View File

@ -1,16 +0,0 @@
/**
* 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 { JsonValue } from "type-fest";
export interface MessageChannel<Message extends JsonValue | void = void> {
id: string;
_messageSignature?: Message;
}
export const messageChannelInjectionToken = getInjectionToken<MessageChannel<any>>({
id: "message-channel",
});

View File

@ -2,17 +2,50 @@
* 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 { MessageChannel } from "./message-channel-injection-token";
import type { DiContainerForInjection } from "@ogre-tools/injectable";
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
export interface MessageChannelListener<TChannel extends MessageChannel<any>> {
channel: TChannel;
handler: (value: SetRequired<TChannel, "_messageSignature">["_messageSignature"]) => void;
export interface MessageChannel<Message> {
id: string;
_messageSignature?: Message; // only used to mark `Message` as used
}
export const messageChannelListenerInjectionToken = getInjectionToken<MessageChannelListener<MessageChannel<any>>>(
export type MessageChannelHandler<Channel> = Channel extends MessageChannel<infer Message>
? (message: Message) => void
: never;
export interface MessageChannelListener<Channel> {
channel: Channel;
handler: MessageChannelHandler<Channel>;
}
export const messageChannelListenerInjectionToken = getInjectionToken<MessageChannelListener<MessageChannel<unknown>>>(
{
id: "message-channel-listener",
},
);
export interface GetMessageChannelListenerInfo<
Channel extends MessageChannel<Message>,
Message,
> {
id: string;
channel: Channel;
handler: (di: DiContainerForInjection) => MessageChannelHandler<Channel>;
causesSideEffects?: boolean;
}
export function getMessageChannelListenerInjectable<
Channel extends MessageChannel<Message>,
Message,
>(info: GetMessageChannelListenerInfo<Channel, Message>) {
return getInjectable({
id: `${info.channel.id}-listener-${info.id}`,
instantiate: (di) => ({
channel: info.channel,
handler: info.handler(di),
}),
injectionToken: messageChannelListenerInjectionToken,
causesSideEffects: info.causesSideEffects,
});
}

View File

@ -3,21 +3,13 @@
* 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 { MessageChannel } from "./message-channel-injection-token";
import type { MessageChannel } from "./message-channel-listener-injection-token";
export interface MessageToChannel {
<TChannel extends MessageChannel<TMessage>, TMessage extends void>(
channel: TChannel,
): void;
<TChannel extends MessageChannel<any>>(
channel: TChannel,
message: SetRequired<TChannel, "_messageSignature">["_messageSignature"],
): void;
export interface SendMessageToChannel {
(channel: MessageChannel<void>): void;
<Message>(channel: MessageChannel<Message>, message: Message): void;
}
export const messageToChannelInjectionToken =
getInjectionToken<MessageToChannel>({
id: "message-to-message-channel",
});
export const sendMessageToChannelInjectionToken = getInjectionToken<SendMessageToChannel>({
id: "send-message-to-message-channel",
});

View File

@ -1,20 +0,0 @@
/**
* 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 { JsonValue } from "type-fest";
export interface RequestChannel<
Request extends JsonValue | void = void,
Response extends JsonValue | void = void,
> {
id: string;
_requestSignature?: Request;
_responseSignature?: Response;
}
export const requestChannelInjectionToken = getInjectionToken<RequestChannel<any, any>>({
id: "request-channel",
});

View File

@ -2,24 +2,9 @@
* 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 { RequestChannel } from "./request-channel-injection-token";
export interface RequestChannelListener<TChannel extends RequestChannel<any, any>> {
channel: TChannel;
handler: (
request: SetRequired<TChannel, "_requestSignature">["_requestSignature"]
) =>
| SetRequired<TChannel, "_responseSignature">["_responseSignature"]
| Promise<
SetRequired<TChannel, "_responseSignature">["_responseSignature"]
>;
export interface RequestChannel<Request, Response> {
id: string;
_requestSignature?: Request; // used only to mark `Request` as "used"
_responseSignature?: Response; // used only to mark `Response` as "used"
}
export const requestChannelListenerInjectionToken = getInjectionToken<RequestChannelListener<RequestChannel<any, any>>>(
{
id: "request-channel-listener",
},
);

View File

@ -3,19 +3,13 @@
* 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";
import type { RequestChannel } from "./request-channel-listener-injection-token";
export type RequestFromChannel = <
TChannel extends RequestChannel<any, any>,
>(
channel: TChannel,
...request: TChannel["_requestSignature"] extends void
? []
: [SetRequired<TChannel, "_requestSignature">["_requestSignature"]]
) => Promise<SetRequired<TChannel, "_responseSignature">["_responseSignature"]>;
export interface RequestFromChannel {
<Request, Response>(channel: RequestChannel<Request, Response>, request: Request): Promise<Response>;
<Response>(channel: RequestChannel<void, Response>): 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,6 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { SingleOrMany } from "./types";
export interface Disposer {
@ -17,9 +19,9 @@ export interface ExtendableDisposer extends Disposer {
push(...vals: (Disposer | ExtendableDisposer | Disposable)[]): void;
}
export function disposer(...items: (Disposer | Disposable | undefined | null)[]): ExtendableDisposer {
export function disposer(...items: SingleOrMany<Disposer | Disposable | undefined | null>[]): ExtendableDisposer {
return Object.assign(() => {
for (const item of items) {
for (const item of items.flat()) {
if (!item) {
continue;
}

View File

@ -1,53 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import request from "request";
import type { JsonValue } from "type-fest";
import { parse } from "./json";
export interface DownloadFileOptions {
url: string;
gzip?: boolean;
timeout?: number;
}
export interface DownloadFileTicket<T> {
url: string;
promise: Promise<T>;
cancel(): void;
}
export function downloadFile({ url, timeout, gzip = true }: DownloadFileOptions): DownloadFileTicket<Buffer> {
const fileChunks: Buffer[] = [];
const req = request(url, { gzip, timeout });
const promise: Promise<Buffer> = new Promise((resolve, reject) => {
req.on("data", (chunk: Buffer) => {
fileChunks.push(chunk);
});
req.once("error", err => {
reject({ url, err });
});
req.once("complete", () => {
resolve(Buffer.concat(fileChunks));
});
});
return {
url,
promise,
cancel() {
req.abort();
},
};
}
export function downloadJson(args: DownloadFileOptions): DownloadFileTicket<JsonValue> {
const { promise, ...rest } = downloadFile(args);
return {
promise: promise.then(res => parse(res.toString())),
...rest,
};
}

View File

@ -14,7 +14,6 @@ export * from "./convertMemory";
export * from "./debouncePromise";
export * from "./delay";
export * from "./disposer";
export * from "./downloadFile";
export * from "./escapeRegExp";
export * from "./formatDuration";
export * from "./getRandId";

View File

@ -1,21 +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 type { RequestChannel } from "../channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../channel/request-channel-injection-token";
export type ResolveSystemProxyChannel = RequestChannel<string, string>;
const resolveSystemProxyChannelInjectable = getInjectable({
id: "resolve-system-proxy-channel",
instantiate: (): ResolveSystemProxyChannel => ({
id: "resolve-system-proxy-channel",
}),
injectionToken: requestChannelInjectionToken,
});
export default resolveSystemProxyChannelInjectable;

View File

@ -0,0 +1,11 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { RequestChannel } from "../channel/request-channel-listener-injection-token";
export type ResolveSystemProxyChannel = RequestChannel<string, string>;
export const resolveSystemProxyChannel: ResolveSystemProxyChannel = {
id: "resolve-system-proxy-channel",
};

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { syncBoxChannel } from "./channels";
import { getMessageChannelListenerInjectable } from "../channel/message-channel-listener-injection-token";
import syncBoxStateInjectable from "./sync-box-state.injectable";
const syncBoxChannelListenerInjectable = getMessageChannelListenerInjectable({
id: "init",
channel: syncBoxChannel,
handler: (di) => ({ id, value }) => di.inject(syncBoxStateInjectable, id).set(value),
});
export default syncBoxChannelListenerInjectable;

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 type { MessageChannel } from "../channel/message-channel-listener-injection-token";
import type { RequestChannel } from "../channel/request-channel-listener-injection-token";
export type SyncBoxChannel = MessageChannel<{ id: string; value: any }>;
export const syncBoxChannel: SyncBoxChannel = {
id: "sync-box-channel",
};
export type SyncBoxInitialValueChannel = RequestChannel<
void,
{ id: string; value: any }[]
>;
export const syncBoxInitialValueChannel: SyncBoxInitialValueChannel = {
id: "sync-box-initial-value-channel",
};

View File

@ -5,17 +5,17 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { IObservableValue } from "mobx";
import { computed } from "mobx";
import syncBoxChannelInjectable from "./sync-box-channel.injectable";
import { messageToChannelInjectionToken } from "../channel/message-to-channel-injection-token";
import { syncBoxChannel } from "./channels";
import { sendMessageToChannelInjectionToken } from "../channel/message-to-channel-injection-token";
import syncBoxStateInjectable from "./sync-box-state.injectable";
import type { SyncBox } from "./sync-box-injection-token";
import { toJS } from "../toJS";
const createSyncBoxInjectable = getInjectable({
id: "create-sync-box",
instantiate: (di) => {
const syncBoxChannel = di.inject(syncBoxChannelInjectable);
const messageToChannel = di.inject(messageToChannelInjectionToken);
const messageToChannel = di.inject(sendMessageToChannelInjectionToken);
const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id);
return <Value>(id: string, initialValue: Value): SyncBox<Value> => {
@ -26,7 +26,7 @@ const createSyncBoxInjectable = getInjectable({
return {
id,
value: computed(() => state.get()),
value: computed(() => toJS(state.get())),
set: (value) => {
state.set(value);

View File

@ -0,0 +1,19 @@
/**
* 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 { MessageChannelHandler } from "../channel/message-channel-listener-injection-token";
import type { SyncBoxChannel } from "./channels";
import syncBoxStateInjectable from "./sync-box-state.injectable";
const syncBoxChannelHandlerInjectable = getInjectable({
id: "sync-box-channel-handler",
instantiate: (di): MessageChannelHandler<SyncBoxChannel> => {
const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id);
return ({ id, value }) => getSyncBoxState(id)?.set(value);
},
});
export default syncBoxChannelHandlerInjectable;

View File

@ -1,35 +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 type { SyncBoxChannel } from "./sync-box-channel.injectable";
import syncBoxChannelInjectable from "./sync-box-channel.injectable";
import syncBoxStateInjectable from "./sync-box-state.injectable";
import type { MessageChannelListener } from "../channel/message-channel-listener-injection-token";
import { messageChannelListenerInjectionToken } from "../channel/message-channel-listener-injection-token";
const syncBoxChannelListenerInjectable = getInjectable({
id: "sync-box-channel-listener",
instantiate: (di): MessageChannelListener<SyncBoxChannel> => {
const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id);
const channel = di.inject(syncBoxChannelInjectable);
return {
channel,
handler: ({ id, value }) => {
const target = getSyncBoxState(id);
if (target) {
target.set(value);
}
},
};
},
injectionToken: messageChannelListenerInjectionToken,
});
export default syncBoxChannelListenerInjectable;

View File

@ -1,21 +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 type { MessageChannel } from "../channel/message-channel-injection-token";
import { messageChannelInjectionToken } from "../channel/message-channel-injection-token";
export type SyncBoxChannel = MessageChannel<{ id: string; value: any }>;
const syncBoxChannelInjectable = getInjectable({
id: "sync-box-channel",
instantiate: (): SyncBoxChannel => ({
id: "sync-box-channel",
}),
injectionToken: messageChannelInjectionToken,
});
export default syncBoxChannelInjectable;

View File

@ -1,24 +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 type { RequestChannel } from "../channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../channel/request-channel-injection-token";
export type SyncBoxInitialValueChannel = RequestChannel<
void,
{ id: string; value: any }[]
>;
const syncBoxInitialValueChannelInjectable = getInjectable({
id: "sync-box-initial-value-channel",
instantiate: (): SyncBoxInitialValueChannel => ({
id: "sync-box-initial-value-channel",
}),
injectionToken: requestChannelInjectionToken,
});
export default syncBoxInitialValueChannelInjectable;

View File

@ -5,7 +5,6 @@
// Helper for working with tarball files (.tar, .tgz)
// Docs: https://github.com/npm/node-tar
import type { FileStat } from "tar";
import tar from "tar";
import path from "path";
import { parse } from "./json";
@ -35,7 +34,7 @@ export function readFileFromTar<ParseJson extends boolean>({ tarPath, filePath,
file: tarPath,
filter: entryPath => path.normalize(entryPath) === filePath,
sync: true,
onentry(entry: FileStat) {
onentry(entry) {
entry.on("data", chunk => {
fileChunks.push(chunk);
});
@ -63,7 +62,7 @@ export async function listTarEntries(filePath: string): Promise<string[]> {
await tar.list({
file: filePath,
onentry: (entry) => {
entries.push(path.normalize(entry.path as unknown as string));
entries.push(path.normalize(entry.path));
},
});

View File

@ -1,15 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { pipeline } from "@ogre-tools/fp";
import { defaultTo } from "lodash/fp";
import { withErrorSuppression } from "./with-error-suppression/with-error-suppression";
export const tentativeParseJson = (toBeParsed: any) => pipeline(
toBeParsed,
withErrorSuppression(JSON.parse),
defaultTo(toBeParsed),
);

View File

@ -1,15 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { pipeline } from "@ogre-tools/fp";
import { defaultTo } from "lodash/fp";
import { withErrorSuppression } from "./with-error-suppression/with-error-suppression";
export const tentativeStringifyJson = (toBeParsed: any) => pipeline(
toBeParsed,
withErrorSuppression(JSON.stringify),
defaultTo(toBeParsed),
);

View File

@ -103,7 +103,7 @@ export function isBoolean(val: unknown): val is boolean {
* checks if val is of type object and isn't null
* @param val the value to be checked
*/
export function isObject(val: unknown): val is object {
export function isObject(val: unknown): val is Record<string | symbol | number, unknown> {
return typeof val === "object" && val !== null;
}

View File

@ -123,7 +123,7 @@ export const apiKubePrefix = "/api-kube"; // k8s cluster apis
// Links
export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string;
export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ" as string;
export const supportUrl = "https://docs.k8slens.dev/latest/support/" as string;
export const supportUrl = "https://docs.k8slens.dev/support/" as string;
export const lensWebsiteWeblinkId = "lens-website-link";
export const lensDocumentationWeblinkId = "lens-documentation-link";
@ -132,4 +132,4 @@ export const lensTwitterWeblinkId = "lens-twitter-link";
export const lensBlogWeblinkId = "lens-blog-link";
export const kubernetesDocumentationWeblinkId = "kubernetes-documentation-link";
export const docsUrl = "https://docs.k8slens.dev/main" as string;
export const docsUrl = "https://docs.k8slens.dev" as string;

View File

@ -7,6 +7,7 @@ import { getGlobalOverride } from "../test-utils/get-global-override";
import applicationInformationInjectable from "./application-information.injectable";
export default getGlobalOverride(applicationInformationInjectable, () => ({
name: "some-product-name",
productName: "some-product-name",
version: "6.0.0",
build: {},

View File

@ -5,16 +5,16 @@
import { getInjectable } from "@ogre-tools/injectable";
import packageJson from "../../../package.json";
export type ApplicationInformation = Pick<typeof packageJson, "version" | "config" | "productName" | "copyright" | "description"> & {
export type ApplicationInformation = Pick<typeof packageJson, "version" | "config" | "productName" | "copyright" | "description" | "name"> & {
build: Partial<typeof packageJson["build"]> & { publish?: unknown[] };
};
const applicationInformationInjectable = getInjectable({
id: "application-information",
instantiate: (): ApplicationInformation => {
const { version, config, productName, build, copyright, description } = packageJson;
const { version, config, productName, build, copyright, description, name } = packageJson;
return { version, config, productName, build, copyright, description };
return { version, config, productName, build, copyright, description, name };
},
causesSideEffects: true,
});

View File

@ -7,7 +7,7 @@ import { getInjectionToken } from "@ogre-tools/injectable";
import { SemVer } from "semver";
import type { InitializableState } from "../initializable-state/create";
import { createInitializableState } from "../initializable-state/create";
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
import type { RequestChannel } from "../utils/channel/request-channel-listener-injection-token";
export const buildVersionInjectionToken = getInjectionToken<InitializableState<string>>({
id: "build-version-token",

View File

@ -182,6 +182,7 @@ exports[`extension special characters in page registrations renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -302,6 +303,7 @@ exports[`extension special characters in page registrations when navigating to r
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -182,6 +182,7 @@ exports[`navigate to extension page renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -302,6 +303,7 @@ exports[`navigate to extension page when extension navigates to child route rend
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -438,6 +440,7 @@ exports[`navigate to extension page when extension navigates to route with param
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -574,6 +577,7 @@ exports[`navigate to extension page when extension navigates to route without pa
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -710,6 +714,7 @@ exports[`navigate to extension page when extension navigates to route without pa
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -106,6 +106,7 @@ exports[`navigating between routes given route with optional path parameters whe
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -226,6 +227,7 @@ exports[`navigating between routes given route without path parameters when navi
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -182,6 +182,7 @@ exports[`add-cluster - navigation using application menu renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -278,7 +279,7 @@ exports[`add-cluster - navigation using application menu when navigating to add
</code>
file.
<a
href="https://docs.k8slens.dev/main/getting-started/add-cluster/"
href="https://docs.k8slens.dev/getting-started/add-cluster/"
rel="noreferrer"
target="_blank"
>
@ -386,6 +387,7 @@ exports[`add-cluster - navigation using application menu when navigating to add
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -201,6 +201,7 @@ exports[`force user to update when too long since update was downloaded when app
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -420,6 +421,7 @@ exports[`force user to update when too long since update was downloaded when app
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -686,6 +688,7 @@ exports[`force user to update when too long since update was downloaded when app
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -201,6 +201,7 @@ exports[`encourage user to update when sufficient time passed since update was d
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -402,6 +403,7 @@ exports[`encourage user to update when sufficient time passed since update was d
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -183,6 +183,7 @@ exports[`installing update using tray when started renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -384,6 +385,7 @@ exports[`installing update using tray when started when user checks for updates
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -585,6 +587,7 @@ exports[`installing update using tray when started when user checks for updates
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -804,6 +807,7 @@ exports[`installing update using tray when started when user checks for updates
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1023,6 +1027,7 @@ exports[`installing update using tray when started when user checks for updates
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1224,6 +1229,7 @@ exports[`installing update using tray when started when user checks for updates
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -183,6 +183,7 @@ exports[`installing update when started renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -384,6 +385,7 @@ exports[`installing update when started when user checks for updates renders 1`]
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -585,6 +587,7 @@ exports[`installing update when started when user checks for updates when new up
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -804,6 +807,7 @@ exports[`installing update when started when user checks for updates when new up
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1023,6 +1027,7 @@ exports[`installing update when started when user checks for updates when new up
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1224,6 +1229,7 @@ exports[`installing update when started when user checks for updates when no new
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -183,6 +183,7 @@ exports[`periodical checking of updates given updater is enabled and configurati
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -183,6 +183,7 @@ exports[`selection of update stability when started renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -222,7 +222,7 @@ describe("selection of update stability", () => {
it('given user changes update channel to "alpha", when user would close the application, installs the update for being stable enough', () => {
selectedUpdateChannel.setValue(updateChannels.alpha.id);
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(false);
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(true);
});
});
});

View File

@ -312,6 +312,7 @@ exports[`Deleting a cluster when an internal kubeconfig cluster is used when the
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -698,6 +699,7 @@ exports[`Deleting a cluster when the kubeconfig has multiple clusters when the d
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1117,6 +1119,7 @@ exports[`Deleting a cluster when the kubeconfig has multiple clusters when the d
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1615,6 +1618,7 @@ exports[`Deleting a cluster when the kubeconfig has multiple clusters when the d
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -2113,6 +2117,7 @@ exports[`Deleting a cluster when the kubeconfig has only one cluster when the di
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -1,20 +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 type { ClusterId } from "../../../../common/cluster-types";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../../../../common/utils/channel/request-channel-injection-token";
export type ClearClusterAsDeletingChannel = RequestChannel<ClusterId, void>;
const clearClusterAsDeletingChannelInjectable = getInjectable({
id: "clear-cluster-as-deleting-channel",
instantiate: (): ClearClusterAsDeletingChannel => ({
id: "clear-cluster-as-deleting",
}),
injectionToken: requestChannelInjectionToken,
});
export default clearClusterAsDeletingChannelInjectable;

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 { ClusterId } from "../../../../common/cluster-types";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
export type ClearClusterAsDeletingChannel = RequestChannel<ClusterId, void>;
export const clearClusterAsDeletingChannel: ClearClusterAsDeletingChannel = {
id: "clear-cluster-as-deleting",
};

View File

@ -1,20 +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 type { ClusterId } from "../../../../common/cluster-types";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../../../../common/utils/channel/request-channel-injection-token";
export type DeleteClusterChannel = RequestChannel<ClusterId, void>;
const deleteClusterChannelInjectable = getInjectable({
id: "delete-cluster-channel",
instantiate: (): DeleteClusterChannel => ({
id: "delete-cluster",
}),
injectionToken: requestChannelInjectionToken,
});
export default deleteClusterChannelInjectable;

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 { ClusterId } from "../../../../common/cluster-types";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
export type DeleteClusterChannel = RequestChannel<ClusterId, void>;
export const deleteClusterChannel: DeleteClusterChannel = {
id: "delete-cluster",
};

View File

@ -1,20 +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 type { ClusterId } from "../../../../common/cluster-types";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-injection-token";
import { requestChannelInjectionToken } from "../../../../common/utils/channel/request-channel-injection-token";
export type SetClusterAsDeletingChannel = RequestChannel<ClusterId, void>;
const setClusterAsDeletingChannelInjectable = getInjectable({
id: "set-cluster-as-deleting-channel",
instantiate: (): SetClusterAsDeletingChannel => ({
id: "set-cluster-as-deleting",
}),
injectionToken: requestChannelInjectionToken,
});
export default setClusterAsDeletingChannelInjectable;

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 { ClusterId } from "../../../../common/cluster-types";
import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token";
export type SetClusterAsDeletingChannel = RequestChannel<ClusterId, void>;
export const setClusterAsDeletingChannel: SetClusterAsDeletingChannel = {
id: "set-cluster-as-deleting",
};

View File

@ -1,23 +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 { requestChannelListenerInjectionToken } from "../../../../common/utils/channel/request-channel-listener-injection-token";
import clustersThatAreBeingDeletedInjectable from "../../../../main/cluster/are-being-deleted.injectable";
import clearClusterAsDeletingChannelInjectable from "../common/clear-as-deleting-channel.injectable";
const clearClusterAsDeletingChannelHandlerInjectable = getInjectable({
id: "clear-cluster-as-deleting-channel-handler",
instantiate: (di) => {
const clustersThatAreBeingDeleted = di.inject(clustersThatAreBeingDeletedInjectable);
return {
channel: di.inject(clearClusterAsDeletingChannelInjectable),
handler: (clusterId) => clustersThatAreBeingDeleted.delete(clusterId),
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default clearClusterAsDeletingChannelHandlerInjectable;

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import clustersThatAreBeingDeletedInjectable from "../../../../main/cluster/are-being-deleted.injectable";
import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
import { clearClusterAsDeletingChannel } from "../common/clear-as-deleting-channel";
const clearClusterAsDeletingChannelListenerInjectable = getRequestChannelListenerInjectable({
channel: clearClusterAsDeletingChannel,
handler: (di) => {
const clustersThatAreBeingDeleted = di.inject(clustersThatAreBeingDeletedInjectable);
return (clusterId) => {
clustersThatAreBeingDeleted.delete(clusterId);
};
},
});
export default clearClusterAsDeletingChannelListenerInjectable;

View File

@ -1,56 +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 appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
import clusterFramesInjectable from "../../../../common/cluster-frames.injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import deleteFileInjectable from "../../../../common/fs/delete-file.injectable";
import joinPathsInjectable from "../../../../common/path/join-paths.injectable";
import { requestChannelListenerInjectionToken } from "../../../../common/utils/channel/request-channel-listener-injection-token";
import deleteClusterChannelInjectable from "../common/delete-channel.injectable";
const deleteClusterChannelHandlerInjectable = getInjectable({
id: "delete-cluster-channel-handler",
instantiate: (di) => {
const appEventBus = di.inject(appEventBusInjectable);
const clusterStore = di.inject(clusterStoreInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable);
const deleteFile = di.inject(deleteFileInjectable);
return {
channel: di.inject(deleteClusterChannelInjectable),
handler: async (clusterId) =>{
appEventBus.emit({ name: "cluster", action: "remove" });
const cluster = clusterStore.getById(clusterId);
if (!cluster) {
return;
}
cluster.disconnect();
clusterFrames.delete(cluster.id);
// Remove from the cluster store as well, this should clear any old settings
clusterStore.clusters.delete(cluster.id);
try {
// remove the local storage file
const localStorageFilePath = joinPaths(directoryForLensLocalStorage, `${cluster.id}.json`);
await deleteFile(localStorageFilePath);
} catch {
// ignore error
}
},
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default deleteClusterChannelHandlerInjectable;

View File

@ -0,0 +1,51 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
import clusterFramesInjectable from "../../../../common/cluster-frames.injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import deleteFileInjectable from "../../../../common/fs/delete-file.injectable";
import joinPathsInjectable from "../../../../common/path/join-paths.injectable";
import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
import { deleteClusterChannel } from "../common/delete-channel";
const deleteClusterChannelListenerInjectable = getRequestChannelListenerInjectable({
channel: deleteClusterChannel,
handler: (di) => {
const appEventBus = di.inject(appEventBusInjectable);
const clusterStore = di.inject(clusterStoreInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable);
const deleteFile = di.inject(deleteFileInjectable);
return async (clusterId) => {
appEventBus.emit({ name: "cluster", action: "remove" });
const cluster = clusterStore.getById(clusterId);
if (!cluster) {
return;
}
cluster.disconnect();
clusterFrames.delete(cluster.id);
// Remove from the cluster store as well, this should clear any old settings
clusterStore.clusters.delete(cluster.id);
try {
// remove the local storage file
const localStorageFilePath = joinPaths(directoryForLensLocalStorage, `${cluster.id}.json`);
await deleteFile(localStorageFilePath);
} catch {
// ignore error
}
};
},
});
export default deleteClusterChannelListenerInjectable;

View File

@ -1,23 +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 { requestChannelListenerInjectionToken } from "../../../../common/utils/channel/request-channel-listener-injection-token";
import clustersThatAreBeingDeletedInjectable from "../../../../main/cluster/are-being-deleted.injectable";
import setClusterAsDeletingChannelInjectable from "../common/set-as-deleting-channel.injectable";
const setClusterAsDeletingChannelHandlerInjectable = getInjectable({
id: "set-cluster-as-deleting-channel-handler",
instantiate: (di) => {
const clustersThatAreBeingDeleted = di.inject(clustersThatAreBeingDeletedInjectable);
return {
channel: di.inject(setClusterAsDeletingChannelInjectable),
handler: (clusterId) => clustersThatAreBeingDeleted.add(clusterId),
};
},
injectionToken: requestChannelListenerInjectionToken,
});
export default setClusterAsDeletingChannelHandlerInjectable;

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import clustersThatAreBeingDeletedInjectable from "../../../../main/cluster/are-being-deleted.injectable";
import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens";
import { setClusterAsDeletingChannel } from "../common/set-as-deleting-channel";
const setClusterAsDeletingChannelHandlerInjectable = getRequestChannelListenerInjectable({
channel: setClusterAsDeletingChannel,
handler: (di) => {
const clustersThatAreBeingDeleted = di.inject(clustersThatAreBeingDeletedInjectable);
return (clusterId) => {
clustersThatAreBeingDeleted.add(clusterId);
};
},
});
export default setClusterAsDeletingChannelHandlerInjectable;

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { ClusterId } from "../../../../common/cluster-types";
import requestFromChannelInjectable from "../../../../renderer/utils/channel/request-from-channel.injectable";
import clearClusterAsDeletingChannelInjectable from "../common/clear-as-deleting-channel.injectable";
import { clearClusterAsDeletingChannel } from "../common/clear-as-deleting-channel";
export type RequestClearClusterAsDeleting = (clusterId: ClusterId) => Promise<void>;
@ -13,7 +13,6 @@ const requestClearClusterAsDeletingInjectable = getInjectable({
id: "request-clear-cluster-as-deleting",
instantiate: (di): RequestClearClusterAsDeleting => {
const requestChannel = di.inject(requestFromChannelInjectable);
const clearClusterAsDeletingChannel = di.inject(clearClusterAsDeletingChannelInjectable);
return (clusterId) => requestChannel(clearClusterAsDeletingChannel, clusterId);
},

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { ClusterId } from "../../../../common/cluster-types";
import requestFromChannelInjectable from "../../../../renderer/utils/channel/request-from-channel.injectable";
import deleteClusterChannelInjectable from "../common/delete-channel.injectable";
import { deleteClusterChannel } from "../common/delete-channel";
export type RequestDeleteCluster = (clusterId: ClusterId) => Promise<void>;
@ -13,7 +13,6 @@ const requestDeleteClusterInjectable = getInjectable({
id: "request-delete-cluster",
instantiate: (di): RequestDeleteCluster => {
const requestChannel = di.inject(requestFromChannelInjectable);
const deleteClusterChannel = di.inject(deleteClusterChannelInjectable);
return (clusterId) => requestChannel(deleteClusterChannel, clusterId);
},

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable";
import type { ClusterId } from "../../../../common/cluster-types";
import requestFromChannelInjectable from "../../../../renderer/utils/channel/request-from-channel.injectable";
import setClusterAsDeletingChannelInjectable from "../common/set-as-deleting-channel.injectable";
import { setClusterAsDeletingChannel } from "../common/set-as-deleting-channel";
export type RequestSetClusterAsDeleting = (clusterId: ClusterId) => Promise<void>;
@ -13,7 +13,6 @@ const requestSetClusterAsDeletingInjectable = getInjectable({
id: "request-set-cluster-as-deleting",
instantiate: (di): RequestSetClusterAsDeleting => {
const requestChannel = di.inject(requestFromChannelInjectable);
const setClusterAsDeletingChannel = di.inject(setClusterAsDeletingChannelInjectable);
return (clusterId) => requestChannel(setClusterAsDeletingChannel, clusterId);
},

View File

@ -264,6 +264,7 @@ exports[`Command Pallet: keyboard shortcut tests when on linux renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -546,6 +547,7 @@ exports[`Command Pallet: keyboard shortcut tests when on linux when pressing ESC
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -828,6 +830,7 @@ exports[`Command Pallet: keyboard shortcut tests when on linux when pressing SHI
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1122,6 +1125,7 @@ exports[`Command Pallet: keyboard shortcut tests when on linux when pressing SHI
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1323,6 +1327,7 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1524,6 +1529,7 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS when pressing ESC
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1725,6 +1731,7 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS when pressing SHI
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -1938,6 +1945,7 @@ exports[`Command Pallet: keyboard shortcut tests when on macOS when pressing SHI
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -182,6 +182,7 @@ exports[`extensions - navigation using application menu renders 1`] = `
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"
@ -274,7 +275,7 @@ exports[`extensions - navigation using application menu when navigating to exten
<p>
Add new features via Lens Extensions. Check out the
<a
href="https://docs.k8slens.dev/main/extensions/"
href="https://docs.k8slens.dev/extensions/"
rel="noreferrer"
target="_blank"
>
@ -408,6 +409,7 @@ exports[`extensions - navigation using application menu when navigating to exten
</div>
<div
class="StatusBar"
data-testid="status-bar"
>
<div
class="leftSide"

View File

@ -6,6 +6,8 @@
import type { RenderResult } 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 downloadBinaryInjectable, { type DownloadBinary } from "../../common/fetch/download-binary.injectable";
import downloadJsonInjectable, { type DownloadJson } from "../../common/fetch/download-json.injectable";
import focusWindowInjectable from "../../renderer/navigation/focus-window.injectable";
// TODO: Make components free of side effects by making them deterministic
@ -15,14 +17,20 @@ describe("extensions - navigation using application menu", () => {
let builder: ApplicationBuilder;
let rendered: RenderResult;
let focusWindowMock: jest.Mock;
let downloadJson: jest.MockedFunction<DownloadJson>;
let downloadBinary: jest.MockedFunction<DownloadBinary>;
beforeEach(async () => {
builder = getApplicationBuilder();
builder.beforeWindowStart((windowDi) => {
focusWindowMock = jest.fn();
downloadJson = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
downloadBinary = jest.fn().mockImplementation((url) => { throw new Error(`Unexpected call to downloadJson for url=${url}`); });
windowDi.override(focusWindowInjectable, () => focusWindowMock);
windowDi.override(downloadJsonInjectable, () => downloadJson);
windowDi.override(downloadBinaryInjectable, () => downloadBinary);
});
rendered = await builder.render();

Some files were not shown because too many files have changed in this diff Show More