From 1091eca46435aa47aff1bfee32f52f94cc1110f4 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 5 Jul 2021 16:03:02 +0300 Subject: [PATCH 001/131] set build.appId (#3264) Signed-off-by: Jari Kolehmainen --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index b151730c3e..6aefeb333e 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ ] }, "build": { + "appId": "com.electron.kontena-lens", "generateUpdatesFilesForAllChannels": true, "files": [ "static/build/main.js" From eb37ae636e22fe9af38ffdc020d04c3568f25c3b Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 5 Jul 2021 17:14:35 +0300 Subject: [PATCH 002/131] Revert "set build.appId (#3264)" (#3268) This reverts commit 1091eca46435aa47aff1bfee32f52f94cc1110f4. --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6aefeb333e..b151730c3e 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,6 @@ ] }, "build": { - "appId": "com.electron.kontena-lens", "generateUpdatesFilesForAllChannels": true, "files": [ "static/build/main.js" From 3138307895ee7cd531aae757ee93b9e43be1ed37 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 5 Jul 2021 18:16:20 +0300 Subject: [PATCH 003/131] Release v5.0.1 (#3269) Signed-off-by: Jari Kolehmainen --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b151730c3e..b1d3e8fff0 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.0.0", + "version": "5.0.1", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From e60c6905da589b898e4a2ca10d4a8c930f3d123d Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 5 Jul 2021 23:17:35 +0300 Subject: [PATCH 004/131] Fix problem with in-tree extension load (#3274) * Revert "don't set prerelease version info when non-prerelease (#3261)" This reverts commit 3f01abbddfd3a72d69034943f3bbdc949576ec18. * include fix from #3256 Signed-off-by: Jari Kolehmainen --- build/set_build_version.ts | 14 ++++++++------ src/extensions/extension-discovery.ts | 8 +++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/build/set_build_version.ts b/build/set_build_version.ts index 3f503e93ba..ebf74d6fab 100644 --- a/build/set_build_version.ts +++ b/build/set_build_version.ts @@ -32,6 +32,8 @@ function getBuildChannel(): string { switch (versionInfo.prerelease?.[0]) { case "beta": return "beta"; + case undefined: + return "latest"; default: return "alpha"; } @@ -57,16 +59,16 @@ async function writeOutNewVersions() { } function main() { - if (versionInfo.prerelease && versionInfo.prerelease.length > 1) { - const prereleaseParts: string[] = [getBuildChannel()]; + const prereleaseParts: string[] = [getBuildChannel()]; + if (versionInfo.prerelease && versionInfo.prerelease.length > 1) { prereleaseParts.push(versionInfo.prerelease[1].toString()); - prereleaseParts.push(buildNumber); - appInfo.version = `${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}-${prereleaseParts.join(".")}`; - } else { - appInfo.version = `${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}+${buildNumber}`; } + prereleaseParts.push(buildNumber); + + appInfo.version = `${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}-${prereleaseParts.join(".")}`; + writeOutNewVersions() .catch((error) => { console.error(error); diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index 9f2fe15067..85231cc88e 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -365,7 +365,13 @@ export class ExtensionDiscovery extends Singleton { let isCompatible = isBundled; if (manifest.engines?.lens) { - isCompatible = semver.satisfies(appSemVer, manifest.engines.lens); + const appSemVerLatestImplied = appSemVer; + + if (appSemVerLatestImplied.prerelease?.[0] === "latest") { + /* remove the "latest" prerelease tag so as not to require the extension to specify it */ + appSemVerLatestImplied.prerelease = []; + } + isCompatible = semver.satisfies(appSemVerLatestImplied, manifest.engines.lens); } return { From 8ac320392421dfb3c28fddeef7b66fecd8d5158a Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Tue, 6 Jul 2021 00:12:57 +0300 Subject: [PATCH 005/131] v5.0.2 (#3275) Signed-off-by: Jari Kolehmainen --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b1d3e8fff0..8cd525a365 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.0.1", + "version": "5.0.2", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From b2836eb57a82bcfa9fdca69517e1b3330ad1987c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 6 Jul 2021 04:01:11 -0400 Subject: [PATCH 006/131] Don't clear searchValue on CommandDialog onBlur (#3172) Signed-off-by: Sebastian Malton --- .../command-palette/command-dialog.tsx | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/renderer/components/command-palette/command-dialog.tsx b/src/renderer/components/command-palette/command-dialog.tsx index 7fc5c56dcf..493d9bea70 100644 --- a/src/renderer/components/command-palette/command-dialog.tsx +++ b/src/renderer/components/command-palette/command-dialog.tsx @@ -35,16 +35,17 @@ import { clusterViewURL } from "../../../common/routes"; @observer export class CommandDialog extends React.Component { @observable menuIsOpen = true; + @observable searchValue: any = undefined; constructor(props: {}) { super(props); makeObservable(this); } - + @computed get activeEntity(): CatalogEntity | undefined { return catalogEntityRegistry.activeEntity; } - + @computed get options() { const registry = CommandRegistry.getInstance(); @@ -104,14 +105,24 @@ export class CommandDialog extends React.Component { return ( } + /> + + Node shell image. Used for creating node shell pod. + + +
+ + } + /> + + Name of a pre-existing secret (optional). Used for pulling image from a private registry. + +
+ + ); + } +} diff --git a/src/renderer/components/cluster-settings/components/index.ts b/src/renderer/components/cluster-settings/components/index.ts index e82425c9c7..beb80269b7 100644 --- a/src/renderer/components/cluster-settings/components/index.ts +++ b/src/renderer/components/cluster-settings/components/index.ts @@ -28,3 +28,4 @@ export * from "./cluster-prometheus-setting"; export * from "./cluster-proxy-setting"; export * from "./cluster-show-metrics"; export * from "./cluster-icon-settings"; +export * from "./cluster-node-shell-setting"; diff --git a/src/renderer/initializers/entity-settings-registry.ts b/src/renderer/initializers/entity-settings-registry.ts index 10723c8ebf..31c8c81c21 100644 --- a/src/renderer/initializers/entity-settings-registry.ts +++ b/src/renderer/initializers/entity-settings-registry.ts @@ -70,6 +70,15 @@ export function initEntitySettingsRegistry() { components: { View: clusterSettings.MetricsSettings, } + }, + { + apiVersions: ["entity.k8slens.dev/v1alpha1"], + kind: "KubernetesCluster", + title: "Node Shell", + group: "Settings", + components: { + View: clusterSettings.NodeShellSettings, + } } ]); } From 9b2031159dd529595002ad78953a077f633fc632 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 12 Jul 2021 12:05:05 -0400 Subject: [PATCH 036/131] Ignore extensions' clusters when adding to kubeConfigSync (#3313) --- src/common/__tests__/user-store.test.ts | 39 +++++-- src/common/utils/__tests__/paths.test.ts | 130 ++++++++++++++++++++++ src/common/utils/paths.ts | 32 ++++++ src/migrations/user-store/5.0.3-beta.1.ts | 13 ++- 4 files changed, 201 insertions(+), 13 deletions(-) create mode 100644 src/common/utils/__tests__/paths.test.ts diff --git a/src/common/__tests__/user-store.test.ts b/src/common/__tests__/user-store.test.ts index 6e5a6433ef..c5a15aec28 100644 --- a/src/common/__tests__/user-store.test.ts +++ b/src/common/__tests__/user-store.test.ts @@ -19,10 +19,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Console } from "console"; - -console = new Console(process.stdout, process.stderr); - import mockFs from "mock-fs"; jest.mock("electron", () => { @@ -37,27 +33,27 @@ jest.mock("electron", () => { }); import { UserStore } from "../user-store"; +import { Console } from "console"; import { SemVer } from "semver"; import electron from "electron"; import { stdout, stderr } from "process"; import { beforeEachWrapped } from "../../../integration/helpers/utils"; import { ThemeStore } from "../../renderer/theme.store"; +import type { ClusterStoreModel } from "../cluster-store"; console = new Console(stdout, stderr); describe("user store tests", () => { describe("for an empty config", () => { beforeEachWrapped(() => { - UserStore.resetInstance(); mockFs({ tmp: { "config.json": "{}", "kube_config": "{}" } }); (UserStore.createInstance() as any).refreshNewContexts = jest.fn(() => Promise.resolve()); - - UserStore.getInstance(); }); afterEach(() => { mockFs.restore(); + UserStore.resetInstance(); }); it("allows setting and retrieving lastSeenAppVersion", () => { @@ -99,14 +95,31 @@ describe("user store tests", () => { describe("migrations", () => { beforeEachWrapped(() => { - UserStore.resetInstance(); mockFs({ "tmp": { "config.json": JSON.stringify({ user: { username: "foobar" }, preferences: { colorTheme: "light" }, lastSeenAppVersion: "1.2.3" - }) + }), + "lens-cluster-store.json": JSON.stringify({ + clusters: [ + { + id: "foobar", + kubeConfigPath: "tmp/extension_data/foo/bar", + }, + { + id: "barfoo", + kubeConfigPath: "some/other/path", + }, + ] + } as ClusterStoreModel), + "extension_data": {}, + }, + "some": { + "other": { + "path": "is file", + } } }); @@ -114,6 +127,7 @@ describe("user store tests", () => { }); afterEach(() => { + UserStore.resetInstance(); mockFs.restore(); }); @@ -122,5 +136,12 @@ describe("user store tests", () => { expect(us.lastSeenAppVersion).toBe("0.0.0"); }); + + it.only("skips clusters for adding to kube-sync with files under extension_data/", () => { + const us = UserStore.getInstance(); + + expect(us.syncKubeconfigEntries.has("tmp/extension_data/foo/bar")).toBe(false); + expect(us.syncKubeconfigEntries.has("some/other/path")).toBe(true); + }); }); }); diff --git a/src/common/utils/__tests__/paths.test.ts b/src/common/utils/__tests__/paths.test.ts new file mode 100644 index 0000000000..0eec289fb9 --- /dev/null +++ b/src/common/utils/__tests__/paths.test.ts @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { describeIf } from "../../../../integration/helpers/utils"; +import { isWindows } from "../../vars"; +import { isLogicalChildPath } from "../paths"; + +describe("isLogicalChildPath", () => { + describeIf(isWindows)("windows tests", () => { + it.each([ + { + parentPath: "C:\\Foo", + testPath: "C:\\Foo\\Bar", + expected: true, + }, + { + parentPath: "C:\\Foo", + testPath: "C:\\Bar", + expected: false, + }, + { + parentPath: "C:\\Foo", + testPath: "C:/Bar", + expected: false, + }, + { + parentPath: "C:\\Foo", + testPath: "C:/Foo/Bar", + expected: true, + }, + { + parentPath: "C:\\Foo", + testPath: "D:\\Foo\\Bar", + expected: false, + }, + ])("test %#", (testData) => { + expect(isLogicalChildPath(testData.parentPath, testData.testPath)).toBe(testData.expected); + }); + }); + + describeIf(!isWindows)("posix tests", () => { + it.each([ + { + parentPath: "/foo", + testPath: "/foo", + expected: false, + }, + { + parentPath: "/foo", + testPath: "/bar", + expected: false, + }, + { + parentPath: "/foo", + testPath: "/foobar", + expected: false, + }, + { + parentPath: "/foo", + testPath: "/foo/bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "/foo/../bar", + expected: false, + }, + { + parentPath: "/foo", + testPath: "/foo/./bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "/foo/.bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "/foo/..bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "/foo/...bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "/foo/..\\.bar", + expected: true, + }, + { + parentPath: "/bar/../foo", + testPath: "/foo/bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "/foo/\\bar", + expected: true, + }, + { + parentPath: "/foo", + testPath: "./bar", + expected: false, + }, + ])("test %#", (testData) => { + expect(isLogicalChildPath(testData.parentPath, testData.testPath)).toBe(testData.expected); + }); + }); +}); diff --git a/src/common/utils/paths.ts b/src/common/utils/paths.ts index 391e1392f2..68d8f61ffa 100644 --- a/src/common/utils/paths.ts +++ b/src/common/utils/paths.ts @@ -33,3 +33,35 @@ function resolveTilde(filePath: string) { export function resolvePath(filePath: string): string { return path.resolve(resolveTilde(filePath)); } + +/** + * Checks if `testPath` represents a potential filesystem entry that would be + * logically "within" the `parentPath` directory. + * + * This function will return `true` in the above case, and `false` otherwise. + * It will return `false` if the two paths are the same (after resolving them). + * + * The function makes no FS calls and is platform dependant. Meaning that the + * results are only guaranteed to be correct for the platform you are running + * on. + * @param parentPath The known path of a directory + * @param testPath The path that is to be tested + */ +export function isLogicalChildPath(parentPath: string, testPath: string): boolean { + parentPath = path.resolve(parentPath); + testPath = path.resolve(testPath); + + if (parentPath === testPath) { + return false; + } + + while (testPath.length >= parentPath.length) { + if (testPath === parentPath) { + return true; + } + + testPath = path.dirname(testPath); + } + + return false; +} diff --git a/src/migrations/user-store/5.0.3-beta.1.ts b/src/migrations/user-store/5.0.3-beta.1.ts index 57271e30a4..ef3448116d 100644 --- a/src/migrations/user-store/5.0.3-beta.1.ts +++ b/src/migrations/user-store/5.0.3-beta.1.ts @@ -20,19 +20,21 @@ */ import { app } from "electron"; -import { existsSync, readJsonSync } from "fs-extra"; +import { existsSync, readFileSync } from "fs"; import path from "path"; import os from "os"; import { ClusterStore, ClusterStoreModel } from "../../common/cluster-store"; import type { KubeconfigSyncEntry, UserPreferencesModel } from "../../common/user-store"; import { MigrationDeclaration, migrationLog } from "../helpers"; +import { isLogicalChildPath } from "../../common/utils"; export default { version: "5.0.3-beta.1", run(store) { try { const { syncKubeconfigEntries = [], ...preferences }: UserPreferencesModel = store.get("preferences") ?? {}; - const { clusters = [] }: ClusterStoreModel = readJsonSync(path.resolve(app.getPath("userData"), "lens-cluster-store.json")) ?? {}; + const { clusters = [] }: ClusterStoreModel = JSON.parse(readFileSync(path.resolve(app.getPath("userData"), "lens-cluster-store.json"), "utf-8")) ?? {}; + const extensionDataDir = path.resolve(app.getPath("userData"), "extension_data"); const syncPaths = new Set(syncKubeconfigEntries.map(s => s.filePath)); syncPaths.add(path.join(os.homedir(), ".kube")); @@ -50,6 +52,11 @@ export default { continue; } + if (isLogicalChildPath(extensionDataDir, cluster.kubeConfigPath)) { + migrationLog(`Skipping ${cluster.id} because kubeConfigPath is placed under an extension_data folder`); + continue; + } + if (!existsSync(cluster.kubeConfigPath)) { migrationLog(`Skipping ${cluster.id} because kubeConfigPath no longer exists`); continue; @@ -64,8 +71,6 @@ export default { migrationLog("Final list of synced paths", updatedSyncEntries); store.set("preferences", { ...preferences, syncKubeconfigEntries: updatedSyncEntries }); } catch (error) { - console.log(error); - if (error.code !== "ENOENT") { // ignore files being missing throw error; From d0d6233869ace7732450264a99c8a8d7a6f9f90f Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Mon, 12 Jul 2021 12:34:34 -0400 Subject: [PATCH 037/131] ignore Lens's prerelease tag when checking extension compatibility (#3317) --- .../__tests__/extension-compatibility.test.ts | 138 ++++++++++++++++++ src/extensions/extension-compatibility.ts | 33 +++++ src/extensions/extension-discovery.ts | 15 +- 3 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 src/extensions/__tests__/extension-compatibility.test.ts create mode 100644 src/extensions/extension-compatibility.ts diff --git a/src/extensions/__tests__/extension-compatibility.test.ts b/src/extensions/__tests__/extension-compatibility.test.ts new file mode 100644 index 0000000000..d8e789a9d9 --- /dev/null +++ b/src/extensions/__tests__/extension-compatibility.test.ts @@ -0,0 +1,138 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { isCompatibleExtension } from "../extension-compatibility"; +import { Console } from "console"; +import { stdout, stderr } from "process"; +import type { LensExtensionManifest } from "../lens-extension"; +import { appSemVer } from "../../common/vars"; + +console = new Console(stdout, stderr); + +describe("extension compatibility", () => { + describe("appSemVer with no prerelease tag", () => { + beforeAll(() => { + appSemVer.major = 5; + appSemVer.minor = 0; + appSemVer.patch = 3; + appSemVer.prerelease = []; + }); + + it("has no extension comparator", () => { + const manifest = { name: "extensionName", version: "0.0.1"}; + + expect(isCompatibleExtension(manifest,)).toBe(false); + }); + + it.each([ + { + comparator: "", + expected: false, + }, + { + comparator: "bad comparator", + expected: false, + }, + { + comparator: "^4.0.0", + expected: false, + }, + { + comparator: "^5.0.0", + expected: true, + }, + { + comparator: "^6.0.0", + expected: false, + }, + { + comparator: "^4.0.0-alpha.1", + expected: false, + }, + { + comparator: "^5.0.0-alpha.1", + expected: true, + }, + { + comparator: "^6.0.0-alpha.1", + expected: false, + }, + ])("extension comparator test: %p", ({ comparator, expected }) => { + const manifest: LensExtensionManifest = { name: "extensionName", version: "0.0.1", engines: { lens: comparator}}; + + expect(isCompatibleExtension(manifest,)).toBe(expected); + }); + }); + + describe("appSemVer with prerelease tag", () => { + beforeAll(() => { + appSemVer.major = 5; + appSemVer.minor = 0; + appSemVer.patch = 3; + appSemVer.prerelease = ["beta", 3]; + }); + + it("has no extension comparator", () => { + const manifest = { name: "extensionName", version: "0.0.1"}; + + expect(isCompatibleExtension(manifest,)).toBe(false); + }); + + it.each([ + { + comparator: "", + expected: false, + }, + { + comparator: "bad comparator", + expected: false, + }, + { + comparator: "^4.0.0", + expected: false, + }, + { + comparator: "^5.0.0", + expected: true, + }, + { + comparator: "^6.0.0", + expected: false, + }, + { + comparator: "^4.0.0-alpha.1", + expected: false, + }, + { + comparator: "^5.0.0-alpha.1", + expected: true, + }, + { + comparator: "^6.0.0-alpha.1", + expected: false, + }, + ])("extension comparator test: %p", ({ comparator, expected }) => { + const manifest: LensExtensionManifest = { name: "extensionName", version: "0.0.1", engines: { lens: comparator}}; + + expect(isCompatibleExtension(manifest,)).toBe(expected); + }); + }); +}); diff --git a/src/extensions/extension-compatibility.ts b/src/extensions/extension-compatibility.ts new file mode 100644 index 0000000000..9ca48a3f2d --- /dev/null +++ b/src/extensions/extension-compatibility.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import semver from "semver"; +import { appSemVer } from "../common/vars"; +import type { LensExtensionManifest } from "./lens-extension"; + +export function isCompatibleExtension(manifest: LensExtensionManifest): boolean { + if (manifest.engines?.lens) { + /* include Lens's prerelease tag in the matching so the extension's compatibility is not limited by it */ + return semver.satisfies(appSemVer, manifest.engines.lens, { includePrerelease: true }); + } + + return false; +} diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index 85231cc88e..70f32de110 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -34,9 +34,8 @@ import { extensionInstaller } from "./extension-installer"; import { ExtensionsStore } from "./extensions-store"; import { ExtensionLoader } from "./extension-loader"; import type { LensExtensionId, LensExtensionManifest } from "./lens-extension"; -import semver from "semver"; -import { appSemVer } from "../common/vars"; import { isProduction } from "../common/vars"; +import { isCompatibleExtension } from "./extension-compatibility"; export interface InstalledExtension { id: LensExtensionId; @@ -362,17 +361,7 @@ export class ExtensionDiscovery extends Singleton { const extensionDir = path.dirname(manifestPath); const npmPackage = path.join(extensionDir, `${manifest.name}-${manifest.version}.tgz`); const absolutePath = (isProduction && await fse.pathExists(npmPackage)) ? npmPackage : extensionDir; - let isCompatible = isBundled; - - if (manifest.engines?.lens) { - const appSemVerLatestImplied = appSemVer; - - if (appSemVerLatestImplied.prerelease?.[0] === "latest") { - /* remove the "latest" prerelease tag so as not to require the extension to specify it */ - appSemVerLatestImplied.prerelease = []; - } - isCompatible = semver.satisfies(appSemVerLatestImplied, manifest.engines.lens); - } + const isCompatible = isBundled || isCompatibleExtension(manifest); return { id, From c6618bde88fe2d5d4cbeabc02d7928c563a5f3bb Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 12 Jul 2021 15:58:25 -0400 Subject: [PATCH 038/131] release v5.1.0-beta.1 (#3360) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 03577c3534..80491a71ac 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.0.3-beta.1", + "version": "5.1.0-beta.1", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From 485783167d6e621e3721a9d99ff2c2c3e871e33c Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 12 Jul 2021 16:13:24 -0400 Subject: [PATCH 039/131] Add SystemName validation to AccessibleNamespaces (#2754) - Don't validate after a submit Signed-off-by: Sebastian Malton --- .../cluster-accessible-namespaces.tsx | 2 + .../editable-list/editable-list.scss | 62 +++++++++++++---- .../editable-list/editable-list.tsx | 10 ++- src/renderer/components/input/input.tsx | 68 +++++++++---------- 4 files changed, 92 insertions(+), 50 deletions(-) diff --git a/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx b/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx index 6794646bea..0c087083fe 100644 --- a/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx +++ b/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx @@ -25,6 +25,7 @@ import type { Cluster } from "../../../../main/cluster"; import { SubTitle } from "../../layout/sub-title"; import { EditableList } from "../../editable-list"; import { observable, makeObservable } from "mobx"; +import { systemName } from "../../input/input_validators"; interface Props { cluster: Cluster; @@ -49,6 +50,7 @@ export class ClusterAccessibleNamespaces extends React.Component { this.namespaces.add(newNamespace); this.props.cluster.accessibleNamespaces = Array.from(this.namespaces); }} + validators={systemName} items={Array.from(this.namespaces)} remove={({ oldItem: oldNamesapce }) => { this.namespaces.delete(oldNamesapce); diff --git a/src/renderer/components/editable-list/editable-list.scss b/src/renderer/components/editable-list/editable-list.scss index 0f88d009d9..6c62c0c59e 100644 --- a/src/renderer/components/editable-list/editable-list.scss +++ b/src/renderer/components/editable-list/editable-list.scss @@ -20,8 +20,13 @@ */ .EditableList { + --gradientColor: var(--colorVague); + .el-contents { display: flex; + background-color: var(--colorVague); + color: $textColorSecondary; + border-radius: $radius; flex-direction: column; &:not(:empty) { @@ -35,20 +40,53 @@ } .el-item { - display: grid; - grid-template-columns: 1fr auto; - padding: $padding $padding * 2; - margin-bottom: $padding / 4; - backdrop-filter: brightness(0.75); - border-radius: var(--border-radius); - - :last-child { - margin-bottom: unset; - } - - :first-child { + display: flex; + flex-direction: row; + padding: $padding 0; + margin-bottom: 1px; + + .el-value-container { + position: relative; + max-width: calc(100% - 29px); align-self: center; + + &::before, &::after { + content: ' '; + position: absolute; + z-index: 20; + display: block; + width: 8px; + height: var(--font-size); + } + + &::before { + left: 0px; + top: 0px; + background: linear-gradient(to right, var(--gradientColor) 0px, transparent); + } + + &::after { + right: 0px; + top: 0px; + background: linear-gradient(to left, var(--gradientColor) 0px, transparent); + } } + + .el-value { + white-space: nowrap; + overflow: scroll!important; + text-overflow: unset!important; + padding-left: 8px; + padding-right: 8px; + + &::-webkit-scrollbar { + display: none; + } + } + } + + :last-child { + margin-bottom: unset; } } } diff --git a/src/renderer/components/editable-list/editable-list.tsx b/src/renderer/components/editable-list/editable-list.tsx index 0b427b6f2a..61bf70bb70 100644 --- a/src/renderer/components/editable-list/editable-list.tsx +++ b/src/renderer/components/editable-list/editable-list.tsx @@ -25,7 +25,7 @@ import { observer } from "mobx-react"; import React from "react"; import { Icon } from "../icon"; -import { Input, InputProps } from "../input"; +import { Input, InputProps, InputValidator } from "../input"; import { boundMethod } from "../../utils"; export interface Props { @@ -33,6 +33,7 @@ export interface Props { add: (newItem: string) => void, remove: (info: { oldItem: T, index: number }) => void, placeholder?: string, + validators?: InputValidator | InputValidator[]; // An optional prop used to convert T to a displayable string // defaults to `String` @@ -61,7 +62,7 @@ export class EditableList extends React.Component> { } render() { - const { items, remove, renderItem, placeholder, inputTheme } = this.props; + const { items, remove, renderItem, placeholder, validators, inputTheme } = this.props; return (
@@ -69,6 +70,7 @@ export class EditableList extends React.Component> {
@@ -76,7 +78,9 @@ export class EditableList extends React.Component> { { items.map((item, index) => (
-
{renderItem(item, index)}
+
+
{renderItem(item, index)}
+
remove(({ index, oldItem: item }))} />
diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index fa1692e988..d182a8c202 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -31,6 +31,7 @@ import isString from "lodash/isString"; import isFunction from "lodash/isFunction"; import isBoolean from "lodash/isBoolean"; import uniqueId from "lodash/uniqueId"; +import { debounce } from "lodash"; const { conditionalValidators, ...InputValidators } = Validators; @@ -59,12 +60,12 @@ export type InputProps = Omit = { @@ -81,15 +82,14 @@ export class Input extends React.Component { public validators: InputValidator[] = []; public state: State = { - dirty: !!this.props.dirty, + focused: false, valid: true, + validating: false, + dirty: !!this.props.dirty, errors: [], + submitted: false, }; - isValid() { - return this.state.valid; - } - setValue(value = "") { if (value !== this.getValue()) { const nativeInputValueSetter = Object.getOwnPropertyDescriptor(this.input.constructor.prototype, "value").set; @@ -213,7 +213,6 @@ export class Input extends React.Component { } setDirty(dirty = true) { - if (this.state.dirty === dirty) return; this.setState({ dirty }); } @@ -221,30 +220,25 @@ export class Input extends React.Component { onFocus(evt: React.FocusEvent) { const { onFocus, autoSelectOnFocus } = this.props; - if (onFocus) onFocus(evt); + onFocus?.(evt); if (autoSelectOnFocus) this.select(); this.setState({ focused: true }); } @boundMethod onBlur(evt: React.FocusEvent) { - const { onBlur } = this.props; - - if (onBlur) onBlur(evt); - if (this.state.dirtyOnBlur) this.setState({ dirty: true, dirtyOnBlur: false }); + this.props.onBlur?.(evt); this.setState({ focused: false }); } + setDirtyOnChange = debounce(() => this.setDirty(), 500); + @boundMethod - onChange(evt: React.ChangeEvent) { + onChange(evt: React.ChangeEvent) { this.props.onChange?.(evt.currentTarget.value, evt); this.validate(); this.autoFitHeight(); - - // mark input as dirty for the first time only onBlur() to avoid immediate error-state show when start typing - if (!this.state.dirty) { - this.setState({ dirtyOnBlur: true }); - } + this.setDirtyOnChange(); // re-render component when used as uncontrolled input // when used @defaultValue instead of @value changing real input.value doesn't call render() @@ -255,20 +249,20 @@ export class Input extends React.Component { @boundMethod onKeyDown(evt: React.KeyboardEvent) { - const modified = evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey; - this.props.onKeyDown?.(evt); - switch (evt.key) { - case "Enter": - if (this.props.onSubmit && !modified && !evt.repeat && this.isValid()) { - this.props.onSubmit(this.getValue(), evt); + if (evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey || evt.repeat) { + return; + } - if (this.isUncontrolled) { - this.setValue(); - } - } - break; + if (evt.key === "Enter") { + if (this.state.valid) { + this.props.onSubmit?.(this.getValue(), evt); + this.setDirtyOnChange.cancel(); + this.setState({ submitted: true }); + } else { + this.setDirty(); + } } } @@ -291,7 +285,11 @@ export class Input extends React.Component { const { defaultValue, value, dirty, validators } = this.props; if (prevProps.value !== value || defaultValue !== prevProps.defaultValue) { - this.validate(); + if (!this.state.submitted) { + this.validate(); + } else { + this.setState({ submitted: false }); + } this.autoFitHeight(); } From 5bccc27204c1a548cff51e98c778a33a751f952b Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 13 Jul 2021 01:51:49 -0400 Subject: [PATCH 040/131] Check for unset pod spec (#3361) Signed-off-by: Sebastian Malton --- src/renderer/api/__tests__/pods.api.test.ts | 38 +++++++++++++++++++++ src/renderer/api/endpoints/pods.api.ts | 6 ++-- 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/renderer/api/__tests__/pods.api.test.ts diff --git a/src/renderer/api/__tests__/pods.api.test.ts b/src/renderer/api/__tests__/pods.api.test.ts new file mode 100644 index 0000000000..e7a90f8e38 --- /dev/null +++ b/src/renderer/api/__tests__/pods.api.test.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { Pod } from "../endpoints"; + +describe("Pod tests", () => { + it("getAllContainers() should never throw", () => { + const pod = new Pod({ + apiVersion: "foo", + kind: "Pod", + metadata: { + name: "foobar", + resourceVersion: "foobar", + uid: "foobar", + }, + }); + + expect(pod.getAllContainers()).toStrictEqual([]); + }); +}); diff --git a/src/renderer/api/endpoints/pods.api.ts b/src/renderer/api/endpoints/pods.api.ts index 8ac8024227..5b85a3d42b 100644 --- a/src/renderer/api/endpoints/pods.api.ts +++ b/src/renderer/api/endpoints/pods.api.ts @@ -213,7 +213,7 @@ export class Pod extends WorkloadKubeObject { autoBind(this); } - declare spec: { + declare spec?: { volumes?: { name: string; persistentVolumeClaim: { @@ -291,11 +291,11 @@ export class Pod extends WorkloadKubeObject { }; getInitContainers() { - return this.spec.initContainers || []; + return this.spec?.initContainers || []; } getContainers() { - return this.spec.containers || []; + return this.spec?.containers || []; } getAllContainers() { From c0c1139e3eaf49b88baa90ff86f4229244847677 Mon Sep 17 00:00:00 2001 From: Violetta Shakirova <38247153+vshakirova@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:35:06 +0400 Subject: [PATCH 041/131] Fix incorrect CRD casing (#3365) Signed-off-by: vshakirova --- src/renderer/components/+custom-resources/crd-list.tsx | 2 +- src/renderer/components/layout/sidebar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/+custom-resources/crd-list.tsx b/src/renderer/components/+custom-resources/crd-list.tsx index 822eff575f..e8bfee160d 100644 --- a/src/renderer/components/+custom-resources/crd-list.tsx +++ b/src/renderer/components/+custom-resources/crd-list.tsx @@ -139,7 +139,7 @@ export class CrdList extends React.Component { ]} renderTableContents={(crd: CustomResourceDefinition) => [ - {crd.getResourceTitle()} + {crd.getResourceKind()} , crd.getGroup(), crd.getVersion(), diff --git a/src/renderer/components/layout/sidebar.tsx b/src/renderer/components/layout/sidebar.tsx index 2aa3f37488..9842d9e45e 100644 --- a/src/renderer/components/layout/sidebar.tsx +++ b/src/renderer/components/layout/sidebar.tsx @@ -73,7 +73,7 @@ export class Sidebar extends React.Component { key={crd.getResourceApiBase()} id={`crd-resource:${crd.getResourceApiBase()}`} url={crd.getResourceUrl()} - text={crd.getResourceTitle()} + text={crd.getResourceKind()} /> ))} From dfd98438090ee82103be4266f8e9161f1f81a8b4 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 13 Jul 2021 08:44:27 -0400 Subject: [PATCH 042/131] Provide better typing for dock tabs (#3340) --- .../dock/__test__/dock-tabs.test.tsx | 20 ++--- .../components/dock/create-resource.store.ts | 8 +- .../components/dock/create-resource.tsx | 4 +- src/renderer/components/dock/dock-tab.tsx | 4 +- src/renderer/components/dock/dock-tabs.tsx | 10 +-- src/renderer/components/dock/dock.store.ts | 81 ++++++++++++++++--- src/renderer/components/dock/dock.tsx | 6 +- .../components/dock/edit-resource.store.ts | 8 +- .../components/dock/edit-resource.tsx | 4 +- .../components/dock/install-chart.store.ts | 8 +- .../components/dock/install-chart.tsx | 4 +- src/renderer/components/dock/log-tab.store.ts | 6 +- src/renderer/components/dock/logs.tsx | 4 +- .../components/dock/terminal-window.tsx | 4 +- .../components/dock/terminal.store.ts | 10 +-- .../components/dock/upgrade-chart.store.ts | 10 +-- .../components/dock/upgrade-chart.tsx | 4 +- 17 files changed, 125 insertions(+), 70 deletions(-) diff --git a/src/renderer/components/dock/__test__/dock-tabs.test.tsx b/src/renderer/components/dock/__test__/dock-tabs.test.tsx index ad27853900..e2061c6543 100644 --- a/src/renderer/components/dock/__test__/dock-tabs.test.tsx +++ b/src/renderer/components/dock/__test__/dock-tabs.test.tsx @@ -24,7 +24,7 @@ import { fireEvent, render } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; import { DockTabs } from "../dock-tabs"; -import { dockStore, IDockTab, TabKind } from "../dock.store"; +import { dockStore, DockTab, TabKind } from "../dock.store"; import { noop } from "../../../utils"; jest.mock("electron", () => ({ @@ -33,12 +33,12 @@ jest.mock("electron", () => ({ }, })); -const initialTabs: IDockTab[] = [ - { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" }, - { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource" }, - { id: "edit", kind: TabKind.EDIT_RESOURCE, title: "Edit resource" }, - { id: "install", kind: TabKind.INSTALL_CHART, title: "Install chart" }, - { id: "logs", kind: TabKind.POD_LOGS, title: "Logs" }, +const initialTabs: DockTab[] = [ + { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false, }, + { id: "create", kind: TabKind.CREATE_RESOURCE, title: "Create resource", pinned: false, }, + { id: "edit", kind: TabKind.EDIT_RESOURCE, title: "Edit resource", pinned: false, }, + { id: "install", kind: TabKind.INSTALL_CHART, title: "Install chart", pinned: false, }, + { id: "logs", kind: TabKind.POD_LOGS, title: "Logs", pinned: false, }, ]; const getComponent = () => ( @@ -142,7 +142,7 @@ describe("", () => { it("disables 'Close All' & 'Close Other' items if only 1 tab available", () => { dockStore.tabs = [{ - id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" + id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false, }]; const { container, getByText } = renderTabs(); const tab = container.querySelector(".Tab"); @@ -157,8 +157,8 @@ describe("", () => { it("disables 'Close To The Right' item if last tab clicked", () => { dockStore.tabs = [ - { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" }, - { id: "logs", kind: TabKind.POD_LOGS, title: "Pod Logs" }, + { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false, }, + { id: "logs", kind: TabKind.POD_LOGS, title: "Pod Logs", pinned: false, }, ]; const { container, getByText } = renderTabs(); const tab = container.querySelectorAll(".Tab")[1]; diff --git a/src/renderer/components/dock/create-resource.store.ts b/src/renderer/components/dock/create-resource.store.ts index 2e049deacf..f0b6b41154 100644 --- a/src/renderer/components/dock/create-resource.store.ts +++ b/src/renderer/components/dock/create-resource.store.ts @@ -27,7 +27,7 @@ import filehound from "filehound"; import { watch } from "chokidar"; import { autoBind } from "../../utils"; import { DockTabStore } from "./dock-tab.store"; -import { dockStore, IDockTab, TabKind } from "./dock.store"; +import { dockStore, DockTabCreateSpecific, TabKind } from "./dock.store"; export class CreateResourceStore extends DockTabStore { constructor() { @@ -81,10 +81,10 @@ export class CreateResourceStore extends DockTabStore { export const createResourceStore = new CreateResourceStore(); -export function createResourceTab(tabParams: Partial = {}) { +export function createResourceTab(tabParams: DockTabCreateSpecific = {}) { return dockStore.createTab({ - kind: TabKind.CREATE_RESOURCE, title: "Create resource", - ...tabParams + ...tabParams, + kind: TabKind.CREATE_RESOURCE, }); } diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index 413cbebc6d..f3423a7771 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -30,7 +30,7 @@ import { observable, makeObservable } from "mobx"; import { observer } from "mobx-react"; import { cssNames } from "../../utils"; import { createResourceStore } from "./create-resource.store"; -import type { IDockTab } from "./dock.store"; +import type { DockTab } from "./dock.store"; import { EditorPanel } from "./editor-panel"; import { InfoPanel } from "./info-panel"; import { resourceApplierApi } from "../../api/endpoints/resource-applier.api"; @@ -39,7 +39,7 @@ import { Notifications } from "../notifications"; interface Props { className?: string; - tab: IDockTab; + tab: DockTab; } @observer diff --git a/src/renderer/components/dock/dock-tab.tsx b/src/renderer/components/dock/dock-tab.tsx index dd4ff63824..a42a2dd64b 100644 --- a/src/renderer/components/dock/dock-tab.tsx +++ b/src/renderer/components/dock/dock-tab.tsx @@ -24,13 +24,13 @@ import "./dock-tab.scss"; import React from "react"; import { observer } from "mobx-react"; import { boundMethod, cssNames, prevDefault, isMiddleClick } from "../../utils"; -import { dockStore, IDockTab } from "./dock.store"; +import { dockStore, DockTab as DockTabModel } from "./dock.store"; import { Tab, TabProps } from "../tabs"; import { Icon } from "../icon"; import { Menu, MenuItem } from "../menu"; import { observable, makeObservable } from "mobx"; -export interface DockTabProps extends TabProps { +export interface DockTabProps extends TabProps { moreActions?: React.ReactNode; } diff --git a/src/renderer/components/dock/dock-tabs.tsx b/src/renderer/components/dock/dock-tabs.tsx index 6064259cbc..372ef100d9 100644 --- a/src/renderer/components/dock/dock-tabs.tsx +++ b/src/renderer/components/dock/dock-tabs.tsx @@ -24,19 +24,19 @@ import React, { Fragment } from "react"; import { Icon } from "../icon"; import { Tabs } from "../tabs/tabs"; import { DockTab } from "./dock-tab"; -import type { IDockTab } from "./dock.store"; +import type { DockTab as DockTabModel } from "./dock.store"; import { TabKind } from "./dock.store"; import { TerminalTab } from "./terminal-tab"; interface Props { - tabs: IDockTab[] + tabs: DockTabModel[] autoFocus: boolean - selectedTab: IDockTab - onChangeTab: (tab: IDockTab) => void + selectedTab: DockTabModel + onChangeTab: (tab: DockTabModel) => void } export const DockTabs = ({ tabs, autoFocus, selectedTab, onChangeTab }: Props) => { - const renderTab = (tab?: IDockTab) => { + const renderTab = (tab?: DockTabModel) => { if (!tab) { return null; } diff --git a/src/renderer/components/dock/dock.store.ts b/src/renderer/components/dock/dock.store.ts index 2c9789ce41..516aba403b 100644 --- a/src/renderer/components/dock/dock.store.ts +++ b/src/renderer/components/dock/dock.store.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import MD5 from "crypto-js/md5"; +import * as uuid from "uuid"; import { action, computed, IReactionOptions, makeObservable, observable, reaction } from "mobx"; import { autoBind, createStorage } from "../../utils"; import throttle from "lodash/throttle"; @@ -35,16 +35,55 @@ export enum TabKind { POD_LOGS = "pod-logs", } -export interface IDockTab { +/** + * This is the storage model for dock tabs. + * + * All fields are required. + */ +export type DockTab = Required; + +/** + * These are the arguments for creating a new Tab on the dock + */ +export interface DockTabCreate { + /** + * The ID of the tab for reference purposes. + */ id?: TabId; + + /** + * What kind of dock tab it is + */ kind: TabKind; + + /** + * The tab's title, defaults to `kind` + */ title?: string; - pinned?: boolean; // not closable + + /** + * If true then the dock entry will take up the whole view and will not be + * closable. + */ + pinned?: boolean; + + /** + * Extra fields are supported. + */ + [key: string]: any; } +/** + * This type is for function which specifically create a single type of dock tab. + * + * That way users should get a type error if they try and specify a `kind` + * themselves. + */ +export type DockTabCreateSpecific = Omit; + export interface DockStorageState { height: number; - tabs: IDockTab[]; + tabs: DockTab[]; selectedTabId?: TabId; isOpen?: boolean; } @@ -62,7 +101,7 @@ export class DockStore implements DockStorageState { private storage = createStorage("dock", { height: 300, tabs: [ - { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal" }, + { id: "terminal", kind: TabKind.TERMINAL, title: "Terminal", pinned: false }, ], }); @@ -88,11 +127,11 @@ export class DockStore implements DockStorageState { }); } - get tabs(): IDockTab[] { + get tabs(): DockTab[] { return this.storage.get().tabs; } - set tabs(tabs: IDockTab[]) { + set tabs(tabs: DockTab[]) { this.storage.merge({ tabs }); } @@ -191,15 +230,31 @@ export class DockStore implements DockStorageState { } @action - createTab(anonTab: IDockTab, addNumber = true): IDockTab { - const tabId = MD5(Math.random().toString() + Date.now()).toString(); - const tab: IDockTab = { id: tabId, ...anonTab }; + createTab(rawTabDesc: DockTabCreate, addNumber = true): DockTab { + const { + id = uuid.v4(), + kind, + pinned = false, + ...restOfTabFields + } = rawTabDesc; + let { title = kind } = rawTabDesc; if (addNumber) { - const tabNumber = this.getNewTabNumber(tab.kind); + const tabNumber = this.getNewTabNumber(kind); - if (tabNumber > 1) tab.title += ` (${tabNumber})`; + if (tabNumber > 1) { + title += ` (${tabNumber})`; + } } + + const tab: DockTab = { + ...restOfTabFields, + id, + kind, + pinned, + title + }; + this.tabs.push(tab); this.selectTab(tab.id); this.open(); @@ -234,7 +289,7 @@ export class DockStore implements DockStorageState { } } - closeTabs(tabs: IDockTab[]) { + closeTabs(tabs: DockTab[]) { tabs.forEach(tab => this.closeTab(tab.id)); } diff --git a/src/renderer/components/dock/dock.tsx b/src/renderer/components/dock/dock.tsx index c3c4ca520b..a8cea06d7b 100644 --- a/src/renderer/components/dock/dock.tsx +++ b/src/renderer/components/dock/dock.tsx @@ -32,7 +32,7 @@ import { ResizeDirection, ResizingAnchor } from "../resizing-anchor"; import { CreateResource } from "./create-resource"; import { createResourceTab } from "./create-resource.store"; import { DockTabs } from "./dock-tabs"; -import { dockStore, IDockTab, TabKind } from "./dock.store"; +import { dockStore, DockTab, TabKind } from "./dock.store"; import { EditResource } from "./edit-resource"; import { InstallChart } from "./install-chart"; import { Logs } from "./logs"; @@ -62,14 +62,14 @@ export class Dock extends React.Component { } }; - onChangeTab = (tab: IDockTab) => { + onChangeTab = (tab: DockTab) => { const { open, selectTab } = dockStore; open(); selectTab(tab.id); }; - renderTab(tab: IDockTab) { + renderTab(tab: DockTab) { switch (tab.kind) { case TabKind.CREATE_RESOURCE: return ; diff --git a/src/renderer/components/dock/edit-resource.store.ts b/src/renderer/components/dock/edit-resource.store.ts index 9b10491901..7931d86451 100644 --- a/src/renderer/components/dock/edit-resource.store.ts +++ b/src/renderer/components/dock/edit-resource.store.ts @@ -22,7 +22,7 @@ import { autoBind, noop } from "../../utils"; import { DockTabStore } from "./dock-tab.store"; import { autorun, IReactionDisposer } from "mobx"; -import { dockStore, IDockTab, TabId, TabKind } from "./dock.store"; +import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store"; import type { KubeObject } from "../../api/kube-object"; import { apiManager } from "../../api/api-manager"; import type { KubeObjectStore } from "../../kube-object.store"; @@ -96,7 +96,7 @@ export class EditResourceStore extends DockTabStore { return this.getData(tabId)?.resource; } - getTabByResource(object: KubeObject): IDockTab { + getTabByResource(object: KubeObject): DockTab { const [tabId] = Array.from(this.data).find(([, { resource }]) => { return object.selfLink === resource; }) || []; @@ -115,7 +115,7 @@ export class EditResourceStore extends DockTabStore { export const editResourceStore = new EditResourceStore(); -export function editResourceTab(object: KubeObject, tabParams: Partial = {}) { +export function editResourceTab(object: KubeObject, tabParams: DockTabCreateSpecific = {}) { // use existing tab if already opened let tab = editResourceStore.getTabByResource(object); @@ -128,8 +128,8 @@ export function editResourceTab(object: KubeObject, tabParams: Partial if (!tab) { tab = dockStore.createTab({ title: `${object.kind}: ${object.getName()}`, + ...tabParams, kind: TabKind.EDIT_RESOURCE, - ...tabParams }, false); editResourceStore.setData(tab.id, { resource: object.selfLink, diff --git a/src/renderer/components/dock/edit-resource.tsx b/src/renderer/components/dock/edit-resource.tsx index 455bbda61a..0496ef3104 100644 --- a/src/renderer/components/dock/edit-resource.tsx +++ b/src/renderer/components/dock/edit-resource.tsx @@ -25,7 +25,7 @@ import React from "react"; import { action, computed, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; import jsYaml from "js-yaml"; -import type { IDockTab } from "./dock.store"; +import type { DockTab } from "./dock.store"; import { cssNames } from "../../utils"; import { editResourceStore } from "./edit-resource.store"; import { InfoPanel } from "./info-panel"; @@ -36,7 +36,7 @@ import type { KubeObject } from "../../api/kube-object"; interface Props { className?: string; - tab: IDockTab; + tab: DockTab; } @observer diff --git a/src/renderer/components/dock/install-chart.store.ts b/src/renderer/components/dock/install-chart.store.ts index 20e0a7a0ac..f7ae6930e6 100644 --- a/src/renderer/components/dock/install-chart.store.ts +++ b/src/renderer/components/dock/install-chart.store.ts @@ -20,7 +20,7 @@ */ import { action, autorun, makeObservable } from "mobx"; -import { dockStore, IDockTab, TabId, TabKind } from "./dock.store"; +import { dockStore, DockTabCreateSpecific, TabId, TabKind } from "./dock.store"; import { DockTabStore } from "./dock-tab.store"; import { getChartDetails, getChartValues, HelmChart } from "../../api/endpoints/helm-charts.api"; import type { IReleaseUpdateDetails } from "../../api/endpoints/helm-releases.api"; @@ -98,12 +98,12 @@ export class InstallChartStore extends DockTabStore { export const installChartStore = new InstallChartStore(); -export function createInstallChartTab(chart: HelmChart, tabParams: Partial = {}) { +export function createInstallChartTab(chart: HelmChart, tabParams: DockTabCreateSpecific = {}) { const { name, repo, version } = chart; const tab = dockStore.createTab({ - kind: TabKind.INSTALL_CHART, title: `Helm Install: ${repo}/${name}`, - ...tabParams + ...tabParams, + kind: TabKind.INSTALL_CHART, }, false); installChartStore.setData(tab.id, { diff --git a/src/renderer/components/dock/install-chart.tsx b/src/renderer/components/dock/install-chart.tsx index 69e0313eec..b0113018ea 100644 --- a/src/renderer/components/dock/install-chart.tsx +++ b/src/renderer/components/dock/install-chart.tsx @@ -24,7 +24,7 @@ import "./install-chart.scss"; import React, { Component } from "react"; import { observable, makeObservable } from "mobx"; import { observer } from "mobx-react"; -import { dockStore, IDockTab } from "./dock.store"; +import { dockStore, DockTab } from "./dock.store"; import { InfoPanel } from "./info-panel"; import { Badge } from "../badge"; import { NamespaceSelect } from "../+namespaces/namespace-select"; @@ -42,7 +42,7 @@ import { navigate } from "../../navigation"; import { releaseURL } from "../../../common/routes"; interface Props { - tab: IDockTab; + tab: DockTab; } @observer diff --git a/src/renderer/components/dock/log-tab.store.ts b/src/renderer/components/dock/log-tab.store.ts index 560cea5cff..50be7a1e34 100644 --- a/src/renderer/components/dock/log-tab.store.ts +++ b/src/renderer/components/dock/log-tab.store.ts @@ -26,7 +26,7 @@ import { podsStore } from "../+workloads-pods/pods.store"; import { IPodContainer, Pod } from "../../api/endpoints"; import type { WorkloadKubeObject } from "../../api/workload-kube-object"; import { DockTabStore } from "./dock-tab.store"; -import { dockStore, IDockTab, TabKind } from "./dock.store"; +import { dockStore, DockTabCreateSpecific, TabKind } from "./dock.store"; export interface LogTabData { pods: Pod[]; @@ -90,10 +90,10 @@ export class LogTabStore extends DockTabStore { dockStore.renameTab(tabId, `Pod ${selectedPod.metadata.name}`); } - private createDockTab(tabParams: Partial) { + private createDockTab(tabParams: DockTabCreateSpecific) { dockStore.createTab({ + ...tabParams, kind: TabKind.POD_LOGS, - ...tabParams }, false); } diff --git a/src/renderer/components/dock/logs.tsx b/src/renderer/components/dock/logs.tsx index 4649d28eeb..6c5c8d115f 100644 --- a/src/renderer/components/dock/logs.tsx +++ b/src/renderer/components/dock/logs.tsx @@ -25,7 +25,7 @@ import { disposeOnUnmount, observer } from "mobx-react"; import { searchStore } from "../../../common/search-store"; import { boundMethod } from "../../utils"; -import type { IDockTab } from "./dock.store"; +import type { DockTab } from "./dock.store"; import { InfoPanel } from "./info-panel"; import { LogResourceSelector } from "./log-resource-selector"; import { LogList } from "./log-list"; @@ -36,7 +36,7 @@ import { LogTabData, logTabStore } from "./log-tab.store"; interface Props { className?: string - tab: IDockTab + tab: DockTab } @observer diff --git a/src/renderer/components/dock/terminal-window.tsx b/src/renderer/components/dock/terminal-window.tsx index d5b4d4a53b..652086d811 100644 --- a/src/renderer/components/dock/terminal-window.tsx +++ b/src/renderer/components/dock/terminal-window.tsx @@ -25,14 +25,14 @@ import React from "react"; import { reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { cssNames } from "../../utils"; -import type { IDockTab } from "./dock.store"; +import type { DockTab } from "./dock.store"; import type { Terminal } from "./terminal"; import { terminalStore } from "./terminal.store"; import { ThemeStore } from "../../theme.store"; interface Props { className?: string; - tab: IDockTab; + tab: DockTab; } @observer diff --git a/src/renderer/components/dock/terminal.store.ts b/src/renderer/components/dock/terminal.store.ts index 7f0396336e..47d100b3ab 100644 --- a/src/renderer/components/dock/terminal.store.ts +++ b/src/renderer/components/dock/terminal.store.ts @@ -23,18 +23,18 @@ import { autorun, observable } from "mobx"; import { autoBind } from "../../utils"; import { Terminal } from "./terminal"; import { TerminalApi } from "../../api/terminal-api"; -import { dockStore, IDockTab, TabId, TabKind } from "./dock.store"; +import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store"; import { WebSocketApiState } from "../../api/websocket-api"; -export interface ITerminalTab extends IDockTab { +export interface ITerminalTab extends DockTab { node?: string; // activate node shell mode } -export function createTerminalTab(tabParams: Partial = {}) { +export function createTerminalTab(tabParams: DockTabCreateSpecific = {}) { return dockStore.createTab({ - kind: TabKind.TERMINAL, title: `Terminal`, - ...tabParams + ...tabParams, + kind: TabKind.TERMINAL, }); } diff --git a/src/renderer/components/dock/upgrade-chart.store.ts b/src/renderer/components/dock/upgrade-chart.store.ts index 5aafdfb0ea..4a43ae4ab1 100644 --- a/src/renderer/components/dock/upgrade-chart.store.ts +++ b/src/renderer/components/dock/upgrade-chart.store.ts @@ -20,7 +20,7 @@ */ import { action, autorun, computed, IReactionDisposer, reaction, makeObservable } from "mobx"; -import { dockStore, IDockTab, TabId, TabKind } from "./dock.store"; +import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store"; import { DockTabStore } from "./dock-tab.store"; import { getReleaseValues, HelmRelease } from "../../api/endpoints/helm-releases.api"; import { releaseStore } from "../+apps-releases/release.store"; @@ -120,14 +120,14 @@ export class UpgradeChartStore extends DockTabStore { this.values.setData(tabId, values); } - getTabByRelease(releaseName: string): IDockTab { + getTabByRelease(releaseName: string): DockTab { return dockStore.getTabById(this.releaseNameReverseLookup.get(releaseName)); } } export const upgradeChartStore = new UpgradeChartStore(); -export function createUpgradeChartTab(release: HelmRelease, tabParams: Partial = {}) { +export function createUpgradeChartTab(release: HelmRelease, tabParams: DockTabCreateSpecific = {}) { let tab = upgradeChartStore.getTabByRelease(release.getName()); if (tab) { @@ -137,9 +137,9 @@ export function createUpgradeChartTab(release: HelmRelease, tabParams: Partial Date: Tue, 13 Jul 2021 15:58:55 +0300 Subject: [PATCH 043/131] Removing limits and requests from Pod metrics (#3369) Signed-off-by: Alex Andreev --- .../+workloads-pods/container-charts.tsx | 8 ++- .../components/+workloads-pods/pod-charts.tsx | 51 +++---------------- 2 files changed, 11 insertions(+), 48 deletions(-) diff --git a/src/renderer/components/+workloads-pods/container-charts.tsx b/src/renderer/components/+workloads-pods/container-charts.tsx index df55fc9b59..0c810b47de 100644 --- a/src/renderer/components/+workloads-pods/container-charts.tsx +++ b/src/renderer/components/+workloads-pods/container-charts.tsx @@ -27,6 +27,7 @@ import { isMetricsEmpty, normalizeMetrics } from "../../api/endpoints/metrics.ap import { NoMetrics } from "../resource-metrics/no-metrics"; import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metrics"; import { ThemeStore } from "../../theme.store"; +import { mapValues } from "lodash"; type IContext = IResourceMetricsValue; @@ -37,10 +38,7 @@ export const ContainerCharts = observer(() => { if (!metrics) return null; if (isMetricsEmpty(metrics)) return ; - const values = Object.values(metrics) - .map(normalizeMetrics) - .map(({ data }) => data.result[0].values); - const [ + const { cpuUsage, cpuRequests, cpuLimits, @@ -48,7 +46,7 @@ export const ContainerCharts = observer(() => { memoryRequests, memoryLimits, fsUsage - ] = values; + } = mapValues(metrics, metric => normalizeMetrics(metric).data.result[0].values); const datasets = [ // CPU diff --git a/src/renderer/components/+workloads-pods/pod-charts.tsx b/src/renderer/components/+workloads-pods/pod-charts.tsx index d3aafa0214..cc05aeec55 100644 --- a/src/renderer/components/+workloads-pods/pod-charts.tsx +++ b/src/renderer/components/+workloads-pods/pod-charts.tsx @@ -19,15 +19,16 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import React, { useContext } from "react"; +import { mapValues } from "lodash"; import { observer } from "mobx-react"; -import type { IPodMetrics } from "../../api/endpoints"; -import { BarChart, cpuOptions, memoryOptions } from "../chart"; +import React, { useContext } from "react"; import { isMetricsEmpty, normalizeMetrics } from "../../api/endpoints/metrics.api"; -import { NoMetrics } from "../resource-metrics/no-metrics"; +import { BarChart, cpuOptions, memoryOptions } from "../chart"; import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metrics"; +import { NoMetrics } from "../resource-metrics/no-metrics"; + import type { WorkloadKubeObject } from "../../api/workload-kube-object"; -import { ThemeStore } from "../../theme.store"; +import type { IPodMetrics } from "../../api/endpoints"; export const podMetricTabs = [ "CPU", @@ -40,27 +41,19 @@ type IContext = IResourceMetricsValue { const { params: { metrics }, tabId, object } = useContext(ResourceMetricsContext); - const { chartCapacityColor } = ThemeStore.getInstance().activeTheme.colors; const id = object.getId(); if (!metrics) return null; if (isMetricsEmpty(metrics)) return ; const options = tabId == 0 ? cpuOptions : memoryOptions; - const values = Object.values(metrics) - .map(normalizeMetrics) - .map(({ data }) => data.result[0].values); - const [ + const { cpuUsage, - cpuRequests, - cpuLimits, memoryUsage, - memoryRequests, - memoryLimits, fsUsage, networkReceive, networkTransmit - ] = values; + } = mapValues(metrics, metric => normalizeMetrics(metric).data.result[0].values); const datasets = [ // CPU @@ -71,20 +64,6 @@ export const PodCharts = observer(() => { tooltip: `Container CPU cores usage`, borderColor: "#3D90CE", data: cpuUsage.map(([x, y]) => ({ x, y })) - }, - { - id: `${id}-cpuRequests`, - label: `Requests`, - tooltip: `Container CPU requests`, - borderColor: "#30b24d", - data: cpuRequests.map(([x, y]) => ({ x, y })) - }, - { - id: `${id}-cpuLimits`, - label: `Limits`, - tooltip: `CPU limits`, - borderColor: chartCapacityColor, - data: cpuLimits.map(([x, y]) => ({ x, y })) } ], // Memory @@ -96,20 +75,6 @@ export const PodCharts = observer(() => { borderColor: "#c93dce", data: memoryUsage.map(([x, y]) => ({ x, y })) }, - { - id: `${id}-memoryRequests`, - label: `Requests`, - tooltip: `Container memory requests`, - borderColor: "#30b24d", - data: memoryRequests.map(([x, y]) => ({ x, y })) - }, - { - id: `${id}-memoryLimits`, - label: `Limits`, - tooltip: `Container memory limits`, - borderColor: chartCapacityColor, - data: memoryLimits.map(([x, y]) => ({ x, y })) - } ], // Network [ From ca1ad425ab5b47992e02a695cb83a0dbcf90f12e Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 13 Jul 2021 17:00:11 +0300 Subject: [PATCH 044/131] Badge: option to show short/full versions of contents (#1397) * add button to expand and view whole badge contents Signed-off-by: Sebastian Malton * revert font changes due to update elsewhere already existing Signed-off-by: Sebastian Malton * make expanding icon smaller and not focusable Signed-off-by: Sebastian Malton * extended badge content by click, selecting text from label doesn't trigger auto-closing Signed-off-by: Roman * fix: hover only intercative badges (with extra content) Signed-off-by: Roman * added common document/selectionchange watcher Signed-off-by: Roman * lint fixes Signed-off-by: Roman * Convert Badge to CSS Modules Signed-off-by: Alex Andreev * Align Badge styles across components Signed-off-by: Alex Andreev * Cleaning up Signed-off-by: Alex Andreev * Not closing badge if user started to select text Signed-off-by: Alex Andreev Co-authored-by: Sebastian Malton Co-authored-by: Alex Andreev --- .../+apps-releases/release-details.scss | 8 +-- .../+apps-releases/release-details.tsx | 2 +- .../+catalog/catalog-entity.store.tsx | 1 + .../+config-autoscalers/hpa-details.scss | 2 +- .../+config-autoscalers/hpa-details.tsx | 2 +- .../components/+config-autoscalers/hpa.scss | 5 +- .../components/+config-autoscalers/hpa.tsx | 1 + .../components/+config-secrets/secrets.tsx | 2 +- .../+custom-resources/crd-details.scss | 19 +---- .../+custom-resources/crd-details.tsx | 4 +- .../crd-resource-details.scss | 10 +-- .../crd-resource-details.tsx | 3 +- .../+custom-resources/crd.mixins.scss | 2 +- .../components/+nodes/node-details.scss | 5 +- .../components/+nodes/nodes-mixins.scss | 2 +- .../+workloads-cronjobs/cronjob-details.scss | 4 +- .../deployment-details.scss | 24 +++---- .../deployment-details.tsx | 4 +- .../+workloads-jobs/job-details.scss | 4 +- .../pod-details-container.scss | 4 -- .../+workloads-pods/pod-details.tsx | 2 +- .../components/+workloads-pods/pods.tsx | 4 +- .../+workloads/workloads-mixins.scss | 2 +- .../badge/{badge.scss => badge.module.css} | 50 ++++++++----- src/renderer/components/badge/badge.tsx | 71 ++++++++++++++++--- src/renderer/components/chart/chart.scss | 2 +- src/renderer/components/chart/chart.tsx | 3 +- src/renderer/components/chart/pie-chart.scss | 2 +- .../components/cluster-show-metrics.tsx | 3 +- src/renderer/components/dock/info-panel.scss | 4 -- .../components/drawer/drawer-item.scss | 2 +- .../components/hotbar/hotbar-selector.scss | 2 +- .../components/hotbar/hotbar-selector.tsx | 1 + src/renderer/components/icon/icon.scss | 2 +- .../item-object-list/page-filters-list.tsx | 2 +- .../components/table/table.mixins.scss | 4 -- 36 files changed, 146 insertions(+), 118 deletions(-) rename src/renderer/components/badge/{badge.scss => badge.module.css} (75%) diff --git a/src/renderer/components/+apps-releases/release-details.scss b/src/renderer/components/+apps-releases/release-details.scss index 3afe13961c..a4bd53610c 100644 --- a/src/renderer/components/+apps-releases/release-details.scss +++ b/src/renderer/components/+apps-releases/release-details.scss @@ -27,13 +27,7 @@ } .status { - .Badge { - @include release-status-bgs; - - &:first-child { - margin-left: 0; - } - } + @include release-status-bgs; } .chart { diff --git a/src/renderer/components/+apps-releases/release-details.tsx b/src/renderer/components/+apps-releases/release-details.tsx index e5bda1f873..fae2e3ba71 100644 --- a/src/renderer/components/+apps-releases/release-details.tsx +++ b/src/renderer/components/+apps-releases/release-details.tsx @@ -272,7 +272,7 @@ export class ReleaseDetails extends Component { {this.renderValues()} diff --git a/src/renderer/components/+catalog/catalog-entity.store.tsx b/src/renderer/components/+catalog/catalog-entity.store.tsx index fea9e5e6ba..b606d54c1a 100644 --- a/src/renderer/components/+catalog/catalog-entity.store.tsx +++ b/src/renderer/components/+catalog/catalog-entity.store.tsx @@ -88,6 +88,7 @@ export class CatalogEntityItem implements ItemObject { onClick?.(event); event.stopPropagation(); }} + expandable={false} /> )); } diff --git a/src/renderer/components/+config-autoscalers/hpa-details.scss b/src/renderer/components/+config-autoscalers/hpa-details.scss index ef9506976f..c1c6e930fc 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.scss +++ b/src/renderer/components/+config-autoscalers/hpa-details.scss @@ -22,7 +22,7 @@ @import "autoscaler.mixins"; .HpaDetails { - .Badge { + .status { @include hpa-status-bgc; } diff --git a/src/renderer/components/+config-autoscalers/hpa-details.tsx b/src/renderer/components/+config-autoscalers/hpa-details.tsx index 1a6db54e78..cf56b2c35c 100644 --- a/src/renderer/components/+config-autoscalers/hpa-details.tsx +++ b/src/renderer/components/+config-autoscalers/hpa-details.tsx @@ -125,7 +125,7 @@ export class HpaDetails extends React.Component { {hpa.getReplicas()} - + {hpa.getConditions().map(({ type, tooltip, isReady }) => { if (!isReady) return null; diff --git a/src/renderer/components/+config-autoscalers/hpa.scss b/src/renderer/components/+config-autoscalers/hpa.scss index 41fa3f6d08..dc79016fcf 100644 --- a/src/renderer/components/+config-autoscalers/hpa.scss +++ b/src/renderer/components/+config-autoscalers/hpa.scss @@ -38,10 +38,7 @@ &.status { flex: 1.5; @include table-cell-labels-offsets; - - .Badge { - @include hpa-status-bgc; - } + @include hpa-status-bgc; } &.age { diff --git a/src/renderer/components/+config-autoscalers/hpa.tsx b/src/renderer/components/+config-autoscalers/hpa.tsx index eca60e8c63..539bbb18d7 100644 --- a/src/renderer/components/+config-autoscalers/hpa.tsx +++ b/src/renderer/components/+config-autoscalers/hpa.tsx @@ -104,6 +104,7 @@ export class HorizontalPodAutoscalers extends React.Component { label={type} tooltip={tooltip} className={cssNames(type.toLowerCase())} + expandable={false} /> ); }) diff --git a/src/renderer/components/+config-secrets/secrets.tsx b/src/renderer/components/+config-secrets/secrets.tsx index d74305a499..3bfeada163 100644 --- a/src/renderer/components/+config-secrets/secrets.tsx +++ b/src/renderer/components/+config-secrets/secrets.tsx @@ -79,7 +79,7 @@ export class Secrets extends React.Component { secret.getName(), , secret.getNs(), - secret.getLabels().map(label => ), + secret.getLabels().map(label => ), secret.getKeys().join(", "), secret.type, secret.getAge(), diff --git a/src/renderer/components/+custom-resources/crd-details.scss b/src/renderer/components/+custom-resources/crd-details.scss index e5d016bb30..8e969a580b 100644 --- a/src/renderer/components/+custom-resources/crd-details.scss +++ b/src/renderer/components/+custom-resources/crd-details.scss @@ -23,11 +23,9 @@ .CRDDetails { .conditions { - .Badge { - @include crd-condition-bgc; - } + @include crd-condition-bgc; } - + .Table { margin-left: -$margin * 2; margin-right: -$margin * 2; @@ -41,21 +39,8 @@ flex: 0.5; } - .description { - flex: 1.5; - - .Badge { - background: transparent; - } - } - .json-path { flex: 3; - - .Badge { - white-space: pre-line; - text-overflow: initial; - } } } } diff --git a/src/renderer/components/+custom-resources/crd-details.tsx b/src/renderer/components/+custom-resources/crd-details.tsx index 146c299884..bef1dc6f67 100644 --- a/src/renderer/components/+custom-resources/crd-details.tsx +++ b/src/renderer/components/+custom-resources/crd-details.tsx @@ -25,7 +25,6 @@ import React from "react"; import { Link } from "react-router-dom"; import { observer } from "mobx-react"; import type { CustomResourceDefinition } from "../../api/endpoints/crd.api"; -import { cssNames } from "../../utils"; import { AceEditor } from "../ace-editor"; import { Badge } from "../badge"; import { DrawerItem, DrawerTitle } from "../drawer"; @@ -86,7 +85,8 @@ export class CRDDetails extends React.Component {

{message}

diff --git a/src/renderer/components/+custom-resources/crd-resource-details.scss b/src/renderer/components/+custom-resources/crd-resource-details.scss index af2c36ef2c..b7e57a0295 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.scss +++ b/src/renderer/components/+custom-resources/crd-resource-details.scss @@ -21,13 +21,9 @@ .CrdResourceDetails { .status { - .value { - .Badge { - &.ready { - color: white; - background-color: $colorOk; - } - } + .ready { + color: white; + background-color: $colorOk; } } } \ No newline at end of file diff --git a/src/renderer/components/+custom-resources/crd-resource-details.tsx b/src/renderer/components/+custom-resources/crd-resource-details.tsx index f7305788eb..dc6181c63c 100644 --- a/src/renderer/components/+custom-resources/crd-resource-details.tsx +++ b/src/renderer/components/+custom-resources/crd-resource-details.tsx @@ -90,7 +90,8 @@ export class CrdResourceDetails extends React.Component { .map(({ kind, message, status }, index) => ( )); diff --git a/src/renderer/components/+custom-resources/crd.mixins.scss b/src/renderer/components/+custom-resources/crd.mixins.scss index 62a160b1ff..89c7c86acc 100644 --- a/src/renderer/components/+custom-resources/crd.mixins.scss +++ b/src/renderer/components/+custom-resources/crd.mixins.scss @@ -30,7 +30,7 @@ $crd-condition-colors: ( @mixin crd-condition-bgc { @each $status, $color in $crd-condition-colors { - &.#{$status} { + .#{$status} { background: $color; color: white; } diff --git a/src/renderer/components/+nodes/node-details.scss b/src/renderer/components/+nodes/node-details.scss index 895fdadfee..8d7672fde5 100644 --- a/src/renderer/components/+nodes/node-details.scss +++ b/src/renderer/components/+nodes/node-details.scss @@ -24,9 +24,6 @@ --status-default-bg: #{$colorError}; .conditions { - .Badge { - cursor: default; - @include node-status-bgs; - } + @include node-status-bgs; } } \ No newline at end of file diff --git a/src/renderer/components/+nodes/nodes-mixins.scss b/src/renderer/components/+nodes/nodes-mixins.scss index 5ac0e4400a..74979c7a9f 100644 --- a/src/renderer/components/+nodes/nodes-mixins.scss +++ b/src/renderer/components/+nodes/nodes-mixins.scss @@ -33,7 +33,7 @@ $node-status-color-list: ( @mixin node-status-bgs { @each $status, $color in $node-status-color-list { - &.#{$status} { + .#{$status} { background: $color; color: white; } diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-details.scss b/src/renderer/components/+workloads-cronjobs/cronjob-details.scss index 513afd4273..f5e3c76ec8 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-details.scss +++ b/src/renderer/components/+workloads-cronjobs/cronjob-details.scss @@ -33,8 +33,6 @@ } .conditions { - .Badge { - @include job-condition-bgs; - } + @include job-condition-bgs; } } \ No newline at end of file diff --git a/src/renderer/components/+workloads-deployments/deployment-details.scss b/src/renderer/components/+workloads-deployments/deployment-details.scss index 75ff19bbd4..490edf169f 100644 --- a/src/renderer/components/+workloads-deployments/deployment-details.scss +++ b/src/renderer/components/+workloads-deployments/deployment-details.scss @@ -21,21 +21,19 @@ .DeploymentDetails { .conditions { - .Badge { - &.available { - color: white; - background-color: $deployment-available; - } + .available { + color: white; + background-color: $deployment-available; + } - &.progressing { - color: white; - background-color: $deployment-progressing; - } + .progressing { + color: white; + background-color: $deployment-progressing; + } - &.replica-failure { - color: white; - background-color: $deployment-replicafailure; - } + .replica-failure { + color: white; + background-color: $deployment-replicafailure; } } } \ No newline at end of file diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx index acd514749f..87f4e08378 100644 --- a/src/renderer/components/+workloads-deployments/deployment-details.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-details.tsx @@ -27,7 +27,6 @@ import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; import type { Deployment } from "../../api/endpoints"; -import { cssNames } from "../../utils"; import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; import { podsStore } from "../+workloads-pods/pods.store"; @@ -118,7 +117,8 @@ export class DeploymentDetails extends React.Component {

{message}

diff --git a/src/renderer/components/+workloads-jobs/job-details.scss b/src/renderer/components/+workloads-jobs/job-details.scss index ffd78bc791..8ac3a66a93 100644 --- a/src/renderer/components/+workloads-jobs/job-details.scss +++ b/src/renderer/components/+workloads-jobs/job-details.scss @@ -22,8 +22,6 @@ .JobDetails { .conditions { - .Badge { - @include job-condition-bgs; - } + @include job-condition-bgs; } } \ No newline at end of file diff --git a/src/renderer/components/+workloads-pods/pod-details-container.scss b/src/renderer/components/+workloads-pods/pod-details-container.scss index dc670c4c09..fca42c9433 100644 --- a/src/renderer/components/+workloads-pods/pod-details-container.scss +++ b/src/renderer/components/+workloads-pods/pod-details-container.scss @@ -58,8 +58,4 @@ @include pod-status-colors; } - - .Badge { - white-space: normal; - } } \ No newline at end of file diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx index 2a7516b816..2bb08deeff 100644 --- a/src/renderer/components/+workloads-pods/pod-details.tsx +++ b/src/renderer/components/+workloads-pods/pod-details.tsx @@ -142,7 +142,7 @@ export class PodDetails extends React.Component { ); diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx index 807d531b20..d7ef7175e8 100644 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ b/src/renderer/components/+workloads-pods/pods.tsx @@ -127,7 +127,7 @@ export class Pods extends React.Component { { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} renderTableContents={(pod: Pod) => [ - , + , , pod.getNs(), this.renderContainersStatus(pod), @@ -145,7 +145,7 @@ export class Pods extends React.Component { ); }), pod.getNodeName() ? - + {pod.getNodeName()} diff --git a/src/renderer/components/+workloads/workloads-mixins.scss b/src/renderer/components/+workloads/workloads-mixins.scss index 61a56f03f8..03c05c2d48 100644 --- a/src/renderer/components/+workloads/workloads-mixins.scss +++ b/src/renderer/components/+workloads/workloads-mixins.scss @@ -98,7 +98,7 @@ $cronjob-condition-color-list: ( @mixin job-condition-bgs { @each $condition, $color in $job-condition-color-list { - &.#{$condition} { + .#{$condition} { color: white; background: $color; } diff --git a/src/renderer/components/badge/badge.scss b/src/renderer/components/badge/badge.module.css similarity index 75% rename from src/renderer/components/badge/badge.scss rename to src/renderer/components/badge/badge.module.css index 23f8e99ff0..19dc8c7631 100644 --- a/src/renderer/components/badge/badge.scss +++ b/src/renderer/components/badge/badge.module.css @@ -19,25 +19,41 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.Badge { +.badge { + position: relative; display: inline-block; - white-space: nowrap; max-width: 100%; +} + +.badge + .badge { + margin-left: 8px; +} + +.badge.interactive:hover { + background-color: var(--mainBackground); + cursor: pointer; +} + +.badge:not(.isExpanded) { + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - - &:not(.flat) { - background: $colorVague; - color: $textColorSecondary; - border-radius: $radius; - padding: .2em .4em; - } - - &.small { - font-size: $font-size-small; - } - - &.clickable { - cursor: pointer; - } +} + +.badge:not(.flat) { + background: var(--colorVague); + border-radius: 3px; + padding: .2em .4em; +} + +.small { + font-size: var(--font-size-small); +} + +.clickable { + cursor: pointer; +} + +.disabled { + opacity: 0.5; } diff --git a/src/renderer/components/badge/badge.tsx b/src/renderer/components/badge/badge.tsx index 14959b04d1..a16b5482c0 100644 --- a/src/renderer/components/badge/badge.tsx +++ b/src/renderer/components/badge/badge.tsx @@ -19,29 +19,84 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./badge.scss"; +import styles from "./badge.module.css"; import React from "react"; +import { computed, makeObservable, observable } from "mobx"; +import { observer } from "mobx-react"; import { cssNames } from "../../utils/cssNames"; import { TooltipDecoratorProps, withTooltip } from "../tooltip"; +import { boundMethod } from "../../utils"; export interface BadgeProps extends React.HTMLAttributes, TooltipDecoratorProps { small?: boolean; flat?: boolean; label?: React.ReactNode; + expandable?: boolean; + disabled?: boolean; } +// Common handler for all Badge instances +document.addEventListener("selectionchange", () => { + Badge.badgeMeta.hasTextSelected ||= window.getSelection().toString().trim().length > 0; +}); + @withTooltip +@observer export class Badge extends React.Component { - render() { - const { className, label, small, flat, children, ...elemProps } = this.props; - const clickable = Boolean(this.props.onClick); + static defaultProps: Partial = { + expandable: true + }; - return <> - + static badgeMeta = observable({ + hasTextSelected: false + }); + + @observable.ref elem: HTMLElement; + @observable isExpanded = false; + + constructor(props: BadgeProps) { + super(props); + makeObservable(this); + } + + @computed get isExpandable() { + if (!this.props.expandable) return false; + + return this.elem?.clientWidth < this.elem?.scrollWidth; + } + + @boundMethod + onMouseUp() { + if (!this.isExpandable || Badge.badgeMeta.hasTextSelected) { + Badge.badgeMeta.hasTextSelected = false; + } else { + this.isExpanded = !this.isExpanded; + } + } + + @boundMethod + bindRef(elem: HTMLElement) { + this.elem = elem; + } + + render() { + const { className, label, disabled, small, children, flat, expandable, ...elemProps } = this.props; + const clickable = Boolean(this.props.onClick) || this.isExpandable; + const classNames = cssNames(styles.badge, className, { + [styles.small]: small, + [styles.flat]: flat, + [styles.clickable]: clickable, + [styles.interactive]: this.isExpandable, + [styles.isExpanded]: this.isExpanded, + [styles.disabled]: disabled + }); + + return ( +
{label} {children} - - ; +
+ ); } } diff --git a/src/renderer/components/chart/chart.scss b/src/renderer/components/chart/chart.scss index 3429b251f9..554d62b883 100644 --- a/src/renderer/components/chart/chart.scss +++ b/src/renderer/components/chart/chart.scss @@ -29,7 +29,7 @@ } .legend { - .Badge { + .LegendBadge { background: transparent; transition: background-color 250ms; white-space: normal; diff --git a/src/renderer/components/chart/chart.tsx b/src/renderer/components/chart/chart.tsx index b68ece5523..d456c397d1 100644 --- a/src/renderer/components/chart/chart.tsx +++ b/src/renderer/components/chart/chart.tsx @@ -169,7 +169,7 @@ export class Chart extends React.Component { const labelElem = (title: string, color: string, tooltip?: string) => ( @@ -177,6 +177,7 @@ export class Chart extends React.Component {
)} tooltip={tooltip} + expandable={false} /> ); diff --git a/src/renderer/components/chart/pie-chart.scss b/src/renderer/components/chart/pie-chart.scss index 7034ee28a1..76c098e39d 100644 --- a/src/renderer/components/chart/pie-chart.scss +++ b/src/renderer/components/chart/pie-chart.scss @@ -30,7 +30,7 @@ flex-direction: column; > * { - &.Badge:hover { + &.LegendBadge:hover { background-color: transparent; } diff --git a/src/renderer/components/cluster-settings/components/cluster-show-metrics.tsx b/src/renderer/components/cluster-settings/components/cluster-show-metrics.tsx index 6544f2325b..effdc16160 100644 --- a/src/renderer/components/cluster-settings/components/cluster-show-metrics.tsx +++ b/src/renderer/components/cluster-settings/components/cluster-show-metrics.tsx @@ -69,13 +69,14 @@ export class ShowMetricsSetting extends React.Component { const tooltipId = `${name}`; return ( - + {name} this.removeMetric(name)} tooltip="Remove" + className="mx-3" /> ); diff --git a/src/renderer/components/dock/info-panel.scss b/src/renderer/components/dock/info-panel.scss index 24b727f5d8..0933b236c1 100644 --- a/src/renderer/components/dock/info-panel.scss +++ b/src/renderer/components/dock/info-panel.scss @@ -30,10 +30,6 @@ margin-right: var(--padding); } - .Badge { - background-color: var(--dockBadgeBackground); - } - > .controls { white-space: nowrap; flex: 1 1; diff --git a/src/renderer/components/drawer/drawer-item.scss b/src/renderer/components/drawer/drawer-item.scss index a884be9576..37ea87947b 100644 --- a/src/renderer/components/drawer/drawer-item.scss +++ b/src/renderer/components/drawer/drawer-item.scss @@ -67,7 +67,7 @@ margin: 0; } - .Badge { + > div { float: left; margin: $spacing; diff --git a/src/renderer/components/hotbar/hotbar-selector.scss b/src/renderer/components/hotbar/hotbar-selector.scss index 4db5ae9900..d0fe3fa57c 100644 --- a/src/renderer/components/hotbar/hotbar-selector.scss +++ b/src/renderer/components/hotbar/hotbar-selector.scss @@ -33,7 +33,7 @@ top: -20px; } - .Badge { + .SelectorIndex { cursor: pointer; background: var(--secondaryBackground); width: 100%; diff --git a/src/renderer/components/hotbar/hotbar-selector.tsx b/src/renderer/components/hotbar/hotbar-selector.tsx index f0ca39d834..3bac4475fe 100644 --- a/src/renderer/components/hotbar/hotbar-selector.tsx +++ b/src/renderer/components/hotbar/hotbar-selector.tsx @@ -49,6 +49,7 @@ export function HotbarSelector({ hotbar }: Props) { preferredPositions: [TooltipPosition.TOP, TooltipPosition.TOP_LEFT], children: hotbar.name }} + className="SelectorIndex" /> store.switchToNext()} /> diff --git a/src/renderer/components/icon/icon.scss b/src/renderer/components/icon/icon.scss index b10eb3708b..02501b3716 100644 --- a/src/renderer/components/icon/icon.scss +++ b/src/renderer/components/icon/icon.scss @@ -153,4 +153,4 @@ @extend .active; } } -} \ No newline at end of file +} diff --git a/src/renderer/components/item-object-list/page-filters-list.tsx b/src/renderer/components/item-object-list/page-filters-list.tsx index 1bee3be8ba..6620350b01 100644 --- a/src/renderer/components/item-object-list/page-filters-list.tsx +++ b/src/renderer/components/item-object-list/page-filters-list.tsx @@ -70,7 +70,7 @@ export class PageFiltersList extends React.Component { diff --git a/src/renderer/components/table/table.mixins.scss b/src/renderer/components/table/table.mixins.scss index 3f7a021aff..12e009631d 100644 --- a/src/renderer/components/table/table.mixins.scss +++ b/src/renderer/components/table/table.mixins.scss @@ -49,8 +49,4 @@ @mixin table-cell-labels-offsets { padding-top: $padding / 2; padding-bottom: 0; - - .Badge + .Badge { - margin-left: $padding / 2; - } } \ No newline at end of file From 9e363b8d5f9d971108bce3cd6412042ac670794f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 13 Jul 2021 11:40:52 -0400 Subject: [PATCH 045/131] Remove win-ca/pem before publish & catch inject error (#3362) --- .azure-pipelines.yml | 38 +++++++++++++++++++------------------- Makefile | 2 ++ src/common/system-ca.ts | 6 +++++- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index afec611177..223f85518d 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -24,12 +24,12 @@ jobs: Write-Output ("##vso[task.setvariable variable=CI_BUILD_TAG;]$CI_BUILD_TAG") condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" displayName: Set the tag name as an environment variable - + - task: NodeTool@0 inputs: versionSpec: $(node_version) displayName: Install Node.js - + - task: Cache@2 inputs: key: 'yarn | "$(Agent.OS)"" | yarn.lock' @@ -37,7 +37,7 @@ jobs: yarn | "$(Agent.OS)" path: $(YARN_CACHE_FOLDER) displayName: Cache Yarn packages - + - bash: | set -e git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay @@ -47,13 +47,13 @@ jobs: env: GH_TOKEN: $(LENS_IDE_GH_TOKEN) displayName: Customize config - + - script: make node_modules displayName: Install dependencies - + - script: make build-npm displayName: Generate npm package - + - script: make build condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" env: @@ -75,12 +75,12 @@ jobs: - script: CI_BUILD_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" displayName: Set the tag name as an environment variable - + - task: NodeTool@0 inputs: versionSpec: $(node_version) displayName: Install Node.js - + - task: Cache@2 inputs: key: 'yarn | "$(Agent.OS)" | yarn.lock' @@ -88,7 +88,7 @@ jobs: yarn | "$(Agent.OS)" path: $(YARN_CACHE_FOLDER) displayName: Cache Yarn packages - + - bash: | set -e git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay @@ -98,13 +98,13 @@ jobs: env: GH_TOKEN: $(LENS_IDE_GH_TOKEN) displayName: Customize config - + - script: make node_modules displayName: Install dependencies - + - script: make build-npm displayName: Generate npm package - + - script: make build condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" env: @@ -128,12 +128,12 @@ jobs: - script: CI_BUILD_TAG=`git describe --tags` && echo "##vso[task.setvariable variable=CI_BUILD_TAG]$CI_BUILD_TAG" condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" displayName: Set the tag name as an environment variable - + - task: NodeTool@0 inputs: versionSpec: $(node_version) displayName: Install Node.js - + - task: Cache@2 inputs: key: 'yarn | "$(Agent.OS)" | yarn.lock' @@ -141,7 +141,7 @@ jobs: yarn | "$(Agent.OS)" path: $(YARN_CACHE_FOLDER) displayName: Cache Yarn packages - + - bash: | set -e git clone "https://${GH_TOKEN}@github.com/lensapp/lens-ide.git" .lens-ide-overlay @@ -151,13 +151,13 @@ jobs: env: GH_TOKEN: $(LENS_IDE_GH_TOKEN) displayName: Customize config - + - script: make node_modules displayName: Install dependencies - + - script: make build-npm displayName: Generate npm package - + - bash: | sudo chown root:root / sudo apt-get update && sudo apt-get install -y snapd @@ -168,7 +168,7 @@ jobs: env: SNAP_LOGIN: $(SNAP_LOGIN) displayName: Setup snapcraft - + - script: make build condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))" env: diff --git a/Makefile b/Makefile index 8b2558e7f7..1a8baf0d37 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,8 @@ build: node_modules binaries/client $(MAKE) build-extensions -B yarn run compile ifeq "$(DETECTED_OS)" "Windows" +# https://github.com/ukoloff/win-ca#clear-pem-folder-on-publish + rm -rf node_modules/win-ca/pem yarn run electron-builder --publish onTag --x64 --ia32 else yarn run electron-builder --publish onTag diff --git a/src/common/system-ca.ts b/src/common/system-ca.ts index a12e55cd02..7ed0120569 100644 --- a/src/common/system-ca.ts +++ b/src/common/system-ca.ts @@ -33,5 +33,9 @@ if (isMac) { } if (isWindows) { - winca.inject("+"); // see: https://github.com/ukoloff/win-ca#caveats + try { + winca.inject("+"); // see: https://github.com/ukoloff/win-ca#caveats + } catch (error) { + logger.error(`[CA]: failed to force load: ${error}`); + } } From c335b0f054e641510438557da1924eb77d63abcc Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 13 Jul 2021 12:36:46 -0400 Subject: [PATCH 046/131] Only create TerminalStore after ThemeStore (#3359) - Use Proxy for backwards compatability - Fix a bunch of unit tests that needed all the Singleton's created - Add comments on the order required for the store migrations Signed-off-by: Sebastian Malton --- package.json | 9 +- .../__tests__/page-registry.test.ts | 166 ++-- src/extensions/renderer-api/components.ts | 2 +- src/main/index.ts | 6 +- src/renderer/bootstrap.tsx | 15 +- .../dock/__test__/dock-tabs.test.tsx | 16 +- .../dock/__test__/log-tab.store.test.ts | 16 +- src/renderer/components/dock/dock.store.ts | 11 +- src/renderer/components/dock/log-tab.store.ts | 16 +- .../components/dock/terminal.store.ts | 27 +- src/renderer/components/tabs/tabs.tsx | 37 +- yarn.lock | 879 +++++++++--------- 12 files changed, 634 insertions(+), 566 deletions(-) diff --git a/package.json b/package.json index 80491a71ac..6f7ea53dbf 100644 --- a/package.json +++ b/package.json @@ -63,8 +63,7 @@ }, "moduleNameMapper": { "\\.(css|scss)$": "/__mocks__/styleMock.ts", - "\\.(svg)$": "/__mocks__/imageMock.ts", - "^@lingui/macro$": "/__mocks__/@linguiMacro.ts" + "\\.(svg)$": "/__mocks__/imageMock.ts" }, "modulePathIgnorePatterns": [ "/dist", @@ -218,7 +217,7 @@ "mobx": "^6.3.0", "mobx-observable-history": "^2.0.1", "mobx-react": "^7.1.0", - "mock-fs": "^4.12.0", + "mock-fs": "4.14", "moment": "^2.29.1", "moment-timezone": "^0.5.33", "node-pty": "^0.9.0", @@ -279,7 +278,7 @@ "@types/marked": "^2.0.3", "@types/md5-file": "^4.0.2", "@types/mini-css-extract-plugin": "^0.9.1", - "@types/mock-fs": "^4.10.0", + "@types/mock-fs": "^4.13.1", "@types/module-alias": "^2.0.0", "@types/node": "12.20", "@types/npm": "^2.0.31", @@ -339,7 +338,7 @@ "html-webpack-plugin": "^4.5.2", "identity-obj-proxy": "^3.0.0", "include-media": "^1.4.9", - "jest": "^26.0.1", + "jest": "26.6.3", "jest-canvas-mock": "^2.3.0", "jest-fetch-mock": "^3.0.3", "jest-mock-extended": "^1.0.16", diff --git a/src/extensions/registries/__tests__/page-registry.test.ts b/src/extensions/registries/__tests__/page-registry.test.ts index ed9bf983cd..9a64d95b6b 100644 --- a/src/extensions/registries/__tests__/page-registry.test.ts +++ b/src/extensions/registries/__tests__/page-registry.test.ts @@ -22,14 +22,24 @@ import { ClusterPageRegistry, getExtensionPageUrl, GlobalPageRegistry, PageParams } from "../page-registry"; import { LensExtension } from "../../lens-extension"; import React from "react"; +import fse from "fs-extra"; import { Console } from "console"; import { stdout, stderr } from "process"; +import { ThemeStore } from "../../../renderer/theme.store"; +import { TerminalStore } from "../../renderer-api/components"; +import { UserStore } from "../../../common/user-store"; + +jest.mock("electron", () => ({ + app: { + getPath: () => "tmp", + }, +})); console = new Console(stdout, stderr); let ext: LensExtension = null; -describe("getPageUrl", () => { +describe("page registry tests", () => { beforeEach(async () => { ext = new LensExtension({ manifest: { @@ -43,6 +53,9 @@ describe("getPageUrl", () => { isEnabled: true, isCompatible: true }); + UserStore.createInstance(); + ThemeStore.createInstance(); + TerminalStore.createInstance(); ClusterPageRegistry.createInstance(); GlobalPageRegistry.createInstance().add({ id: "page-with-params", @@ -54,70 +67,6 @@ describe("getPageUrl", () => { test2: "" // no default value, just declaration }, }, ext); - }); - - afterEach(() => { - GlobalPageRegistry.resetInstance(); - ClusterPageRegistry.resetInstance(); - }); - - it("returns a page url for extension", () => { - expect(getExtensionPageUrl({ extensionId: ext.name })).toBe("/extension/foo-bar"); - }); - - it("allows to pass base url as parameter", () => { - expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "/test" })).toBe("/extension/foo-bar/test"); - }); - - it("removes @ and replace `/` to `--`", () => { - expect(getExtensionPageUrl({ extensionId: "@foo/bar" })).toBe("/extension/foo--bar"); - }); - - it("adds / prefix", () => { - expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "test" })).toBe("/extension/foo-bar/test"); - }); - - it("normalize possible multi-slashes in page.id", () => { - expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "//test/" })).toBe("/extension/foo-bar/test"); - }); - - it("gets page url with custom params", () => { - const params: PageParams = { test1: "one", test2: "2" }; - const searchParams = new URLSearchParams(params); - const pageUrl = getExtensionPageUrl({ - extensionId: ext.name, - pageId: "page-with-params", - params, - }); - - expect(pageUrl).toBe(`/extension/foo-bar/page-with-params?${searchParams}`); - }); - - it("gets page url with default custom params", () => { - const defaultPageUrl = getExtensionPageUrl({ - extensionId: ext.name, - pageId: "page-with-params", - }); - - expect(defaultPageUrl).toBe(`/extension/foo-bar/page-with-params?test1=test1-default`); - }); -}); - -describe("globalPageRegistry", () => { - beforeEach(async () => { - ext = new LensExtension({ - manifest: { - name: "@acme/foo-bar", - version: "0.1.1" - }, - id: "/this/is/fake/package.json", - absolutePath: "/absolute/fake/", - manifestPath: "/this/is/fake/package.json", - isBundled: false, - isEnabled: true, - isCompatible: true - }); - ClusterPageRegistry.createInstance(); GlobalPageRegistry.createInstance().add([ { id: "test-page", @@ -142,33 +91,82 @@ describe("globalPageRegistry", () => { afterEach(() => { GlobalPageRegistry.resetInstance(); ClusterPageRegistry.resetInstance(); + TerminalStore.resetInstance(); + ThemeStore.resetInstance(); + UserStore.resetInstance(); + fse.remove("tmp"); }); - describe("getByPageTarget", () => { - it("matching to first registered page without id", () => { - const page = GlobalPageRegistry.getInstance().getByPageTarget({ extensionId: ext.name }); - - expect(page.id).toEqual(undefined); - expect(page.extensionId).toEqual(ext.name); - expect(page.url).toEqual(getExtensionPageUrl({ extensionId: ext.name })); + describe("getPageUrl", () => { + it("returns a page url for extension", () => { + expect(getExtensionPageUrl({ extensionId: ext.name })).toBe("/extension/foo-bar"); }); - it("returns matching page", () => { - const page = GlobalPageRegistry.getInstance().getByPageTarget({ - pageId: "test-page", - extensionId: ext.name - }); - - expect(page.id).toEqual("test-page"); + it("allows to pass base url as parameter", () => { + expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "/test" })).toBe("/extension/foo-bar/test"); }); - it("returns null if target not found", () => { - const page = GlobalPageRegistry.getInstance().getByPageTarget({ - pageId: "wrong-page", - extensionId: ext.name + it("removes @ and replace `/` to `--`", () => { + expect(getExtensionPageUrl({ extensionId: "@foo/bar" })).toBe("/extension/foo--bar"); + }); + + it("adds / prefix", () => { + expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "test" })).toBe("/extension/foo-bar/test"); + }); + + it("normalize possible multi-slashes in page.id", () => { + expect(getExtensionPageUrl({ extensionId: ext.name, pageId: "//test/" })).toBe("/extension/foo-bar/test"); + }); + + it("gets page url with custom params", () => { + const params: PageParams = { test1: "one", test2: "2" }; + const searchParams = new URLSearchParams(params); + const pageUrl = getExtensionPageUrl({ + extensionId: ext.name, + pageId: "page-with-params", + params, }); - expect(page).toBeNull(); + expect(pageUrl).toBe(`/extension/foo-bar/page-with-params?${searchParams}`); + }); + + it("gets page url with default custom params", () => { + const defaultPageUrl = getExtensionPageUrl({ + extensionId: ext.name, + pageId: "page-with-params", + }); + + expect(defaultPageUrl).toBe(`/extension/foo-bar/page-with-params?test1=test1-default`); + }); + }); + + describe("globalPageRegistry", () => { + describe("getByPageTarget", () => { + it("matching to first registered page without id", () => { + const page = GlobalPageRegistry.getInstance().getByPageTarget({ extensionId: ext.name }); + + expect(page.id).toEqual(undefined); + expect(page.extensionId).toEqual(ext.name); + expect(page.url).toEqual(getExtensionPageUrl({ extensionId: ext.name })); + }); + + it("returns matching page", () => { + const page = GlobalPageRegistry.getInstance().getByPageTarget({ + pageId: "test-page", + extensionId: ext.name + }); + + expect(page.id).toEqual("test-page"); + }); + + it("returns null if target not found", () => { + const page = GlobalPageRegistry.getInstance().getByPageTarget({ + pageId: "wrong-page", + extensionId: ext.name + }); + + expect(page).toBeNull(); + }); }); }); }); diff --git a/src/extensions/renderer-api/components.ts b/src/extensions/renderer-api/components.ts index f19ef18f7a..cb4cfd0377 100644 --- a/src/extensions/renderer-api/components.ts +++ b/src/extensions/renderer-api/components.ts @@ -67,5 +67,5 @@ export * from "../../renderer/components/+events/kube-event-details"; // specific exports export * from "../../renderer/components/status-brick"; -export { terminalStore, createTerminalTab } from "../../renderer/components/dock/terminal.store"; +export { terminalStore, createTerminalTab, TerminalStore } from "../../renderer/components/dock/terminal.store"; export { logTabStore } from "../../renderer/components/dock/log-tab.store"; diff --git a/src/main/index.ts b/src/main/index.ts index 91a9b7c68a..684a2e5fb2 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -62,7 +62,7 @@ import { FilesystemProvisionerStore } from "./extension-filesystem"; import { SentryInit } from "../common/sentry"; // This has to be called before start using winton-based logger -// For example, before any logger.log +// For example, before any logger.log SentryInit(); const workingDir = path.join(app.getPath("appData"), appName); @@ -145,8 +145,12 @@ app.on("ready", async () => { UserStore.createInstance().startMainReactions(); + // ClusterStore depends on: UserStore ClusterStore.createInstance().provideInitialFromMain(); + + // HotbarStore depends on: ClusterStore HotbarStore.createInstance(); + ExtensionsStore.createInstance(); FilesystemProvisionerStore.createInstance(); WeblinkStore.createInstance(); diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index cde700e2b5..132abc9cd6 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -48,6 +48,7 @@ import { ExtensionsStore } from "../extensions/extensions-store"; import { FilesystemProvisionerStore } from "../main/extension-filesystem"; import { ThemeStore } from "./theme.store"; import { SentryInit } from "../common/sentry"; +import { TerminalStore } from "./components/dock/terminal.store"; configurePackages(); @@ -89,18 +90,28 @@ export async function bootstrap(App: AppComponent) { SentryInit(); - await ClusterStore.createInstance().loadInitialOnRenderer(); + // ClusterStore depends on: UserStore + const cs = ClusterStore.createInstance(); + + await cs.loadInitialOnRenderer(); + + // HotbarStore depends on: ClusterStore HotbarStore.createInstance(); ExtensionsStore.createInstance(); FilesystemProvisionerStore.createInstance(); + + // ThemeStore depends on: UserStore ThemeStore.createInstance(); + + // TerminalStore depends on: ThemeStore + TerminalStore.createInstance(); WeblinkStore.createInstance(); ExtensionInstallationStateStore.bindIpcListeners(); HelmRepoManager.createInstance(); // initialize the manager // Register additional store listeners - ClusterStore.getInstance().registerIpcListener(); + cs.registerIpcListener(); // init app's dependencies if any if (App.init) { diff --git a/src/renderer/components/dock/__test__/dock-tabs.test.tsx b/src/renderer/components/dock/__test__/dock-tabs.test.tsx index e2061c6543..cdc1994f65 100644 --- a/src/renderer/components/dock/__test__/dock-tabs.test.tsx +++ b/src/renderer/components/dock/__test__/dock-tabs.test.tsx @@ -22,14 +22,18 @@ import React from "react"; import { fireEvent, render } from "@testing-library/react"; import "@testing-library/jest-dom/extend-expect"; +import fse from "fs-extra"; import { DockTabs } from "../dock-tabs"; import { dockStore, DockTab, TabKind } from "../dock.store"; import { noop } from "../../../utils"; +import { ThemeStore } from "../../../theme.store"; +import { TerminalStore } from "../terminal.store"; +import { UserStore } from "../../../../common/user-store"; jest.mock("electron", () => ({ app: { - getPath: () => "/foo", + getPath: () => "tmp", }, })); @@ -55,10 +59,20 @@ const getTabKinds = () => dockStore.tabs.map(tab => tab.kind); describe("", () => { beforeEach(async () => { + UserStore.createInstance(); + ThemeStore.createInstance(); + TerminalStore.createInstance(); await dockStore.whenReady; dockStore.tabs = initialTabs; }); + afterEach(() => { + ThemeStore.resetInstance(); + TerminalStore.resetInstance(); + UserStore.resetInstance(); + fse.remove("tmp"); + }); + it("renders w/o errors", () => { const { container } = renderTabs(); diff --git a/src/renderer/components/dock/__test__/log-tab.store.test.ts b/src/renderer/components/dock/__test__/log-tab.store.test.ts index 3e1542ed6a..da7d0b1052 100644 --- a/src/renderer/components/dock/__test__/log-tab.store.test.ts +++ b/src/renderer/components/dock/__test__/log-tab.store.test.ts @@ -20,14 +20,18 @@ */ import { podsStore } from "../../+workloads-pods/pods.store"; +import { UserStore } from "../../../../common/user-store"; import { Pod } from "../../../api/endpoints"; +import { ThemeStore } from "../../../theme.store"; import { dockStore } from "../dock.store"; import { logTabStore } from "../log-tab.store"; +import { TerminalStore } from "../terminal.store"; import { deploymentPod1, deploymentPod2, deploymentPod3, dockerPod } from "./pod.mock"; +import fse from "fs-extra"; jest.mock("electron", () => ({ app: { - getPath: () => "/foo", + getPath: () => "tmp", }, })); @@ -36,9 +40,19 @@ podsStore.items.push(new Pod(deploymentPod1)); podsStore.items.push(new Pod(deploymentPod2)); describe("log tab store", () => { + beforeEach(() => { + UserStore.createInstance(); + ThemeStore.createInstance(); + TerminalStore.createInstance(); + }); + afterEach(() => { logTabStore.reset(); dockStore.reset(); + UserStore.resetInstance(); + ThemeStore.resetInstance(); + TerminalStore.resetInstance(); + fse.remove("tmp"); }); it("creates log tab without sibling pods", () => { diff --git a/src/renderer/components/dock/dock.store.ts b/src/renderer/components/dock/dock.store.ts index 516aba403b..eb5902d7db 100644 --- a/src/renderer/components/dock/dock.store.ts +++ b/src/renderer/components/dock/dock.store.ts @@ -136,7 +136,12 @@ export class DockStore implements DockStorageState { } get selectedTabId(): TabId | undefined { - return this.storage.get().selectedTabId || this.tabs[0]?.id; + return this.storage.get().selectedTabId + || ( + this.tabs.length > 0 + ? this.tabs[0]?.id + : undefined + ); } set selectedTabId(tabId: TabId) { @@ -277,9 +282,9 @@ export class DockStore implements DockStorageState { if (newTab?.kind === TabKind.TERMINAL) { // close the dock when selected sibling inactive terminal tab - const { terminalStore } = await import("./terminal.store"); + const { TerminalStore } = await import("./terminal.store"); - if (!terminalStore.isConnected(newTab.id)) this.close(); + if (!TerminalStore.getInstance(false)?.isConnected(newTab.id)) this.close(); } this.selectTab(newTab.id); } else { diff --git a/src/renderer/components/dock/log-tab.store.ts b/src/renderer/components/dock/log-tab.store.ts index 50be7a1e34..bd7d61539b 100644 --- a/src/renderer/components/dock/log-tab.store.ts +++ b/src/renderer/components/dock/log-tab.store.ts @@ -108,8 +108,10 @@ export class LogTabStore extends DockTabStore { }); } - private updateTabsData() { - this.data.forEach((tabData, tabId) => { + private async updateTabsData() { + const promises: Promise[] = []; + + for (const [tabId, tabData] of this.data) { const pod = new Pod(tabData.selectedPod); const pods = podsStore.getPodsByOwnerId(pod.getOwnerRefs()[0]?.uid); const isSelectedPodInList = pods.find(item => item.getId() == pod.getId()); @@ -126,14 +128,16 @@ export class LogTabStore extends DockTabStore { this.renameTab(tabId); } else { - this.closeTab(tabId); + promises.push(this.closeTab(tabId)); } - }); + } + + await Promise.all(promises); } - private closeTab(tabId: string) { + private async closeTab(tabId: string) { this.clearData(tabId); - dockStore.closeTab(tabId); + await dockStore.closeTab(tabId); } } diff --git a/src/renderer/components/dock/terminal.store.ts b/src/renderer/components/dock/terminal.store.ts index 47d100b3ab..fb3359368d 100644 --- a/src/renderer/components/dock/terminal.store.ts +++ b/src/renderer/components/dock/terminal.store.ts @@ -20,7 +20,7 @@ */ import { autorun, observable } from "mobx"; -import { autoBind } from "../../utils"; +import { autoBind, Singleton } from "../../utils"; import { Terminal } from "./terminal"; import { TerminalApi } from "../../api/terminal-api"; import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store"; @@ -38,11 +38,12 @@ export function createTerminalTab(tabParams: DockTabCreateSpecific = {}) { }); } -export class TerminalStore { +export class TerminalStore extends Singleton { protected terminals = new Map(); protected connections = observable.map(); constructor() { + super(); autoBind(this); // connect active tab @@ -129,4 +130,24 @@ export class TerminalStore { } } -export const terminalStore = new TerminalStore(); +/** + * @deprecated use `TerminalStore.getInstance()` instead + */ +export const terminalStore = new Proxy({}, { + get(target, p) { + if (p === "$$typeof") { + return "TerminalStore"; + } + + const ts = TerminalStore.getInstance(); + const res = (ts as any)?.[p]; + + if (typeof res === "function") { + return function(...args: any[]) { + return res.apply(ts, args); + }; + } + + return res; + }, +}) as TerminalStore; diff --git a/src/renderer/components/tabs/tabs.tsx b/src/renderer/components/tabs/tabs.tsx index fb3fb5e58c..8a910211fa 100644 --- a/src/renderer/components/tabs/tabs.tsx +++ b/src/renderer/components/tabs/tabs.tsx @@ -84,7 +84,7 @@ export interface TabProps extends DOMAttributes { export class Tab extends React.PureComponent { static contextType = TabsContext; declare context: TabsContextValue; - public elem: HTMLElement; + public ref = React.createRef(); get isActive() { const { active, value } = this.props; @@ -93,11 +93,11 @@ export class Tab extends React.PureComponent { } focus() { - this.elem.focus(); + this.ref.current?.focus(); } scrollIntoView() { - this.elem.scrollIntoView({ + this.ref.current?.scrollIntoView?.({ behavior: "smooth", inline: "center", }); @@ -106,30 +106,28 @@ export class Tab extends React.PureComponent { @boundMethod onClick(evt: React.MouseEvent) { const { value, active, disabled, onClick } = this.props; - const { onChange } = this.context; - if (disabled || active) return; - if (onClick) onClick(evt); - if (onChange) onChange(value); + if (disabled || active) { + return; + } + + onClick?.(evt); + this.context.onChange?.(value); } @boundMethod onFocus(evt: React.FocusEvent) { - const { onFocus } = this.props; - - if (onFocus) onFocus(evt); + this.props.onFocus?.(evt); this.scrollIntoView(); } @boundMethod onKeyDown(evt: React.KeyboardEvent) { - const ENTER_KEY = evt.keyCode === 13; - const SPACE_KEY = evt.keyCode === 32; + if (evt.key === " " || evt.key === "Enter") { + this.ref.current?.click(); + } - if (SPACE_KEY || ENTER_KEY) this.elem.click(); - const { onKeyDown } = this.props; - - if (onKeyDown) onKeyDown(evt); + this.props?.onKeyDown(evt); } componentDidMount() { @@ -138,11 +136,6 @@ export class Tab extends React.PureComponent { } } - @boundMethod - protected bindRef(elem: HTMLElement) { - this.elem = elem; - } - render() { const { active, disabled, icon, label, value, ...elemProps } = this.props; let { className } = this.props; @@ -160,7 +153,7 @@ export class Tab extends React.PureComponent { onClick={this.onClick} onFocus={this.onFocus} onKeyDown={this.onKeyDown} - ref={this.bindRef} + ref={this.ref} > {typeof icon === "string" ? : icon}
diff --git a/yarn.lock b/yarn.lock index d34cce3a5f..cff6305987 100644 --- a/yarn.lock +++ b/yarn.lock @@ -115,6 +115,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz#ec5a5cf0eec925b66c60580328b122c01230a127" integrity sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA== +"@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + "@babel/helper-replace-supers@^7.10.1": version "7.10.1" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz#ec6859d20c5d8087f6a2dc4e014db7228975f13d" @@ -203,6 +208,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.1" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -252,6 +264,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + "@babel/runtime-corejs3@^7.10.2": version "7.12.1" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.12.1.tgz#51b9092befbeeed938335a109dbe0df51451e9dc" @@ -587,156 +606,160 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.0.1.tgz#62b3b2fa8990f3cbffbef695c42ae9ddbc8f4b39" - integrity sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw== +"@jest/console@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" + "@types/node" "*" chalk "^4.0.0" - jest-message-util "^26.0.1" - jest-util "^26.0.1" + jest-message-util "^26.6.2" + jest-util "^26.6.2" slash "^3.0.0" -"@jest/core@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.0.1.tgz#aa538d52497dfab56735efb00e506be83d841fae" - integrity sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ== +"@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== dependencies: - "@jest/console" "^26.0.1" - "@jest/reporters" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^26.0.1" - jest-config "^26.0.1" - jest-haste-map "^26.0.1" - jest-message-util "^26.0.1" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" jest-regex-util "^26.0.0" - jest-resolve "^26.0.1" - jest-resolve-dependencies "^26.0.1" - jest-runner "^26.0.1" - jest-runtime "^26.0.1" - jest-snapshot "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" - jest-watcher "^26.0.1" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" micromatch "^4.0.2" p-each-series "^2.1.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.0.1.tgz#82f519bba71959be9b483675ee89de8c8f72a5c8" - integrity sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g== +"@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== dependencies: - "@jest/fake-timers" "^26.0.1" - "@jest/types" "^26.0.1" - jest-mock "^26.0.1" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" -"@jest/fake-timers@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.0.1.tgz#f7aeff13b9f387e9d0cac9a8de3bba538d19d796" - integrity sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg== +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" "@sinonjs/fake-timers" "^6.0.1" - jest-message-util "^26.0.1" - jest-mock "^26.0.1" - jest-util "^26.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" -"@jest/globals@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.0.1.tgz#3f67b508a7ce62b6e6efc536f3d18ec9deb19a9c" - integrity sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA== +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== dependencies: - "@jest/environment" "^26.0.1" - "@jest/types" "^26.0.1" - expect "^26.0.1" + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" -"@jest/reporters@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.0.1.tgz#14ae00e7a93e498cec35b0c00ab21c375d9b078f" - integrity sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g== +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" graceful-fs "^4.2.4" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.0" + istanbul-lib-instrument "^4.0.3" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^26.0.1" - jest-resolve "^26.0.1" - jest-util "^26.0.1" - jest-worker "^26.0.0" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^4.1.3" + v8-to-istanbul "^7.0.0" optionalDependencies: - node-notifier "^7.0.0" + node-notifier "^8.0.0" -"@jest/source-map@^26.0.0": - version "26.0.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.0.0.tgz#fd7706484a7d3faf7792ae29783933bbf48a4749" - integrity sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ== +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== dependencies: callsites "^3.0.0" graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.0.1.tgz#1ffdc1ba4bc289919e54b9414b74c9c2f7b2b718" - integrity sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg== +"@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== dependencies: - "@jest/console" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz#b0563424728f3fe9e75d1442b9ae4c11da73f090" - integrity sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg== +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== dependencies: - "@jest/test-result" "^26.0.1" + "@jest/test-result" "^26.6.2" graceful-fs "^4.2.4" - jest-haste-map "^26.0.1" - jest-runner "^26.0.1" - jest-runtime "^26.0.1" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" -"@jest/transform@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.0.1.tgz#0e3ecbb34a11cd4b2080ed0a9c4856cf0ceb0639" - integrity sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA== +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" babel-plugin-istanbul "^6.0.0" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.2.4" - jest-haste-map "^26.0.1" + jest-haste-map "^26.6.2" jest-regex-util "^26.0.0" - jest-util "^26.0.1" + jest-util "^26.6.2" micromatch "^4.0.2" pirates "^4.0.1" slash "^3.0.0" @@ -753,16 +776,6 @@ "@types/yargs" "^15.0.0" chalk "^3.0.0" -"@jest/types@^26.0.1": - version "26.0.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.0.1.tgz#b78333fbd113fa7aec8d39de24f88de8686dac67" - integrity sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^26.3.0": version "26.3.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" @@ -1217,6 +1230,17 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A== +"@types/babel__core@^7.0.0": + version "7.1.15" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.15.tgz#2ccfb1ad55a02c83f8e0ad327cbc332f55eb1024" + integrity sha512-bxlMKPDbY8x5h6HBwVzEOk2C8fb6SLfYQ5Jw3uBYuYF1lfWk/kbLd81la82vrIkBb0l+JdmrZaDikPrNxpS/Ew== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__core@^7.1.7": version "7.1.8" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.8.tgz#057f725aca3641f49fc11c7a87a9de5ec588a5d7" @@ -1250,6 +1274,13 @@ dependencies: "@babel/types" "^7.3.0" +"@types/babel__traverse@^7.0.4": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== + dependencies: + "@babel/types" "^7.3.0" + "@types/body-parser@*": version "1.19.0" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" @@ -1665,10 +1696,10 @@ dependencies: "@types/node" "*" -"@types/mock-fs@^4.10.0": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@types/mock-fs/-/mock-fs-4.10.0.tgz#460061b186993d76856f669d5317cda8a007c24b" - integrity sha512-FQ5alSzmHMmliqcL36JqIA4Yyn9jyJKvRSGV3mvPh108VFatX7naJDzSG4fnFQNZFq9dIx0Dzoe6ddflMB2Xkg== +"@types/mock-fs@^4.13.1": + version "4.13.1" + resolved "https://registry.yarnpkg.com/@types/mock-fs/-/mock-fs-4.13.1.tgz#9201554ceb23671badbfa8ac3f1fa9e0706305be" + integrity sha512-m6nFAJ3lBSnqbvDZioawRvpLXSaPyn52Srf7OfzjubYbYX8MTUdIgDxQl0wEapm4m/pNYSd9TXocpQ0TvZFlYA== dependencies: "@types/node" "*" @@ -1956,10 +1987,10 @@ dependencies: "@types/node" "*" -"@types/stack-utils@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" - integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/stream-buffers@^3.0.3": version "3.0.3" @@ -2975,16 +3006,16 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2" integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA== -babel-jest@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.0.1.tgz#450139ce4b6c17174b136425bda91885c397bc46" - integrity sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw== +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== dependencies: - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" "@types/babel__core" "^7.1.7" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^26.0.0" + babel-preset-jest "^26.6.2" chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -3016,13 +3047,14 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz#fd1d35f95cf8849fc65cb01b5e58aedd710b34a8" - integrity sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w== +babel-plugin-jest-hoist@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" babel-plugin-macros@^2.0.0: @@ -3039,14 +3071,15 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-preset-current-node-syntax@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" - integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -3054,14 +3087,15 @@ babel-preset-current-node-syntax@^0.1.2: "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz#1eac82f513ad36c4db2e9263d7c485c825b1faa6" - integrity sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw== +babel-preset-jest@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== dependencies: - babel-plugin-jest-hoist "^26.0.0" - babel-preset-current-node-syntax "^0.1.2" + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" babel-runtime@^6.26.0: version "6.26.0" @@ -3879,6 +3913,11 @@ circular-dependency-plugin@^5.2.2: resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600" integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ== +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -5030,16 +5069,16 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff-sequences@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" - integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== - diff-sequences@^26.5.0: version "26.5.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.5.0.tgz#ef766cf09d43ed40406611f11c6d8d9dd8b2fefd" integrity sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q== +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -5419,6 +5458,11 @@ elliptic@^6.0.0, elliptic@^6.5.2: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -6046,16 +6090,16 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.0.1.tgz#18697b9611a7e2725e20ba3ceadda49bc9865421" - integrity sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg== +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" ansi-styles "^4.0.0" - jest-get-type "^26.0.0" - jest-matcher-utils "^26.0.1" - jest-message-util "^26.0.1" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" jest-regex-util "^26.0.0" express@^4.17.1: @@ -8233,7 +8277,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is-wsl@^2.1.1: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -8296,7 +8340,7 @@ istanbul-lib-coverage@^3.0.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== -istanbul-lib-instrument@^4.0.0: +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== @@ -8359,57 +8403,57 @@ jest-canvas-mock@^2.3.0: cssfontparser "^1.2.1" moo-color "^1.0.2" -jest-changed-files@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.0.1.tgz#1334630c6a1ad75784120f39c3aa9278e59f349f" - integrity sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw== +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" execa "^4.0.0" throat "^5.0.0" -jest-cli@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.0.1.tgz#3a42399a4cbc96a519b99ad069a117d955570cac" - integrity sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w== +jest-cli@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== dependencies: - "@jest/core" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" prompts "^2.0.1" - yargs "^15.3.1" + yargs "^15.4.1" -jest-config@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.0.1.tgz#096a3d4150afadf719d1fab00e9a6fb2d6d67507" - integrity sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg== +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^26.0.1" - "@jest/types" "^26.0.1" - babel-jest "^26.0.1" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - jest-environment-jsdom "^26.0.1" - jest-environment-node "^26.0.1" - jest-get-type "^26.0.0" - jest-jasmine2 "^26.0.1" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" jest-regex-util "^26.0.0" - jest-resolve "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" micromatch "^4.0.2" - pretty-format "^26.0.1" + pretty-format "^26.6.2" jest-diff@^25.2.1: version "25.5.0" @@ -8431,15 +8475,15 @@ jest-diff@^26.0.0: jest-get-type "^26.3.0" pretty-format "^26.6.1" -jest-diff@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.0.1.tgz#c44ab3cdd5977d466de69c46929e0e57f89aa1de" - integrity sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ== +jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== dependencies: chalk "^4.0.0" - diff-sequences "^26.0.0" - jest-get-type "^26.0.0" - pretty-format "^26.0.1" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" jest-docblock@^26.0.0: version "26.0.0" @@ -8448,39 +8492,41 @@ jest-docblock@^26.0.0: dependencies: detect-newline "^3.0.0" -jest-each@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.0.1.tgz#633083061619302fc90dd8f58350f9d77d67be04" - integrity sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q== +jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" chalk "^4.0.0" - jest-get-type "^26.0.0" - jest-util "^26.0.1" - pretty-format "^26.0.1" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" -jest-environment-jsdom@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz#217690852e5bdd7c846a4e3b50c8ffd441dfd249" - integrity sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g== +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== dependencies: - "@jest/environment" "^26.0.1" - "@jest/fake-timers" "^26.0.1" - "@jest/types" "^26.0.1" - jest-mock "^26.0.1" - jest-util "^26.0.1" - jsdom "^16.2.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" -jest-environment-node@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.0.1.tgz#584a9ff623124ff6eeb49e0131b5f7612b310b13" - integrity sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ== +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== dependencies: - "@jest/environment" "^26.0.1" - "@jest/fake-timers" "^26.0.1" - "@jest/types" "^26.0.1" - jest-mock "^26.0.1" - jest-util "^26.0.1" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" jest-fetch-mock@^3.0.3: version "3.0.3" @@ -8495,88 +8541,86 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-get-type@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.0.0.tgz#381e986a718998dbfafcd5ec05934be538db4039" - integrity sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg== - jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.0.1.tgz#40dcc03c43ac94d25b8618075804d09cd5d49de7" - integrity sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA== +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" "@types/graceful-fs" "^4.1.2" + "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.4" - jest-serializer "^26.0.0" - jest-util "^26.0.1" - jest-worker "^26.0.0" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" - which "^2.0.2" optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz#947c40ee816636ba23112af3206d6fa7b23c1c1c" - integrity sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg== +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^26.0.1" - "@jest/source-map" "^26.0.0" - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^26.0.1" + expect "^26.6.2" is-generator-fn "^2.0.0" - jest-each "^26.0.1" - jest-matcher-utils "^26.0.1" - jest-message-util "^26.0.1" - jest-runtime "^26.0.1" - jest-snapshot "^26.0.1" - jest-util "^26.0.1" - pretty-format "^26.0.1" + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" throat "^5.0.0" -jest-leak-detector@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz#79b19ab3f41170e0a78eb8fa754a116d3447fb8c" - integrity sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA== +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== dependencies: - jest-get-type "^26.0.0" - pretty-format "^26.0.1" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" -jest-matcher-utils@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz#12e1fc386fe4f14678f4cc8dbd5ba75a58092911" - integrity sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw== +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== dependencies: chalk "^4.0.0" - jest-diff "^26.0.1" - jest-get-type "^26.0.0" - pretty-format "^26.0.1" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" -jest-message-util@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.0.1.tgz#07af1b42fc450b4cc8e90e4c9cef11b33ce9b0ac" - integrity sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q== +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/types" "^26.0.1" - "@types/stack-utils" "^1.0.1" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.4" micromatch "^4.0.2" + pretty-format "^26.6.2" slash "^3.0.0" stack-utils "^2.0.2" @@ -8587,129 +8631,134 @@ jest-mock-extended@^1.0.16: dependencies: ts-essentials "^4.0.0" -jest-mock@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.0.1.tgz#7fd1517ed4955397cf1620a771dc2d61fad8fd40" - integrity sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q== +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" + "@types/node" "*" -jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz#607ba7ccc32151d185a477cff45bf33bce417f0b" - integrity sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw== +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" jest-regex-util "^26.0.0" - jest-snapshot "^26.0.1" + jest-snapshot "^26.6.2" -jest-resolve@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.0.1.tgz#21d1ee06f9ea270a343a8893051aeed940cde736" - integrity sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ== +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" chalk "^4.0.0" graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.1" - jest-util "^26.0.1" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" read-pkg-up "^7.0.1" - resolve "^1.17.0" + resolve "^1.18.1" slash "^3.0.0" -jest-runner@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.0.1.tgz#ea03584b7ae4bacfb7e533d680a575a49ae35d50" - integrity sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA== +jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== dependencies: - "@jest/console" "^26.0.1" - "@jest/environment" "^26.0.1" - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" chalk "^4.0.0" + emittery "^0.7.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-config "^26.0.1" + jest-config "^26.6.3" jest-docblock "^26.0.0" - jest-haste-map "^26.0.1" - jest-jasmine2 "^26.0.1" - jest-leak-detector "^26.0.1" - jest-message-util "^26.0.1" - jest-resolve "^26.0.1" - jest-runtime "^26.0.1" - jest-util "^26.0.1" - jest-worker "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" source-map-support "^0.5.6" throat "^5.0.0" -jest-runtime@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.0.1.tgz#a121a6321235987d294168e282d52b364d7d3f89" - integrity sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw== +jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== dependencies: - "@jest/console" "^26.0.1" - "@jest/environment" "^26.0.1" - "@jest/fake-timers" "^26.0.1" - "@jest/globals" "^26.0.1" - "@jest/source-map" "^26.0.0" - "@jest/test-result" "^26.0.1" - "@jest/transform" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" "@types/yargs" "^15.0.0" chalk "^4.0.0" + cjs-module-lexer "^0.6.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-config "^26.0.1" - jest-haste-map "^26.0.1" - jest-message-util "^26.0.1" - jest-mock "^26.0.1" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" jest-regex-util "^26.0.0" - jest-resolve "^26.0.1" - jest-snapshot "^26.0.1" - jest-util "^26.0.1" - jest-validate "^26.0.1" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^15.3.1" + yargs "^15.4.1" -jest-serializer@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.0.0.tgz#f6c521ddb976943b93e662c0d4d79245abec72a3" - integrity sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ== +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== dependencies: + "@types/node" "*" graceful-fs "^4.2.4" -jest-snapshot@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.0.1.tgz#1baa942bd83d47b837a84af7fcf5fd4a236da399" - integrity sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA== +jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.0.0" chalk "^4.0.0" - expect "^26.0.1" + expect "^26.6.2" graceful-fs "^4.2.4" - jest-diff "^26.0.1" - jest-get-type "^26.0.0" - jest-matcher-utils "^26.0.1" - jest-message-util "^26.0.1" - jest-resolve "^26.0.1" - make-dir "^3.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" natural-compare "^1.4.0" - pretty-format "^26.0.1" + pretty-format "^26.6.2" semver "^7.3.2" jest-util@26.x: @@ -8724,57 +8773,60 @@ jest-util@26.x: is-ci "^2.0.0" micromatch "^4.0.2" -jest-util@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.0.1.tgz#72c4c51177b695fdd795ca072a6f94e3d7cef00a" - integrity sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g== +jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" + "@types/node" "*" chalk "^4.0.0" graceful-fs "^4.2.4" is-ci "^2.0.0" - make-dir "^3.0.0" + micromatch "^4.0.2" -jest-validate@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.0.1.tgz#a62987e1da5b7f724130f904725e22f4e5b2e23c" - integrity sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA== +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== dependencies: - "@jest/types" "^26.0.1" + "@jest/types" "^26.6.2" camelcase "^6.0.0" chalk "^4.0.0" - jest-get-type "^26.0.0" + jest-get-type "^26.3.0" leven "^3.1.0" - pretty-format "^26.0.1" + pretty-format "^26.6.2" -jest-watcher@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.0.1.tgz#5b5e3ebbdf10c240e22a98af66d645631afda770" - integrity sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw== +jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== dependencies: - "@jest/test-result" "^26.0.1" - "@jest/types" "^26.0.1" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^26.0.1" + jest-util "^26.6.2" string-length "^4.0.1" -jest-worker@^26.0.0: - version "26.0.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.0.0.tgz#4920c7714f0a96c6412464718d0c58a3df3fb066" - integrity sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw== +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== dependencies: + "@types/node" "*" merge-stream "^2.0.0" supports-color "^7.0.0" -jest@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-26.0.1.tgz#5c51a2e58dff7525b65f169721767173bf832694" - integrity sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg== +jest@26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" + integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== dependencies: - "@jest/core" "^26.0.1" + "@jest/core" "^26.6.3" import-local "^3.0.2" - jest-cli "^26.0.1" + jest-cli "^26.6.3" joi@^17.3.0: version "17.3.0" @@ -8852,38 +8904,6 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^16.2.2: - version "16.2.2" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.2.2.tgz#76f2f7541646beb46a938f5dc476b88705bedf2b" - integrity sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg== - dependencies: - abab "^2.0.3" - acorn "^7.1.1" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.2.0" - data-urls "^2.0.0" - decimal.js "^10.2.0" - domexception "^2.0.1" - escodegen "^1.14.1" - html-encoding-sniffer "^2.0.1" - is-potential-custom-element-name "^1.0.0" - nwsapi "^2.2.0" - parse5 "5.1.1" - request "^2.88.2" - request-promise-native "^1.0.8" - saxes "^5.0.0" - symbol-tree "^3.2.4" - tough-cookie "^3.0.1" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.0.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - ws "^7.2.3" - xml-name-validator "^3.0.0" - jsdom@^16.4.0: version "16.4.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" @@ -10151,10 +10171,10 @@ mobx@^6.3.0: resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.0.tgz#a8fb693c3047bdfcb1eaff9aa48e36a7eb084f96" integrity sha512-Aa1+VXsg4WxqJMTQfWoYuJi5UD10VZhiobSmcs5kcmI3BIT0aVtn7DysvCeDADCzl7dnbX+0BTHUj/v7gLlZpQ== -mock-fs@^4.12.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.12.0.tgz#a5d50b12d2d75e5bec9dac3b67ffe3c41d31ade4" - integrity sha512-/P/HtrlvBxY4o/PzXY9cCNBrdylDNxg7gnrv2sMNxj+UJ2m8jSpl0/A6fuJeNAWr99ZvGWH8XCbE0vmnM5KupQ== +mock-fs@4.14: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== modern-normalize@^1.0.0: version "1.1.0" @@ -10437,16 +10457,16 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-7.0.1.tgz#a355e33e6bebacef9bf8562689aed0f4230ca6f9" - integrity sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg== +node-notifier@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== dependencies: growly "^1.3.0" - is-wsl "^2.1.1" - semver "^7.2.1" + is-wsl "^2.2.0" + semver "^7.3.2" shellwords "^0.1.1" - uuid "^7.0.3" + uuid "^8.3.0" which "^2.0.2" node-pty@^0.9.0: @@ -11869,16 +11889,6 @@ pretty-format@^26.0.0, pretty-format@^26.6.1: ansi-styles "^4.0.0" react-is "^17.0.1" -pretty-format@^26.0.1: - version "26.0.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.0.1.tgz#a4fe54fe428ad2fd3413ca6bbd1ec8c2e277e197" - integrity sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw== - dependencies: - "@jest/types" "^26.0.1" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" @@ -12812,7 +12822,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.3.2: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.18.1, resolve@^1.20.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -15081,12 +15091,7 @@ uuid@^3.3.2, uuid@^3.3.3, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - -uuid@^8.3.2: +uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== @@ -15101,10 +15106,10 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== -v8-to-istanbul@^4.1.3: - version "4.1.4" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" - integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== +v8-to-istanbul@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -15257,7 +15262,7 @@ webidl-conversions@^5.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== -webidl-conversions@^6.0.0, webidl-conversions@^6.1.0: +webidl-conversions@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== @@ -15726,7 +15731,7 @@ yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@18.x, yargs-parser@^18.1.1: +yargs-parser@18.x, yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== @@ -15812,10 +15817,10 @@ yargs@^14.2.3: y18n "^4.0.0" yargs-parser "^15.0.1" -yargs@^15.3.1: - version "15.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" - integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" decamelize "^1.2.0" @@ -15827,7 +15832,7 @@ yargs@^15.3.1: string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^18.1.1" + yargs-parser "^18.1.2" yargs@^16.2.0: version "16.2.0" From 2059034eabf3cad1b0e0b35c1421fa6d298fbcd5 Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Wed, 14 Jul 2021 01:47:06 -0400 Subject: [PATCH 047/131] remove unused filenamify package (#3371) Signed-off-by: Jim Ehrismann --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6f7ea53dbf..0ea6d57a40 100644 --- a/package.json +++ b/package.json @@ -200,7 +200,6 @@ "electron-updater": "^4.3.1", "electron-window-state": "^5.0.3", "filehound": "^1.17.4", - "filenamify": "^4.1.0", "fs-extra": "^9.0.1", "grapheme-splitter": "^1.0.4", "handlebars": "^4.7.7", From f2cd0aa8e85beb9ab5754eb800bb0728f378be8b Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Jul 2021 01:48:32 -0400 Subject: [PATCH 048/131] Catch empty string error is parseKubeApi (#3358) * Catch empty string error is parseKubeApi - Add more debugging lines Signed-off-by: Sebastian Malton * Add some unit tests Signed-off-by: Sebastian Malton * Use resolved pathname Signed-off-by: Sebastian Malton --- .../api/__tests__/kube-api-parse.test.ts | 12 ++++++- src/renderer/api/api-manager.ts | 10 ++++-- src/renderer/api/kube-api-parse.ts | 31 ++++++++++++++++--- .../kube-object/kube-object-details.tsx | 12 ++++--- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/renderer/api/__tests__/kube-api-parse.test.ts b/src/renderer/api/__tests__/kube-api-parse.test.ts index beb8df8221..18b805212d 100644 --- a/src/renderer/api/__tests__/kube-api-parse.test.ts +++ b/src/renderer/api/__tests__/kube-api-parse.test.ts @@ -129,8 +129,18 @@ const tests: KubeApiParseTestData[] = [ }], ]; +const throwtests = [ + undefined, + "", + "ajklsmh" +]; + describe("parseApi unit tests", () => { - it.each(tests)("testing %s", (url, expected) => { + it.each(tests)("testing %j", (url, expected) => { expect(parseKubeApi(url)).toStrictEqual(expected); }); + + it.each(throwtests)("testing %j should throw", (url) => { + expect(() => parseKubeApi(url)).toThrowError("invalid apiPath"); + }); }); diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index eafcf0d8ee..9477446386 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -58,8 +58,14 @@ export class ApiManager { } } - protected resolveApi(api: string | KubeApi): KubeApi { - if (typeof api === "string") return this.getApi(api); + protected resolveApi(api?: string | KubeApi): KubeApi | undefined { + if (!api) { + return undefined; + } + + if (typeof api === "string") { + return this.getApi(api); + } return api; } diff --git a/src/renderer/api/kube-api-parse.ts b/src/renderer/api/kube-api-parse.ts index b02067fb63..10e280d6c8 100644 --- a/src/renderer/api/kube-api-parse.ts +++ b/src/renderer/api/kube-api-parse.ts @@ -24,6 +24,9 @@ import type { KubeObject } from "./kube-object"; import { splitArray } from "../../common/utils"; import { apiManager } from "./api-manager"; +import { isDebugging } from "../../common/vars"; +import logger from "../../main/logger"; +import { inspect } from "util"; export interface IKubeObjectRef { kind: string; @@ -47,8 +50,26 @@ export interface IKubeApiParsed extends IKubeApiLinkRef { } export function parseKubeApi(path: string): IKubeApiParsed { - path = new URL(path, location.origin).pathname; - const [, prefix, ...parts] = path.split("/"); + if (!isDebugging) { + return _parseKubeApi(path); + } + + try { + const res = _parseKubeApi(path); + + logger.debug(`parseKubeApi(${inspect(path, false, null, false)}) -> ${inspect(res, false, null, false)}`); + + return res; + } catch (error) { + logger.debug(`parseKubeApi(${inspect(path, false, null, false)}) threw: ${error}`); + + throw error; + } +} + +function _parseKubeApi(path: string): IKubeApiParsed { + const apiPath = new URL(path, location.origin).pathname; + const [, prefix, ...parts] = apiPath.split("/"); const apiPrefix = `/${prefix}`; const [left, right, namespaced] = splitArray(parts, "namespaces"); let apiGroup, apiVersion, namespace, resource, name; @@ -70,12 +91,14 @@ export function parseKubeApi(path: string): IKubeApiParsed { apiGroup = left.join("/"); } else { switch (left.length) { + case 0: + throw new Error(`invalid apiPath: ${apiPath}`); case 4: [apiGroup, apiVersion, resource, name] = left; break; case 2: resource = left.pop(); - // fallthrough + // fallthrough case 1: apiVersion = left.pop(); apiGroup = ""; @@ -113,7 +136,7 @@ export function parseKubeApi(path: string): IKubeApiParsed { const apiBase = [apiPrefix, apiGroup, apiVersion, resource].filter(v => v).join("/"); if (!apiBase) { - throw new Error(`invalid apiPath: ${path}`); + throw new Error(`invalid apiPath: ${apiPath}`); } return { diff --git a/src/renderer/components/kube-object/kube-object-details.tsx b/src/renderer/components/kube-object/kube-object-details.tsx index 8098f77064..1f0c494b4e 100644 --- a/src/renderer/components/kube-object/kube-object-details.tsx +++ b/src/renderer/components/kube-object/kube-object-details.tsx @@ -34,6 +34,7 @@ import { CrdResourceDetails } from "../+custom-resources"; import { KubeObjectMenu } from "./kube-object-menu"; import type { CustomResourceDefinition } from "../../api/endpoints"; import { KubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; +import logger from "../../../main/logger"; /** * Used to store `object.selfLink` to show more info about resource in the details panel. @@ -67,6 +68,7 @@ export function hideDetails() { } export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlobals = true) { + logger.debug("getDetailsUrl", { selfLink, resetSelected, mergeGlobals }); const params = new URLSearchParams(mergeGlobals ? navigation.searchParams : ""); params.set(kubeDetailsUrlParam.name, selfLink); @@ -100,10 +102,12 @@ export class KubeObjectDetails extends React.Component { } @computed get object() { - const store = apiManager.getStore(this.path); - - if (store) { - return store.getByPath(this.path); + try { + return apiManager + .getStore(this.path) + ?.getByPath(this.path); + } catch (error) { + logger.error(`[KUBE-OBJECT-DETAILS]: failed to get store or object: ${error}`, { path: this.path }); } } From 9c2942c333b1899b24bf0d7c852e4127814eaf47 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Jul 2021 05:31:58 -0400 Subject: [PATCH 049/131] Fix `.TableHead.sticky` in for details panels (#3331) * Fix .TableHead.sticky in for details panels Signed-off-by: Sebastian Malton * Fine-tune TableHead styling in PodDetailsList Signed-off-by: Alex Andreev Co-authored-by: Alex Andreev --- src/renderer/components/+nodes/node-details-resources.tsx | 2 +- .../components/+workloads-pods/pod-details-list.scss | 4 ++++ src/renderer/components/table/table-head.scss | 5 +++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/renderer/components/+nodes/node-details-resources.tsx b/src/renderer/components/+nodes/node-details-resources.tsx index df613cf436..636a3f55f0 100644 --- a/src/renderer/components/+nodes/node-details-resources.tsx +++ b/src/renderer/components/+nodes/node-details-resources.tsx @@ -54,7 +54,7 @@ export class NodeDetailsResources extends React.Component { selectable scrollable={false} > - + CPU Memory Ephemeral Storage diff --git a/src/renderer/components/+workloads-pods/pod-details-list.scss b/src/renderer/components/+workloads-pods/pod-details-list.scss index 05dd787a2c..04ccd2d05e 100644 --- a/src/renderer/components/+workloads-pods/pod-details-list.scss +++ b/src/renderer/components/+workloads-pods/pod-details-list.scss @@ -28,6 +28,10 @@ &.virtual { height: 500px; } + + .TableHead.sticky { + top: calc(var(--spacing) * -1); + } } .TableCell { diff --git a/src/renderer/components/table/table-head.scss b/src/renderer/components/table/table-head.scss index 4e194b2708..efa0bdb9b9 100644 --- a/src/renderer/components/table/table-head.scss +++ b/src/renderer/components/table/table-head.scss @@ -38,7 +38,7 @@ position: -webkit-sticky; // safari position: sticky; z-index: 1; - top: 0; // turn on sticky behaviour + top: 0; } &.nowrap { @@ -48,6 +48,7 @@ .TableCell { display: flex; align-items: center; + word-break: normal; &.checkbox { > .Checkbox { @@ -55,4 +56,4 @@ } } } -} \ No newline at end of file +} From 0a8cf96415e2edcca97db617d0e5df6202ae44d1 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Jul 2021 08:30:57 -0400 Subject: [PATCH 050/131] Add kube-version and distro on detail panel (#3335) --- .../catalog-entities/kubernetes-cluster.ts | 7 ++- src/extensions/lens-renderer-extension.ts | 3 +- .../catalog-entity-detail-registry.ts | 15 ++++-- src/main/cluster-manager.ts | 22 ++++---- src/main/cluster.ts | 19 ++++--- src/renderer/bootstrap.tsx | 1 + .../catalog-entity-detail-registry.tsx | 50 +++++++++++++++++++ src/renderer/initializers/index.ts | 5 +- 8 files changed, 95 insertions(+), 27 deletions(-) create mode 100644 src/renderer/initializers/catalog-entity-detail-registry.tsx diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index db948df25a..83a94547bb 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -55,13 +55,18 @@ export interface KubernetesClusterSpec extends CatalogEntitySpec { }; } +export interface KubernetesClusterMetadata extends CatalogEntityMetadata { + distro?: string; + kubeVersion?: string; +} + export type KubernetesClusterStatusPhase = "connected" | "connecting" | "disconnected" | "deleting"; export interface KubernetesClusterStatus extends CatalogEntityStatus { phase: KubernetesClusterStatusPhase; } -export class KubernetesCluster extends CatalogEntity { +export class KubernetesCluster extends CatalogEntity { public static readonly apiVersion = "entity.k8slens.dev/v1alpha1"; public static readonly kind = "KubernetesCluster"; diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index a3026d99a5..dd5670fd67 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -23,6 +23,7 @@ import type * as registries from "./registries"; import type { Cluster } from "../main/cluster"; import { LensExtension } from "./lens-extension"; import { getExtensionPageUrl } from "./registries/page-registry"; +import type { CatalogEntity } from "../common/catalog"; export class LensRendererExtension extends LensExtension { globalPages: registries.PageRegistration[] = []; @@ -37,7 +38,7 @@ export class LensRendererExtension extends LensExtension { kubeWorkloadsOverviewItems: registries.WorkloadsOverviewDetailRegistration[] = []; commands: registries.CommandRegistration[] = []; welcomeMenus: registries.WelcomeMenuRegistration[] = []; - catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = []; + catalogEntityDetailItems: registries.CatalogEntityDetailRegistration[] = []; topBarItems: registries.TopBarRegistration[] = []; async navigate

(pageId?: string, params?: P) { diff --git a/src/extensions/registries/catalog-entity-detail-registry.ts b/src/extensions/registries/catalog-entity-detail-registry.ts index b4b9fb0dd2..4ee9c4d667 100644 --- a/src/extensions/registries/catalog-entity-detail-registry.ts +++ b/src/extensions/registries/catalog-entity-detail-registry.ts @@ -20,20 +20,25 @@ */ import type React from "react"; +import type { CatalogEntity } from "../common-api/catalog"; import { BaseRegistry } from "./base-registry"; -export interface CatalogEntityDetailComponents { - Details: React.ComponentType; +export interface CatalogEntityDetailsProps { + entity: T; } -export interface CatalogEntityDetailRegistration { +export interface CatalogEntityDetailComponents { + Details: React.ComponentType>; +} + +export interface CatalogEntityDetailRegistration { kind: string; apiVersions: string[]; - components: CatalogEntityDetailComponents; + components: CatalogEntityDetailComponents; priority?: number; } -export class CatalogEntityDetailRegistry extends BaseRegistry { +export class CatalogEntityDetailRegistry extends BaseRegistry> { getItemsForKind(kind: string, apiVersion: string) { const items = this.getItems().filter((item) => { return item.kind === kind && item.apiVersions.includes(apiVersion); diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 42d2dd1d19..73d2c50f46 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -23,7 +23,7 @@ import "../common/cluster-ipc"; import type http from "http"; import { action, autorun, makeObservable, observable, observe, reaction, toJS } from "mobx"; import { ClusterId, ClusterStore, getClusterIdFromHost } from "../common/cluster-store"; -import type { Cluster } from "./cluster"; +import { Cluster } from "./cluster"; import logger from "./logger"; import { apiKubePrefix } from "../common/vars"; import { Singleton } from "../common/utils"; @@ -103,13 +103,13 @@ export class ClusterManager extends Singleton { this.updateEntityStatus(entity, cluster); - entity.metadata.labels = Object.assign({}, cluster.labels, entity.metadata.labels); - entity.metadata.labels.distro = cluster.distribution; - - if (cluster.preferences?.clusterName) { - entity.metadata.name = cluster.preferences.clusterName; - } - + entity.metadata.labels = { + ...entity.metadata.labels, + ...cluster.labels, + }; + entity.metadata.distro = cluster.distribution; + entity.metadata.kubeVersion = cluster.version; + entity.metadata.name = cluster.name; entity.spec.metrics ||= { source: "local" }; if (entity.spec.metrics.source === "local") { @@ -236,8 +236,10 @@ export function catalogEntityFromCluster(cluster: Cluster) { name: cluster.name, source: "local", labels: { - distro: cluster.distribution, - } + ...cluster.labels, + }, + distro: cluster.distribution, + kubeVersion: cluster.version, }, spec: { kubeconfigPath: cluster.kubeConfigPath, diff --git a/src/main/cluster.ts b/src/main/cluster.ts index bf893f8484..58dbbf944f 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -235,8 +235,18 @@ export class Cluster implements ClusterModel, ClusterState { return this.preferences.clusterName || this.contextName; } + /** + * The detected kubernetes distribution + */ @computed get distribution(): string { - return this.metadata.distribution?.toString() || "unknown"; + return this.metadata[ClusterMetadataKey.DISTRIBUTION]?.toString() || "unknown"; + } + + /** + * The detected kubernetes version + */ + @computed get version(): string { + return this.metadata[ClusterMetadataKey.VERSION]?.toString() || "unknown"; } /** @@ -251,13 +261,6 @@ export class Cluster implements ClusterModel, ClusterState { return toJS({ prometheus, prometheusProvider }); } - /** - * Kubernetes version - */ - get version(): string { - return String(this.metadata?.version ?? ""); - } - constructor(model: ClusterModel) { makeObservable(this); this.id = model.id; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 132abc9cd6..2a36c15089 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -80,6 +80,7 @@ export async function bootstrap(App: AppComponent) { initializers.intiKubeObjectDetailRegistry(); initializers.initWelcomeMenuRegistry(); initializers.initWorkloadsOverviewDetailRegistry(); + initializers.initCatalogEntityDetailRegistry(); initializers.initCatalog(); initializers.initIpcRendererListeners(); diff --git a/src/renderer/initializers/catalog-entity-detail-registry.tsx b/src/renderer/initializers/catalog-entity-detail-registry.tsx new file mode 100644 index 0000000000..8cae26afbf --- /dev/null +++ b/src/renderer/initializers/catalog-entity-detail-registry.tsx @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import { KubernetesCluster } from "../../common/catalog-entities"; +import { CatalogEntityDetailRegistry, CatalogEntityDetailsProps } from "../../extensions/registries"; +import { DrawerItem, DrawerTitle } from "../components/drawer"; + +export function initCatalogEntityDetailRegistry() { + CatalogEntityDetailRegistry.getInstance() + .add([ + { + apiVersions: [KubernetesCluster.apiVersion], + kind: KubernetesCluster.kind, + components: { + Details: ({ entity }: CatalogEntityDetailsProps) => ( + <> + +

+ + {entity.metadata.distro || "unknown"} + + + {entity.metadata.kubeVersion || "unknown"} + +
+ + ), + }, + } + ]); +} diff --git a/src/renderer/initializers/index.ts b/src/renderer/initializers/index.ts index 03f32f1a9d..943fef6a30 100644 --- a/src/renderer/initializers/index.ts +++ b/src/renderer/initializers/index.ts @@ -19,12 +19,13 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +export * from "./catalog-entity-detail-registry"; +export * from "./catalog"; export * from "./command-registry"; export * from "./entity-settings-registry"; +export * from "./ipc"; export * from "./kube-object-detail-registry"; export * from "./kube-object-menu-registry"; export * from "./registries"; export * from "./welcome-menu-registry"; export * from "./workloads-overview-detail-registry"; -export * from "./catalog"; -export * from "./ipc"; From c029039035b0800774141082ac7217eae151c190 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Jul 2021 08:33:41 -0400 Subject: [PATCH 051/131] Remove conflicting classNames under quota-entries (#3334) - Both flex and inline modify the display rule and inline was winning Signed-off-by: Sebastian Malton --- .../components/+config-resource-quotas/add-quota-dialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx b/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx index 1227e97049..17dc3e5eae 100644 --- a/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx +++ b/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx @@ -218,7 +218,7 @@ export class AddQuotaDialog extends React.Component {
{this.quotaEntries.map(([quota, value]) => { return ( -
+
{quota}
{value}
this.quotas[quota] = ""} /> From 2d831af78c67d2a9f80653d9f8427ca068b0bd61 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Jul 2021 09:11:17 -0400 Subject: [PATCH 052/131] Update yarn.lock (#3380) Signed-off-by: Sebastian Malton --- yarn.lock | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/yarn.lock b/yarn.lock index cff6305987..c3aa2d82ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6361,20 +6361,6 @@ filelist@^1.0.1: dependencies: minimatch "^3.0.4" -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= - -filenamify@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-4.1.0.tgz#54d110810ae74eebfe115c1b995bd07e03cf2184" - integrity sha512-KQV/uJDI9VQgN7sHH1Zbk6+42cD6mnQ2HONzkXUfPJ+K2FC8GZ1dpewbbHw0Sz8Tf5k3EVdHVayM4DoAwWlmtg== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.1" - trim-repeated "^1.0.0" - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -14030,13 +14016,6 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-outer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - style-loader@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" @@ -14511,13 +14490,6 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= - dependencies: - escape-string-regexp "^1.0.2" - triple-beam@^1.2.0, triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" From 89a3575f9dab5c8275140527e58bc282b1006113 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 14 Jul 2021 10:27:58 -0400 Subject: [PATCH 053/131] Bump raw-loader from 4.0.1 to 4.0.2 (#3373) Bumps [raw-loader](https://github.com/webpack-contrib/raw-loader) from 4.0.1 to 4.0.2. - [Release notes](https://github.com/webpack-contrib/raw-loader/releases) - [Changelog](https://github.com/webpack-contrib/raw-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/raw-loader/compare/v4.0.1...v4.0.2) --- updated-dependencies: - dependency-name: raw-loader dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 0ea6d57a40..89054fd33d 100644 --- a/package.json +++ b/package.json @@ -353,7 +353,7 @@ "postinstall-postinstall": "^2.1.0", "progress-bar-webpack-plugin": "^2.1.0", "randomcolor": "^0.6.2", - "raw-loader": "^4.0.1", + "raw-loader": "^4.0.2", "react-beautiful-dnd": "^13.1.0", "react-refresh": "^0.9.0", "react-router-dom": "^5.2.0", diff --git a/yarn.lock b/yarn.lock index c3aa2d82ed..9d099b7397 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12185,13 +12185,13 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" -raw-loader@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.1.tgz#14e1f726a359b68437e183d5a5b7d33a3eba6933" - integrity sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A== +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== dependencies: loader-utils "^2.0.0" - schema-utils "^2.6.5" + schema-utils "^3.0.0" rc@^1.0.1, rc@^1.1.6, rc@^1.2.7, rc@^1.2.8: version "1.2.8" From 074b1562dd3d8580737d1fdfc727048a003e845f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Jul 2021 10:48:59 -0400 Subject: [PATCH 054/131] release v5.1.0-beta.2 (#3381) Signed-off-by: Sebastian Malton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 89054fd33d..306bac96f8 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.1.0-beta.1", + "version": "5.1.0-beta.2", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From 07fbf741005b4ad6679ef75c196cd0c55f56b8a3 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Jul 2021 10:29:56 -0400 Subject: [PATCH 055/131] release v5.1.0 (#3390) Signed-off-by: Sebastian Malton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 306bac96f8..01d2b33d74 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.1.0-beta.2", + "version": "5.1.0", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From f9635504f9ec28c06b55b4312f5c93e127ee888f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Jul 2021 12:59:03 -0400 Subject: [PATCH 056/131] Stringify errors that get sent to notifications (#3393) Signed-off-by: Sebastian Malton --- src/renderer/components/notifications/notifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/notifications/notifications.tsx b/src/renderer/components/notifications/notifications.tsx index c940ec469a..b4e9344aa0 100644 --- a/src/renderer/components/notifications/notifications.tsx +++ b/src/renderer/components/notifications/notifications.tsx @@ -88,7 +88,7 @@ export class Notifications extends React.Component { getMessage(notification: Notification) { let { message } = notification; - if (message instanceof JsonApiErrorParsed) { + if (message instanceof JsonApiErrorParsed || message instanceof Error) { message = message.toString(); } From 873a165a60a7d66b5d0ae6a68c3384e1e8518f83 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Jul 2021 12:59:43 -0400 Subject: [PATCH 057/131] Fix clusters not being defined (#3392) Signed-off-by: Sebastian Malton --- src/migrations/cluster-store/5.0.0-beta.10.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/migrations/cluster-store/5.0.0-beta.10.ts b/src/migrations/cluster-store/5.0.0-beta.10.ts index 3a2b5bdfe3..4b7fa9f3bd 100644 --- a/src/migrations/cluster-store/5.0.0-beta.10.ts +++ b/src/migrations/cluster-store/5.0.0-beta.10.ts @@ -45,7 +45,7 @@ export default { workspaces.set(id, name); } - const clusters: ClusterModel[] = store.get("clusters"); + const clusters: ClusterModel[] = store.get("clusters") ?? []; for (const cluster of clusters) { if (cluster.workspace && workspaces.has(cluster.workspace)) { From c3823b9d2721cf80daab0cbb110e082fe2131459 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Jul 2021 13:52:21 -0400 Subject: [PATCH 058/131] Failing to load KubeStateMetrics should only warn (#3394) Signed-off-by: Sebastian Malton --- src/renderer/components/+workloads-pods/pods.store.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/+workloads-pods/pods.store.ts b/src/renderer/components/+workloads-pods/pods.store.ts index f9d5d87464..62a6a720af 100644 --- a/src/renderer/components/+workloads-pods/pods.store.ts +++ b/src/renderer/components/+workloads-pods/pods.store.ts @@ -51,11 +51,9 @@ export class PodsStore extends KubeObjectStore { async loadKubeMetrics(namespace?: string) { try { - const metrics = await podMetricsApi.list({ namespace }); - - this.kubeMetrics.replace(metrics); + this.kubeMetrics.replace(await podMetricsApi.list({ namespace })); } catch (error) { - console.error("loadKubeMetrics failed", error); + console.warn("loadKubeMetrics failed", error); } } From ea4a5a4c578ea95d3c43a0797a3b891d7d357f8b Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Jul 2021 14:19:33 -0400 Subject: [PATCH 059/131] getPortFrom should log the lines it retrived if it fails to get a port (#3395) --- src/main/utils/get-port.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/utils/get-port.ts b/src/main/utils/get-port.ts index cdce0ce740..c44e843712 100644 --- a/src/main/utils/get-port.ts +++ b/src/main/utils/get-port.ts @@ -21,6 +21,7 @@ import type { Readable } from "stream"; import URLParse from "url-parse"; +import logger from "../logger"; interface GetPortArgs { /** @@ -47,11 +48,15 @@ interface GetPortArgs { * @returns A Promise for port number */ export function getPortFrom(stream: Readable, args: GetPortArgs): Promise { + const logLines: string[] = []; + return new Promise((resolve, reject) => { const handler = (data: any) => { const logItem: string = data.toString(); const match = logItem.match(args.lineRegex); + logLines.push(logItem); + if (match) { // use unknown protocol so that there is no default port const addr = new URLParse(`s://${match.groups.address.trim()}`); @@ -64,6 +69,7 @@ export function getPortFrom(stream: Readable, args: GetPortArgs): Promise { stream.off("data", handler); + logger.warn(`[getPortFrom]: failed to retrieve port via ${args.lineRegex.toString()}: ${logLines}`); reject(new Error("failed to retrieve port from stream")); }, args.timeout ?? 5000); From 9903417fedaf4e17d2fdf344cd6ff1065b35d588 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 15 Jul 2021 17:16:54 -0400 Subject: [PATCH 060/131] Failing to load chart releases should only warn (#3399) Signed-off-by: Sebastian Malton --- src/renderer/components/+apps-releases/release.store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/components/+apps-releases/release.store.ts b/src/renderer/components/+apps-releases/release.store.ts index ae69d38bc1..d3ffdadb43 100644 --- a/src/renderer/components/+apps-releases/release.store.ts +++ b/src/renderer/components/+apps-releases/release.store.ts @@ -91,7 +91,7 @@ export class ReleaseStore extends ItemStore { this.failedLoading = false; } catch (error) { this.failedLoading = true; - console.error("Loading Helm Chart releases has failed", error); + console.warn("Loading Helm Chart releases has failed", error); if (error.error) { Notifications.error(error.error); From e87b4836be29eca6f7aaa4da5b1656798b67dc4a Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Fri, 16 Jul 2021 08:53:01 -0400 Subject: [PATCH 061/131] Fix spelling (#3181) Co-authored-by: Josh Soref --- docs/extensions/get-started/anatomy.md | 2 +- docs/extensions/guides/generator.md | 2 +- docs/extensions/guides/kube-object-list-layout.md | 2 +- docs/extensions/guides/main-extension.md | 2 +- docs/extensions/guides/resource-stack.md | 2 +- docs/extensions/guides/stores.md | 4 ++-- docs/extensions/testing-and-publishing/publishing.md | 2 +- src/common/__tests__/catalog-category-registry.test.ts | 2 +- src/common/hotbar-store.ts | 4 ++-- src/common/user-store/preferences-helpers.ts | 6 +++--- src/common/utils/extended-map.ts | 2 +- src/extensions/__tests__/extension-loader.test.ts | 2 +- src/extensions/ipc/ipc-main.ts | 2 +- src/extensions/lens-extension.ts | 2 +- src/main/__test__/context-handler.test.ts | 2 +- src/main/helm/helm-repo-manager.ts | 2 +- src/main/index.ts | 2 +- src/main/tray.ts | 2 +- src/renderer/api/kube-api-parse.ts | 2 +- src/renderer/bootstrap.tsx | 2 +- src/renderer/components/+apps-releases/release.store.ts | 2 +- src/renderer/components/+apps-releases/releases.tsx | 2 +- .../components/+extensions/extension-install.store.ts | 2 +- src/renderer/components/+extensions/extensions.tsx | 4 ++-- .../components/+preferences/add-helm-repo-dialog.tsx | 4 ++-- src/renderer/components/+preferences/preferences.tsx | 2 +- src/renderer/components/cluster-manager/bottom-bar.test.tsx | 2 +- .../components/cluster-accessible-namespaces.tsx | 4 ++-- src/renderer/components/error-boundary/error-boundary.tsx | 2 +- src/renderer/components/file-picker/file-picker.tsx | 4 ++-- .../kube-object-status-icon/kube-object-status-icon.tsx | 2 +- .../components/markdown-viewer/markdown-viewer.scss | 4 ++-- .../components/notifications/notifications.store.tsx | 2 +- src/renderer/components/resizing-anchor/resizing-anchor.tsx | 2 +- src/renderer/themes/lens-dark.json | 6 +++--- src/renderer/themes/lens-light.json | 6 +++--- src/renderer/themes/theme-vars.scss | 6 +++--- src/renderer/utils/__tests__/jsonPath.test.tsx | 2 +- src/renderer/utils/jsonPath.ts | 2 +- 39 files changed, 54 insertions(+), 54 deletions(-) diff --git a/docs/extensions/get-started/anatomy.md b/docs/extensions/get-started/anatomy.md index 87b3042189..745bff5a89 100644 --- a/docs/extensions/get-started/anatomy.md +++ b/docs/extensions/get-started/anatomy.md @@ -6,7 +6,7 @@ In this section you will learn how this extension works under the hood. The Hello World sample extension does three things: - Implements `onActivate()` and outputs a message to the console. -- Implements `onDectivate()` and outputs a message to the console. +- Implements `onDeactivate()` and outputs a message to the console. - Registers `ClusterPage` so that the page is visible in the left-side menu of the cluster dashboard. Let's take a closer look at our Hello World sample's source code and see how these three things are achieved. diff --git a/docs/extensions/guides/generator.md b/docs/extensions/guides/generator.md index cbbdaa31bd..64838c1bc4 100644 --- a/docs/extensions/guides/generator.md +++ b/docs/extensions/guides/generator.md @@ -72,4 +72,4 @@ To dive deeper, consider looking at [Common Capabilities](../capabilities/common 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). -The Generator source code is hosted at [Github](https://github.com/lensapp/generator-lens-ext). +The Generator source code is hosted at [GitHub](https://github.com/lensapp/generator-lens-ext). diff --git a/docs/extensions/guides/kube-object-list-layout.md b/docs/extensions/guides/kube-object-list-layout.md index f5ee72e902..6d350c98cf 100644 --- a/docs/extensions/guides/kube-object-list-layout.md +++ b/docs/extensions/guides/kube-object-list-layout.md @@ -213,7 +213,7 @@ export class CertificatePage extends React.Component<{ extension: LensRendererEx return ( certificate.getName(), [sortBy.namespace]: (certificate: Certificate) => certificate.metadata.namespace, diff --git a/docs/extensions/guides/main-extension.md b/docs/extensions/guides/main-extension.md index 8a501ca533..fec937db17 100644 --- a/docs/extensions/guides/main-extension.md +++ b/docs/extensions/guides/main-extension.md @@ -94,7 +94,7 @@ export default class SamplePageMainExtension extends Main.LensExtension { ``` When the menu item is clicked the `navigate()` method looks for and displays a global page with id `"myGlobalPage"`. -This page would be defined in your extension's `Renderer.LensExtension` implmentation (See [`Renderer.LensExtension`](renderer-extension.md)). +This page would be defined in your extension's `Renderer.LensExtension` implementation (See [`Renderer.LensExtension`](renderer-extension.md)). ### `addCatalogSource()` and `removeCatalogSource()` Methods diff --git a/docs/extensions/guides/resource-stack.md b/docs/extensions/guides/resource-stack.md index 8d36bb6495..4d8c494339 100644 --- a/docs/extensions/guides/resource-stack.md +++ b/docs/extensions/guides/resource-stack.md @@ -90,7 +90,7 @@ This is the cluster that the resource stack will be applied to, and the construc Similarly, `ExampleClusterFeature` implements an `uninstall()` method which simply invokes the `kubectlDeleteFolder()` method of the `Renderer.K8sApi.ResourceStack` class. `kubectlDeleteFolder()` tries to delete from the cluster all kubernetes resources found in the folder passed to it, again in this case `../resources`. -`ExampleClusterFeature` also implements an `isInstalled()` method, which demonstrates how you can utiliize the kubernetes api to inspect the resource stack status. +`ExampleClusterFeature` also implements an `isInstalled()` method, which demonstrates how you can utilize the kubernetes api to inspect the resource stack status. `isInstalled()` simply tries to find a pod named `example-pod`, as a way to determine if the pod is already installed. This method can be useful in creating a context-sensitive UI for installing/uninstalling the feature, as demonstrated in the next sample code. diff --git a/docs/extensions/guides/stores.md b/docs/extensions/guides/stores.md index 3a990f58b7..8b31548a32 100644 --- a/docs/extensions/guides/stores.md +++ b/docs/extensions/guides/stores.md @@ -148,8 +148,8 @@ export class ExamplePreferenceInput extends React.Component { return ( { ExamplePreferencesStore.getInstace().enabled = v; }} + value={ExamplePreferencesStore.getInstance().enabled} + onChange={v => { ExamplePreferencesStore.getInstance().enabled = v; }} /> ); } diff --git a/docs/extensions/testing-and-publishing/publishing.md b/docs/extensions/testing-and-publishing/publishing.md index d8e7b8efad..9ea33c4e19 100644 --- a/docs/extensions/testing-and-publishing/publishing.md +++ b/docs/extensions/testing-and-publishing/publishing.md @@ -43,4 +43,4 @@ Say you have your project folder at `~/my-extension/` and you want to create an npm pack ``` -This will create a NPM tarball that can be hosted on Github Releases or any other publicly available file hosting service. +This will create a NPM tarball that can be hosted on GitHub Releases or any other publicly available file hosting service. diff --git a/src/common/__tests__/catalog-category-registry.test.ts b/src/common/__tests__/catalog-category-registry.test.ts index 11f7a88afd..90615d552c 100644 --- a/src/common/__tests__/catalog-category-registry.test.ts +++ b/src/common/__tests__/catalog-category-registry.test.ts @@ -56,7 +56,7 @@ class TestCatalogCategory2 extends CatalogCategory { } describe("CatalogCategoryRegistry", () => { - it("should remove only the category registered when running the disopser", () => { + it("should remove only the category registered when running the disposer", () => { const registry = new TestCatalogCategoryRegistry(); expect(registry.items.length).toBe(0); diff --git a/src/common/hotbar-store.ts b/src/common/hotbar-store.ts index 90694e9976..1634bab5cb 100644 --- a/src/common/hotbar-store.ts +++ b/src/common/hotbar-store.ts @@ -52,7 +52,7 @@ export interface HotbarStoreModel { activeHotbarId: string; } -export const defaultHotbarCells = 12; // Number is choosen to easy hit any item with keyboard +export const defaultHotbarCells = 12; // Number is chosen to easy hit any item with keyboard export class HotbarStore extends BaseStore { @observable hotbars: Hotbar[] = []; @@ -203,7 +203,7 @@ export class HotbarStore extends BaseStore { } /** - * Remvove all hotbar items that reference the `uid`. + * Remove all hotbar items that reference the `uid`. * @param uid The `EntityId` that each hotbar item refers to * @returns A function that will (in an action) undo the removing of the hotbar items. This function will not complete if the hotbar has changed. */ diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts index 3d205cd380..4363511120 100644 --- a/src/common/user-store/preferences-helpers.ts +++ b/src/common/user-store/preferences-helpers.ts @@ -193,9 +193,9 @@ const hiddenTableColumns: PreferenceDescription<[string, string[]][], Map extends Map { } /** - * Get the value behind `key`. If it was not pressent, first insert the value returned by `getVal` + * Get the value behind `key`. If it was not present, first insert the value returned by `getVal` * @param key The key to insert into the map with * @param getVal A function that returns a new instance of `V`. * @returns The value in the map diff --git a/src/extensions/__tests__/extension-loader.test.ts b/src/extensions/__tests__/extension-loader.test.ts index 5e0844d02d..e35afe5a1b 100644 --- a/src/extensions/__tests__/extension-loader.test.ts +++ b/src/extensions/__tests__/extension-loader.test.ts @@ -84,7 +84,7 @@ jest.mock( (channel: string, listener: (event: any, ...args: any[]) => void) => { if (channel === "extensions:main") { // First initialize with extensions 1 and 2 - // and then broadcast event to remove extensioin 2 and add extension number 3 + // and then broadcast event to remove extension 2 and add extension number 3 setTimeout(() => { listener({}, [ [ diff --git a/src/extensions/ipc/ipc-main.ts b/src/extensions/ipc/ipc-main.ts index eec89e9966..5884a3989e 100644 --- a/src/extensions/ipc/ipc-main.ts +++ b/src/extensions/ipc/ipc-main.ts @@ -36,7 +36,7 @@ export abstract class IpcMain extends IpcRegistrar { * Listen for broadcasts within your extension * @param channel The channel to listen for broadcasts on * @param listener The function that will be called with the arguments of the broadcast - * @returns An optional disopser, Lens will cleanup when the extension is disabled or uninstalled even if this is not called + * @returns An optional disposer, Lens will cleanup when the extension is disabled or uninstalled even if this is not called */ listen(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; diff --git a/src/extensions/lens-extension.ts b/src/extensions/lens-extension.ts index ac77a3b229..5d562953ca 100644 --- a/src/extensions/lens-extension.ts +++ b/src/extensions/lens-extension.ts @@ -74,7 +74,7 @@ export class LensExtension { * getExtensionFileFolder returns the path to an already created folder. This * folder is for the sole use of this extension. * - * Note: there is no security done on this folder, only obfiscation of the + * Note: there is no security done on this folder, only obfuscation of the * folder name. */ async getExtensionFileFolder(): Promise { diff --git a/src/main/__test__/context-handler.test.ts b/src/main/__test__/context-handler.test.ts index 0ba3a2d028..0652ed5733 100644 --- a/src/main/__test__/context-handler.test.ts +++ b/src/main/__test__/context-handler.test.ts @@ -207,7 +207,7 @@ describe("ContextHandler", () => { expect(service.id === "id_0"); }); - it("shouldn't pick the second provider of 2 succcess(es) after 1 failure(s)", async () => { + it("shouldn't pick the second provider of 2 success(es) after 1 failure(s)", async () => { const reg = PrometheusProviderRegistry.getInstance(); let count = 0; diff --git a/src/main/helm/helm-repo-manager.ts b/src/main/helm/helm-repo-manager.ts index dc30d92edd..d1c904539d 100644 --- a/src/main/helm/helm-repo-manager.ts +++ b/src/main/helm/helm-repo-manager.ts @@ -146,7 +146,7 @@ export class HelmRepoManager extends Singleton { return stdout; } - public static async addСustomRepo(repoAttributes : HelmRepo) { + public static async addCustomRepo(repoAttributes : HelmRepo) { logger.info(`[HELM]: adding repo "${repoAttributes.name}" from ${repoAttributes.url}`); const helm = await helmCli.binaryPath(); diff --git a/src/main/index.ts b/src/main/index.ts index 684a2e5fb2..a5116cfdb0 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -137,7 +137,7 @@ app.on("ready", async () => { /** * The following sync MUST be done before HotbarStore creation, because that * store has migrations that will remove items that previous migrations add - * if this is not presant + * if this is not present */ syncGeneralEntities(); diff --git a/src/main/tray.ts b/src/main/tray.ts index 24b00d122a..3a772503f8 100644 --- a/src/main/tray.ts +++ b/src/main/tray.ts @@ -93,7 +93,7 @@ function createTrayMenu(windowManager: WindowManager): Menu { click() { windowManager .navigate(preferencesURL()) - .catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to nativate to Preferences`, { error })); + .catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to navigate to Preferences`, { error })); }, } ]; diff --git a/src/renderer/api/kube-api-parse.ts b/src/renderer/api/kube-api-parse.ts index 10e280d6c8..689a6b9858 100644 --- a/src/renderer/api/kube-api-parse.ts +++ b/src/renderer/api/kube-api-parse.ts @@ -114,7 +114,7 @@ function _parseKubeApi(path: string): IKubeApiParsed { * There is no well defined selection from an array of items that were * separated by '/' * - * Solution is to create a huristic. Namely: + * Solution is to create a heuristic. Namely: * 1. if '.' in left[0] then apiGroup <- left[0] * 2. if left[1] matches /^v[0-9]/ then apiGroup, apiVersion <- left[0], left[1] * 3. otherwise assume apiVersion <- left[0] diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 2a36c15089..87c30dba59 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -53,7 +53,7 @@ import { TerminalStore } from "./components/dock/terminal.store"; configurePackages(); /** - * If this is a development buid, wait a second to attach + * If this is a development build, wait a second to attach * Chrome Debugger to renderer process * https://stackoverflow.com/questions/52844870/debugging-electron-renderer-process-with-vscode */ diff --git a/src/renderer/components/+apps-releases/release.store.ts b/src/renderer/components/+apps-releases/release.store.ts index d3ffdadb43..9b450a7465 100644 --- a/src/renderer/components/+apps-releases/release.store.ts +++ b/src/renderer/components/+apps-releases/release.store.ts @@ -58,7 +58,7 @@ export class ReleaseStore extends ItemStore { }); } - watchSelecteNamespaces(): (() => void) { + watchSelectedNamespaces(): (() => void) { return reaction(() => namespaceStore.context.contextNamespaces, namespaces => { this.loadAll(namespaces); }); diff --git a/src/renderer/components/+apps-releases/releases.tsx b/src/renderer/components/+apps-releases/releases.tsx index 9a746b8909..453983e8d0 100644 --- a/src/renderer/components/+apps-releases/releases.tsx +++ b/src/renderer/components/+apps-releases/releases.tsx @@ -56,7 +56,7 @@ export class HelmReleases extends Component { componentDidMount() { disposeOnUnmount(this, [ releaseStore.watchAssociatedSecrets(), - releaseStore.watchSelecteNamespaces(), + releaseStore.watchSelectedNamespaces(), ]); } diff --git a/src/renderer/components/+extensions/extension-install.store.ts b/src/renderer/components/+extensions/extension-install.store.ts index c02bb482fd..4ca79ec337 100644 --- a/src/renderer/components/+extensions/extension-install.store.ts +++ b/src/renderer/components/+extensions/extension-install.store.ts @@ -225,7 +225,7 @@ export class ExtensionInstallationStateStore { } /** - * If there is at least one extension currently ininstalling + * If there is at least one extension currently uninstalling */ static get anyUninstalling(): boolean { return ExtensionInstallationStateStore.uninstalling > 0; diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index b135487343..0265a6ddf3 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -49,7 +49,7 @@ import { docsUrl } from "../../../common/vars"; function getMessageFromError(error: any): string { if (!error || typeof error !== "object") { - return "an error has occured"; + return "an error has occurred"; } if (error.message) { @@ -63,7 +63,7 @@ function getMessageFromError(error: any): string { const rawMessage = String(error); if (rawMessage === String({})) { - return "an error has occured"; + return "an error has occurred"; } return rawMessage; diff --git a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx index 1bf978de68..44ef32b9af 100644 --- a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx +++ b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx @@ -107,7 +107,7 @@ export class AddHelmRepoDialog extends React.Component { async addCustomRepo() { try { - await HelmRepoManager.addСustomRepo(this.helmRepo); + await HelmRepoManager.addCustomRepo(this.helmRepo); Notifications.ok(<>Helm repository {this.helmRepo.name} has added); this.props.onAddRepo(); this.close(); @@ -145,7 +145,7 @@ export class AddHelmRepoDialog extends React.Component { /> {this.renderFileInput(`Key file`, FileType.KeyFile, AddHelmRepoDialog.keyExtensions)} {this.renderFileInput(`Ca file`, FileType.CaFile, AddHelmRepoDialog.certExtensions)} - {this.renderFileInput(`Cerificate file`, FileType.CertFile, AddHelmRepoDialog.certExtensions)} + {this.renderFileInput(`Certificate file`, FileType.CertFile, AddHelmRepoDialog.certExtensions)} ", () => { expect(container).toBeInstanceOf(HTMLElement); }); - it("renders w/o errors when .getItems() returns unexpected (not type complient) data", async () => { + it("renders w/o errors when .getItems() returns unexpected (not type compliant) data", async () => { StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => undefined); expect(() => render()).not.toThrow(); StatusBarRegistry.getInstance().getItems = jest.fn().mockImplementationOnce(() => "hello"); diff --git a/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx b/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx index 0c087083fe..62f426efe1 100644 --- a/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx +++ b/src/renderer/components/cluster-settings/components/cluster-accessible-namespaces.tsx @@ -52,8 +52,8 @@ export class ClusterAccessibleNamespaces extends React.Component { }} validators={systemName} items={Array.from(this.namespaces)} - remove={({ oldItem: oldNamesapce }) => { - this.namespaces.delete(oldNamesapce); + remove={({ oldItem: oldNamespace }) => { + this.namespaces.delete(oldNamespace); this.props.cluster.accessibleNamespaces = Array.from(this.namespaces); }} inputTheme="round-black" diff --git a/src/renderer/components/error-boundary/error-boundary.tsx b/src/renderer/components/error-boundary/error-boundary.tsx index 56be840ff7..dd345e9f48 100644 --- a/src/renderer/components/error-boundary/error-boundary.tsx +++ b/src/renderer/components/error-boundary/error-boundary.tsx @@ -35,7 +35,7 @@ export class ErrorBoundary extends React.Component { { const slackLink = Slack; - const githubLink = Github; + const githubLink = GitHub; const pageUrl = location.pathname; return ( diff --git a/src/renderer/components/file-picker/file-picker.tsx b/src/renderer/components/file-picker/file-picker.tsx index cc1e850cd2..3661bb2cb2 100644 --- a/src/renderer/components/file-picker/file-picker.tsx +++ b/src/renderer/components/file-picker/file-picker.tsx @@ -127,7 +127,7 @@ export class FilePicker extends React.Component { return files; } - handleIndiviualFileSizes(files: File[]): File[] { + handleIndividualFileSizes(files: File[]): File[] { const { onOverSizeLimit, maxSize } = this.props; switch (onOverSizeLimit) { @@ -178,7 +178,7 @@ export class FilePicker extends React.Component { try { const numberLimitedFiles = this.handleFileCount(files); - const sizeLimitedFiles = this.handleIndiviualFileSizes(numberLimitedFiles); + const sizeLimitedFiles = this.handleIndividualFileSizes(numberLimitedFiles); const totalSizeLimitedFiles = this.handleTotalFileSizes(sizeLimitedFiles); if ("uploadDir" in this.props) { diff --git a/src/renderer/components/kube-object-status-icon/kube-object-status-icon.tsx b/src/renderer/components/kube-object-status-icon/kube-object-status-icon.tsx index ebde73f894..9cd9d6b3af 100644 --- a/src/renderer/components/kube-object-status-icon/kube-object-status-icon.tsx +++ b/src/renderer/components/kube-object-status-icon/kube-object-status-icon.tsx @@ -63,7 +63,7 @@ interface SplitStatusesByLevel { } /** - * This fuction returns the class level for corresponding to the highest status level + * This function returns the class level for corresponding to the highest status level * and the statuses split by their levels. * @param src a list of status items */ diff --git a/src/renderer/components/markdown-viewer/markdown-viewer.scss b/src/renderer/components/markdown-viewer/markdown-viewer.scss index 9afa1f29cc..ccd2599e18 100644 --- a/src/renderer/components/markdown-viewer/markdown-viewer.scss +++ b/src/renderer/components/markdown-viewer/markdown-viewer.scss @@ -383,8 +383,8 @@ blockquote { padding: 8px 1em; - color: $helmDescriptionBlockqouteColor; - border-left: 0.25em solid $helmDescriptionBlockqouteBorder; + color: $helmDescriptionBlockquoteColor; + border-left: 0.25em solid $helmDescriptionBlockquoteBorder; background: $helmDescriptionBlockquoteBackground; } diff --git a/src/renderer/components/notifications/notifications.store.tsx b/src/renderer/components/notifications/notifications.store.tsx index 753a0978d3..63ab5d5e05 100644 --- a/src/renderer/components/notifications/notifications.store.tsx +++ b/src/renderer/components/notifications/notifications.store.tsx @@ -39,7 +39,7 @@ export interface Notification { message: NotificationMessage; status?: NotificationStatus; timeout?: number; // auto-hiding timeout in milliseconds, 0 = no hide - onClose?(): void; // additonal logic on when the notification times out or is closed by the "x" + onClose?(): void; // additional logic on when the notification times out or is closed by the "x" } export class NotificationsStore { diff --git a/src/renderer/components/resizing-anchor/resizing-anchor.tsx b/src/renderer/components/resizing-anchor/resizing-anchor.tsx index a71a604e51..ec564ca219 100644 --- a/src/renderer/components/resizing-anchor/resizing-anchor.tsx +++ b/src/renderer/components/resizing-anchor/resizing-anchor.tsx @@ -100,7 +100,7 @@ interface Props { minExtent?: number; /** - * The following events are triggerred with respect to the above values. + * The following events are triggered with respect to the above values. * - The "__Exceed" call will be made when the unbounded extent goes from * < the above to >= the above * - The "__Subceed" call is similar but is triggered when the unbounded diff --git a/src/renderer/themes/lens-dark.json b/src/renderer/themes/lens-dark.json index fff229699b..9bc65a204a 100644 --- a/src/renderer/themes/lens-dark.json +++ b/src/renderer/themes/lens-dark.json @@ -44,8 +44,8 @@ "helmStableRepo": "#3d90ce", "helmIncubatorRepo": "#ff7043", "helmDescriptionHr": "#41474a", - "helmDescriptionBlockqouteColor": "#bbb", - "helmDescriptionBlockqouteBorder": "#8a8f93", + "helmDescriptionBlockquoteColor": "#bbb", + "helmDescriptionBlockquoteBorder": "#8a8f93", "helmDescriptionBlockquoteBackground": "#3b4348", "helmDescriptionHeaders": "#3e4147", "helmDescriptionH6": "#6a737d", @@ -113,7 +113,7 @@ "iconActiveColor": "#ffffff", "iconActiveBackground": "#ffffff18", "filterAreaBackground": "#23272b", - "chartLiveBarBackgound": "#00000033", + "chartLiveBarBackground": "#00000033", "chartStripesColor": "#ffffff08", "chartCapacityColor": "#4c545f", "pieChartDefaultColor": "#30353a", diff --git a/src/renderer/themes/lens-light.json b/src/renderer/themes/lens-light.json index 8893fe27ac..eae521c1cd 100644 --- a/src/renderer/themes/lens-light.json +++ b/src/renderer/themes/lens-light.json @@ -45,8 +45,8 @@ "helmStableRepo": "#3d90ce", "helmIncubatorRepo": "#ff7043", "helmDescriptionHr": "#dddddd", - "helmDescriptionBlockqouteColor": "#555555", - "helmDescriptionBlockqouteBorder": "#8a8f93", + "helmDescriptionBlockquoteColor": "#555555", + "helmDescriptionBlockquoteBorder": "#8a8f93", "helmDescriptionBlockquoteBackground": "#eeeeee", "helmDescriptionHeaders": "#3e4147", "helmDescriptionH6": "#6a737d", @@ -114,7 +114,7 @@ "iconActiveColor": "#ffffff", "iconActiveBackground": "#a6a6a694", "filterAreaBackground": "#f7f7f7", - "chartLiveBarBackgound": "#00000033", + "chartLiveBarBackground": "#00000033", "chartStripesColor": "#00000009", "chartCapacityColor": "#cccccc", "pieChartDefaultColor": "#efefef", diff --git a/src/renderer/themes/theme-vars.scss b/src/renderer/themes/theme-vars.scss index ac6424a183..80792216b9 100644 --- a/src/renderer/themes/theme-vars.scss +++ b/src/renderer/themes/theme-vars.scss @@ -74,8 +74,8 @@ $helmImgBackground: var(--helmImgBackground); $helmStableRepo: var(--helmStableRepo); $helmIncubatorRepo: var(--helmIncubatorRepo); $helmDescriptionHr: var(--helmDescriptionHr); -$helmDescriptionBlockqouteColor: var(--helmDescriptionBlockqouteColor); -$helmDescriptionBlockqouteBorder: var(--helmDescriptionBlockqouteBorder); +$helmDescriptionBlockquoteColor: var(--helmDescriptionBlockquoteColor); +$helmDescriptionBlockquoteBorder: var(--helmDescriptionBlockquoteBorder); $helmDescriptionBlockquoteBackground: var(--helmDescriptionBlockquoteBackground); $helmDescriptionHeaders: var(--helmDescriptionHeaders); $helmDescriptionH6: var(--helmDescriptionH6); @@ -132,7 +132,7 @@ $drawerItemNameColor: var(--drawerItemNameColor); $drawerItemValueColor: var(--drawerItemValueColor); // Charts -$chartLiveBarBackgound: var(--chartLiveBarBackgound); +$chartLiveBarBackground: var(--chartLiveBarBackground); $chartStripesColor: var(--chartStripesColor); $chartCapacityColor: var(--chartCapacityColor); $pieChartDefaultColor: var(--pieChartDefaultColor); diff --git a/src/renderer/utils/__tests__/jsonPath.test.tsx b/src/renderer/utils/__tests__/jsonPath.test.tsx index 6e2049ab19..43468bb1dc 100644 --- a/src/renderer/utils/__tests__/jsonPath.test.tsx +++ b/src/renderer/utils/__tests__/jsonPath.test.tsx @@ -28,7 +28,7 @@ describe("parseJsonPath", () => { expect(res).toBe(".metadata.labels['kubesphere.io/alias-name']"); }); - test("should convert keys with escpaped charatecrs to use indexed notation", () => { + test("should convert keys with escaped characters to use indexed notation", () => { const res = parseJsonPath(".metadata.labels.kubesphere\\\"io/alias-name"); expect(res).toBe(".metadata.labels['kubesphere\"io/alias-name']"); diff --git a/src/renderer/utils/jsonPath.ts b/src/renderer/utils/jsonPath.ts index d055bf6ecb..92855d241a 100644 --- a/src/renderer/utils/jsonPath.ts +++ b/src/renderer/utils/jsonPath.ts @@ -48,7 +48,7 @@ function convertToIndexNotation(key: string, firstItem = false) { } return `['${key}']`; - } else { // no illegal chracters found, do not touch + } else { // no illegal characters found, do not touch const prefix = firstItem ? "" : "."; return `${prefix}${key}`; From eef5dd5f9cca46ed030cb9047a3a57e154a5224d Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 16 Jul 2021 08:57:26 -0400 Subject: [PATCH 062/131] Display toleration's value in pod-details (#3384) Signed-off-by: Sebastian Malton --- src/renderer/api/workload-kube-object.ts | 1 + src/renderer/components/+workloads-pods/pod-tolerations.tsx | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/renderer/api/workload-kube-object.ts b/src/renderer/api/workload-kube-object.ts index 34fa5d5109..8d1f2a6a36 100644 --- a/src/renderer/api/workload-kube-object.ts +++ b/src/renderer/api/workload-kube-object.ts @@ -26,6 +26,7 @@ export interface IToleration { key?: string; operator?: string; effect?: string; + value?: string; tolerationSeconds?: number; } diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-tolerations.tsx index 5ca65cc2b3..9fdaef1c81 100644 --- a/src/renderer/components/+workloads-pods/pod-tolerations.tsx +++ b/src/renderer/components/+workloads-pods/pod-tolerations.tsx @@ -35,6 +35,7 @@ enum sortBy { Operator = "operator", Effect = "effect", Seconds = "seconds", + Value = "value", } const sortingCallbacks = { @@ -45,7 +46,7 @@ const sortingCallbacks = { }; const getTableRow = (toleration: IToleration) => { - const { key, operator, effect, tolerationSeconds } = toleration; + const { key, operator, effect, tolerationSeconds, value } = toleration; return ( { > {key} {operator} + {value} {effect} {tolerationSeconds} @@ -74,6 +76,7 @@ export function PodTolerations({ tolerations }: Props) { Key Operator + Value Effect Seconds From acbca1ac1d49bfed08ebd2afd948ce9cffd0b5b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Jul 2021 14:06:49 -0400 Subject: [PATCH 063/131] Bump mock-fs from 4.12.0 to 4.14.0 (#3117) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Andreev --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 01d2b33d74..952113d365 100644 --- a/package.json +++ b/package.json @@ -216,7 +216,7 @@ "mobx": "^6.3.0", "mobx-observable-history": "^2.0.1", "mobx-react": "^7.1.0", - "mock-fs": "4.14", + "mock-fs": "^4.14.0", "moment": "^2.29.1", "moment-timezone": "^0.5.33", "node-pty": "^0.9.0", diff --git a/yarn.lock b/yarn.lock index 9d099b7397..8243b03f6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10157,7 +10157,7 @@ mobx@^6.3.0: resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.0.tgz#a8fb693c3047bdfcb1eaff9aa48e36a7eb084f96" integrity sha512-Aa1+VXsg4WxqJMTQfWoYuJi5UD10VZhiobSmcs5kcmI3BIT0aVtn7DysvCeDADCzl7dnbX+0BTHUj/v7gLlZpQ== -mock-fs@4.14: +mock-fs@^4.14.0: version "4.14.0" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== From 8c5457cf892c7920d0c72566787cbf3db42caa42 Mon Sep 17 00:00:00 2001 From: Alexandre Barone <48306674+devodev@users.noreply.github.com> Date: Fri, 16 Jul 2021 14:07:06 -0400 Subject: [PATCH 064/131] Fix broken shell when workdir not exists (#3349) Signed-off-by: devodev --- src/main/shell-session/local-shell-session.ts | 5 ++++- src/main/shell-session/node-shell-session.ts | 4 ++++ src/main/shell-session/shell-session.ts | 13 ++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/shell-session/local-shell-session.ts b/src/main/shell-session/local-shell-session.ts index 35a1369339..ed1208fcbd 100644 --- a/src/main/shell-session/local-shell-session.ts +++ b/src/main/shell-session/local-shell-session.ts @@ -31,8 +31,11 @@ export class LocalShellSession extends ShellSession { return [helmCli.getBinaryDir()]; } - public async open() { + protected get cwd(): string | undefined { + return this.cluster.preferences?.terminalCWD; + } + public async open() { const env = await this.getCachedShellEnv(); const shell = env.PTYSHELL; const args = await this.getShellArgs(shell); diff --git a/src/main/shell-session/node-shell-session.ts b/src/main/shell-session/node-shell-session.ts index 7542baecd6..aa8c14ba9b 100644 --- a/src/main/shell-session/node-shell-session.ts +++ b/src/main/shell-session/node-shell-session.ts @@ -32,6 +32,10 @@ export class NodeShellSession extends ShellSession { protected podId = `node-shell-${uuid()}`; protected kc: KubeConfig; + protected get cwd(): string | undefined { + return undefined; + } + constructor(socket: WebSocket, cluster: Cluster, protected nodeName: string) { super(socket, cluster); } diff --git a/src/main/shell-session/shell-session.ts b/src/main/shell-session/shell-session.ts index 039caa44ce..4771ec8b77 100644 --- a/src/main/shell-session/shell-session.ts +++ b/src/main/shell-session/shell-session.ts @@ -19,6 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import fse from "fs-extra"; import type { Cluster } from "../cluster"; import { Kubectl } from "../kubectl"; import type * as WebSocket from "ws"; @@ -50,9 +51,7 @@ export abstract class ShellSession { protected kubectlBinDirP: Promise; protected kubeconfigPathP: Promise; - protected get cwd(): string | undefined { - return this.cluster.preferences?.terminalCWD; - } + protected abstract get cwd(): string | undefined; constructor(protected websocket: WebSocket, protected cluster: Cluster) { this.kubectl = new Kubectl(cluster.version); @@ -60,10 +59,14 @@ export abstract class ShellSession { this.kubectlBinDirP = this.kubectl.binDir(); } - open(shell: string, args: string[], env: Record): void { + protected async open(shell: string, args: string[], env: Record) { + const cwd = (this.cwd && await fse.pathExists(this.cwd)) + ? this.cwd + : env.HOME; + this.shellProcess = pty.spawn(shell, args, { cols: 80, - cwd: this.cwd || env.HOME, + cwd, env, name: "xterm-256color", rows: 30, From 7f332d24b25a43801e5e9396e1b005f4d8a54cff Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 16 Jul 2021 23:04:17 -0400 Subject: [PATCH 065/131] release v5.1.1 (#3414) Signed-off-by: Sebastian Malton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 952113d365..624643e4e3 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.1.0", + "version": "5.1.1", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From b03a255f29a6684928549b6e1a76912ff90f7726 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 07:32:59 +0300 Subject: [PATCH 066/131] Bump webpack from 4.44.2 to 4.46.0 (#3413) Bumps [webpack](https://github.com/webpack/webpack) from 4.44.2 to 4.46.0. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v4.44.2...v4.46.0) --- updated-dependencies: - dependency-name: webpack dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 68 ++++++++++------------------------------------------ 2 files changed, 14 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 624643e4e3..e51b56986d 100644 --- a/package.json +++ b/package.json @@ -377,7 +377,7 @@ "typescript": "^4.3.2", "typescript-plugin-css-modules": "^3.2.0", "url-loader": "^4.1.0", - "webpack": "^4.44.2", + "webpack": "^4.46.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.11.0", "webpack-node-externals": "^1.7.2", diff --git a/yarn.lock b/yarn.lock index 8243b03f6a..41166efdd7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2486,21 +2486,11 @@ acorn@^6.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.0.0: +acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" - integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== - -acorn@^7.4.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" - integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== - agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -2550,17 +2540,12 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv-keywords@^3.1.0, ajv-keywords@^3.5.2: +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" - integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== - -ajv@^6.1.0, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.5.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2570,16 +2555,6 @@ ajv@^6.1.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.5.5: - version "6.12.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" - integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -5518,19 +5493,10 @@ enhanced-resolve@4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" -enhanced-resolve@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" - integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - -enhanced-resolve@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" - integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== +enhanced-resolve@^4.0.0, enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.5.0" @@ -5801,15 +5767,7 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.0.0, eslint-scope@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.0.tgz#d0f971dfe59c69e0cada684b23d49dbf82600ce5" - integrity sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^5.1.1: +eslint-scope@^5.0.0, eslint-scope@^5.1.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -15335,10 +15293,10 @@ webpack-sources@^2.2.0: source-list-map "^2.0.1" source-map "^0.6.1" -webpack@^4.44.2: - version "4.44.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" - integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== +webpack@^4.46.0: + version "4.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== dependencies: "@webassemblyjs/ast" "1.9.0" "@webassemblyjs/helper-module-context" "1.9.0" @@ -15348,7 +15306,7 @@ webpack@^4.44.2: ajv "^6.10.2" ajv-keywords "^3.4.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.3.0" + enhanced-resolve "^4.5.0" eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" loader-runner "^2.4.0" From c1554d90f2b8a8a170f18615b075def1b26205e9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 19 Jul 2021 00:34:18 -0400 Subject: [PATCH 067/131] Catch Cluster create error in cluster-manager (#3401) * Catch Cluster create error in cluster-manager - Only warn if the file is missing. The file must have disapeared betweeen kubeconfig-sync detecting it and cluster-manager performing the addCluster call - For all other errors log an error Signed-off-by: Sebastian Malton * Fix spelling Signed-off-by: Sebastian Malton * Use logger Signed-off-by: Sebastian Malton --- src/main/cluster-manager.ts | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index 73d2c50f46..628bcb22f6 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -32,6 +32,8 @@ import { KubernetesCluster, KubernetesClusterPrometheusMetrics, KubernetesCluste import { ipcMainOn } from "../common/ipc"; import { once } from "lodash"; +const logPrefix = "[CLUSTER-MANAGER]:"; + export class ClusterManager extends Singleton { private store = ClusterStore.getInstance(); deleting = observable.set(); @@ -73,7 +75,7 @@ export class ClusterManager extends Singleton { if (removedClusters.length > 0) { const meta = removedClusters.map(cluster => cluster.getMeta()); - logger.info(`[CLUSTER-MANAGER]: removing clusters`, meta); + logger.info(`${logPrefix} removing clusters`, meta); removedClusters.forEach(cluster => cluster.disconnect()); this.store.removedClusters.clear(); } @@ -161,14 +163,22 @@ export class ClusterManager extends Singleton { const cluster = this.store.getById(entity.metadata.uid); if (!cluster) { - this.store.addCluster({ - id: entity.metadata.uid, - preferences: { - clusterName: entity.metadata.name - }, - kubeConfigPath: entity.spec.kubeconfigPath, - contextName: entity.spec.kubeconfigContext - }); + try { + this.store.addCluster({ + id: entity.metadata.uid, + preferences: { + clusterName: entity.metadata.name + }, + kubeConfigPath: entity.spec.kubeconfigPath, + contextName: entity.spec.kubeconfigContext + }); + } catch (error) { + if (error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) { + logger.warn(`${logPrefix} kubeconfig file disappeared`, { path: entity.spec.kubeconfigPath }); + } else { + logger.error(`${logPrefix} failed to add cluster: ${error}`); + } + } } else { cluster.kubeConfigPath = entity.spec.kubeconfigPath; cluster.contextName = entity.spec.kubeconfigContext; @@ -179,7 +189,7 @@ export class ClusterManager extends Singleton { } protected onNetworkOffline = () => { - logger.info("[CLUSTER-MANAGER]: network is offline"); + logger.info(`${logPrefix} network is offline`); this.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.online = false; @@ -190,7 +200,7 @@ export class ClusterManager extends Singleton { }; protected onNetworkOnline = () => { - logger.info("[CLUSTER-MANAGER]: network is online"); + logger.info(`${logPrefix} network is online`); this.store.clustersList.forEach((cluster) => { if (!cluster.disconnected) { cluster.refreshConnectionStatus().catch((e) => e); From c5040657439d72f84aaf0763e7e6e52114a96531 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 19 Jul 2021 00:46:55 -0400 Subject: [PATCH 068/131] Check metadata labels is an object in getRoleLabels (#3416) Signed-off-by: Sebastian Malton --- src/renderer/api/endpoints/nodes.api.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/renderer/api/endpoints/nodes.api.ts b/src/renderer/api/endpoints/nodes.api.ts index 17a7571209..d2c35761ca 100644 --- a/src/renderer/api/endpoints/nodes.api.ts +++ b/src/renderer/api/endpoints/nodes.api.ts @@ -156,9 +156,13 @@ export class Node extends KubeObject { } getRoleLabels() { - const roleLabels = Object.keys(this.metadata.labels).filter(key => - key.includes("node-role.kubernetes.io") - ).map(key => key.match(/([^/]+$)/)[0]); // all after last slash + if (!this.metadata?.labels || typeof this.metadata.labels !== "object") { + return ""; + } + + const roleLabels = Object.keys(this.metadata.labels) + .filter(key => key.includes("node-role.kubernetes.io")) + .map(key => key.match(/([^/]+$)/)[0]); // all after last slash if (this.metadata.labels["kubernetes.io/role"] != undefined) { roleLabels.push(this.metadata.labels["kubernetes.io/role"]); From 48797b4a572175ab1c428b795bea4ceb0480ad11 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 19 Jul 2021 00:47:54 -0400 Subject: [PATCH 069/131] Check KubeObject.metadata is defined in getOwnerRefs (#3412) Signed-off-by: Sebastian Malton --- src/renderer/api/kube-object.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/renderer/api/kube-object.ts b/src/renderer/api/kube-object.ts index df249f64e6..024651c4d9 100644 --- a/src/renderer/api/kube-object.ts +++ b/src/renderer/api/kube-object.ts @@ -254,12 +254,10 @@ export class KubeObject ({ - ...ownerRef, - namespace: this.getNs(), - })); + return refs.map(ownerRef => ({ ...ownerRef, namespace })); } getSearchFields() { From 106c9d4a37f266562262578cf75a15a7635eeb06 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 19 Jul 2021 11:01:38 +0300 Subject: [PATCH 070/131] fix KubeObjectStore loadAll error message (#3421) Signed-off-by: Jari Kolehmainen --- src/renderer/kube-object.store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index 62dd1cb117..d94ba51bdb 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -195,7 +195,7 @@ export abstract class KubeObjectStore extends ItemSt if (error.message) { Notifications.error(error.message); } - console.error("Loading store items failed", { error }); + console.error("[KubeObjectStore] loadAll failed", this.api.apiBase, error); this.resetOnError(error); this.failedLoading = true; } finally { From ba0581d98250372c5f685438a867d83481541f1f Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Mon, 19 Jul 2021 16:35:51 +0300 Subject: [PATCH 071/131] Skip previously bundled extensions (#3420) Signed-off-by: Jari Kolehmainen --- src/extensions/extension-compatibility.ts | 4 ++++ src/extensions/extension-discovery.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/extensions/extension-compatibility.ts b/src/extensions/extension-compatibility.ts index 9ca48a3f2d..cc5db5515e 100644 --- a/src/extensions/extension-compatibility.ts +++ b/src/extensions/extension-compatibility.ts @@ -31,3 +31,7 @@ export function isCompatibleExtension(manifest: LensExtensionManifest): boolean return false; } + +export function isCompatibleBundledExtension(manifest: LensExtensionManifest): boolean { + return manifest.version === appSemVer.raw; +} diff --git a/src/extensions/extension-discovery.ts b/src/extensions/extension-discovery.ts index 70f32de110..077f29d694 100644 --- a/src/extensions/extension-discovery.ts +++ b/src/extensions/extension-discovery.ts @@ -35,7 +35,7 @@ import { ExtensionsStore } from "./extensions-store"; import { ExtensionLoader } from "./extension-loader"; import type { LensExtensionId, LensExtensionManifest } from "./lens-extension"; import { isProduction } from "../common/vars"; -import { isCompatibleExtension } from "./extension-compatibility"; +import { isCompatibleBundledExtension, isCompatibleExtension } from "./extension-compatibility"; export interface InstalledExtension { id: LensExtensionId; @@ -361,7 +361,7 @@ export class ExtensionDiscovery extends Singleton { const extensionDir = path.dirname(manifestPath); const npmPackage = path.join(extensionDir, `${manifest.name}-${manifest.version}.tgz`); const absolutePath = (isProduction && await fse.pathExists(npmPackage)) ? npmPackage : extensionDir; - const isCompatible = isBundled || isCompatibleExtension(manifest); + const isCompatible = (isBundled && isCompatibleBundledExtension(manifest)) || isCompatibleExtension(manifest); return { id, From e767c392a41b329ebc26917e8db1300d78c4c9d8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 09:39:23 -0400 Subject: [PATCH 072/131] Bump typescript-plugin-css-modules from 3.2.0 to 3.4.0 (#3382) Bumps [typescript-plugin-css-modules](https://github.com/mrmckeb/typescript-plugin-css-modules) from 3.2.0 to 3.4.0. - [Release notes](https://github.com/mrmckeb/typescript-plugin-css-modules/releases) - [Commits](https://github.com/mrmckeb/typescript-plugin-css-modules/compare/v3.2.0...v3.4.0) --- updated-dependencies: - dependency-name: typescript-plugin-css-modules dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 118 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 78 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index e51b56986d..067cef3a94 100644 --- a/package.json +++ b/package.json @@ -375,7 +375,7 @@ "typedoc-plugin-markdown": "^3.9.0", "typeface-roboto": "^1.1.13", "typescript": "^4.3.2", - "typescript-plugin-css-modules": "^3.2.0", + "typescript-plugin-css-modules": "^3.4.0", "url-loader": "^4.1.0", "webpack": "^4.46.0", "webpack-cli": "^3.3.11", diff --git a/yarn.lock b/yarn.lock index 41166efdd7..4ac75156c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5258,6 +5258,11 @@ dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + dotenv@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-5.0.1.tgz#a5317459bd3d79ab88cff6e44057a6a3fbb1fcef" @@ -7439,7 +7444,7 @@ iconv-corefoundation@^1.1.5: cli-truncate "^1.1.0" node-addon-api "^1.6.3" -iconv-lite@0.4.24, iconv-lite@^0.4.17: +iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -7460,13 +7465,6 @@ icss-utils@^3.0.1: dependencies: postcss "^6.0.2" -icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - icss-utils@^5.0.0, icss-utils@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" @@ -7538,6 +7536,13 @@ import-cwd@^2.0.0: dependencies: import-from "^2.1.0" +import-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" + integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== + dependencies: + import-from "^3.0.0" + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" @@ -7577,6 +7582,13 @@ import-from@^2.1.0: dependencies: resolve-from "^3.0.0" +import-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== + dependencies: + resolve-from "^5.0.0" + import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" @@ -9202,12 +9214,13 @@ lcid@^2.0.0: dependencies: invert-kv "^2.0.0" -less@^3.11.1: - version "3.13.1" - resolved "https://registry.yarnpkg.com/less/-/less-3.13.1.tgz#0ebc91d2a0e9c0c6735b83d496b0ab0583077909" - integrity sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw== +less@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/less/-/less-4.1.1.tgz#15bf253a9939791dc690888c3ff424f3e6c7edba" + integrity sha512-w09o8tZFPThBscl5d0Ggp3RcrKIouBoQscnOMgFH3n5V3kN/CXGHNfCkRPtxJk6nKryDXaV9aHLK55RXuH4sAw== dependencies: copy-anything "^2.0.1" + parse-node-version "^1.0.1" tslib "^1.10.0" optionalDependencies: errno "^0.1.1" @@ -9215,7 +9228,7 @@ less@^3.11.1: image-size "~0.5.0" make-dir "^2.1.0" mime "^1.4.1" - native-request "^1.0.5" + needle "^2.5.2" source-map "~0.6.0" leven@^3.1.0: @@ -9387,6 +9400,11 @@ lie@~3.3.0: dependencies: immediate "~3.0.5" +lilconfig@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" + integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg== + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -10226,11 +10244,6 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== -native-request@^1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.8.tgz#8f66bf606e0f7ea27c0e5995eb2f5d03e33ae6fb" - integrity sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag== - native-url@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/native-url/-/native-url-0.2.6.tgz#ca1258f5ace169c716ff44eccbddb674e10399ae" @@ -10243,6 +10256,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +needle@^2.5.2: + version "2.8.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.8.0.tgz#1c8ef9c1a2c29dcc1e83d73809d7bc681c80a048" + integrity sha512-ZTq6WYkN/3782H1393me3utVYdq2XyqNUFBsprEE3VMAT0+hP/cItpnITpqsY6ep2yeFE4Tqtqwc74VqUlUYtw== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -11362,6 +11384,11 @@ parse-json@^5.0.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" @@ -11652,7 +11679,7 @@ postcss-js@^3.0.3: camelcase-css "^2.0.1" postcss "^8.1.6" -postcss-load-config@^2.0.0, postcss-load-config@^2.1.0: +postcss-load-config@^2.0.0: version "2.1.2" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== @@ -11660,6 +11687,15 @@ postcss-load-config@^2.0.0, postcss-load-config@^2.1.0: cosmiconfig "^5.0.0" import-cwd "^2.0.0" +postcss-load-config@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.0.tgz#d39c47091c4aec37f50272373a6a648ef5e97829" + integrity sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g== + dependencies: + import-cwd "^3.0.0" + lilconfig "^2.0.3" + yaml "^1.10.2" + postcss-loader@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" @@ -11741,7 +11777,7 @@ postcss@^6.0.14, postcss@^6.0.2, postcss@^6.0.9: source-map "^0.6.1" supports-color "^5.4.0" -postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.27: +postcss@^7.0.0: version "7.0.35" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== @@ -11750,10 +11786,10 @@ postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.27: source-map "^0.6.1" supports-color "^6.1.0" -postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.14, postcss@^8.2.15: - version "8.3.0" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.0.tgz#b1a713f6172ca427e3f05ef1303de8b65683325f" - integrity sha512-+ogXpdAjWGa+fdYY5BQ96V/6tAo+TdSSIMP5huJBIygdWwKtVoB5JWZ7yUd4xZ8r+8Kvvx4nyg/PQ071H4UtcQ== +postcss@^8.1.6, postcss@^8.2.1, postcss@^8.2.14, postcss@^8.2.15, postcss@^8.3.0: + version "8.3.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" + integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== dependencies: colorette "^1.2.2" nanoid "^3.1.23" @@ -12981,10 +13017,10 @@ sass-loader@^8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sass@^1.26.5: - version "1.32.12" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.12.tgz#a2a47ad0f1c168222db5206444a30c12457abb9f" - integrity sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA== +sass@^1.32.13: + version "1.35.2" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.35.2.tgz#b732314fcdaf7ef8d0f1698698adc378043cb821" + integrity sha512-jhO5KAR+AMxCEwIH3v+4zbB2WB0z67V1X0jbapfVwQQdjHZUGUyukpnoM6+iCMfsIUC016w9OPKQ5jrNOS9uXw== dependencies: chokidar ">=3.0.0 <4.0.0" @@ -13982,7 +14018,7 @@ style-loader@^2.0.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -stylus@^0.54.7: +stylus@^0.54.8: version "0.54.8" resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== @@ -14681,23 +14717,23 @@ typeface-roboto@^1.1.13: resolved "https://registry.yarnpkg.com/typeface-roboto/-/typeface-roboto-1.1.13.tgz#9c4517cb91e311706c74823e857b4bac9a764ae5" integrity sha512-YXvbd3a1QTREoD+FJoEkl0VQNJoEjewR2H11IjVv4bp6ahuIcw0yyw/3udC4vJkHw3T3cUh85FTg8eWef3pSaw== -typescript-plugin-css-modules@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/typescript-plugin-css-modules/-/typescript-plugin-css-modules-3.2.0.tgz#44241064395d565f242689b96eb50bf413e68ddf" - integrity sha512-oMe5IDKuPvLBOeaYupAqqq8UMTu7Jo5e0InqkQSSKXuZCcRm0+LQfUVMyM62IFpgzxHLuN32a7YdBxAaSXZrRQ== +typescript-plugin-css-modules@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/typescript-plugin-css-modules/-/typescript-plugin-css-modules-3.4.0.tgz#4ff6905d88028684d1608c05c62cb6346e5548cc" + integrity sha512-2MdjfSg4MGex1csCWRUwKD+MpgnvcvLLr9bSAMemU/QYGqBsXdez0cc06H/fFhLtRoKJjXg6PSTur3Gy1Umhpw== dependencies: - dotenv "^8.2.0" - icss-utils "^4.1.1" - less "^3.11.1" + dotenv "^10.0.0" + icss-utils "^5.1.0" + less "^4.1.1" lodash.camelcase "^4.3.0" - postcss "^7.0.27" + postcss "^8.3.0" postcss-filter-plugins "^3.0.1" postcss-icss-keyframes "^0.2.1" postcss-icss-selectors "^2.0.3" - postcss-load-config "^2.1.0" + postcss-load-config "^3.0.1" reserved-words "^0.1.2" - sass "^1.26.5" - stylus "^0.54.7" + sass "^1.32.13" + stylus "^0.54.8" tsconfig-paths "^3.9.0" typescript@^4.0.3: @@ -15656,7 +15692,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.7.2: +yaml@^1.10.2, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== From 868373563066a990c358c7c169aa4a6a545573bb Mon Sep 17 00:00:00 2001 From: Violetta Shakirova <38247153+vshakirova@users.noreply.github.com> Date: Mon, 19 Jul 2021 19:38:44 +0400 Subject: [PATCH 073/131] Fix HPA visibility (#3426) Signed-off-by: vshakirova --- src/common/rbac.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/rbac.ts b/src/common/rbac.ts index 55b8ca414c..9d8242cd42 100644 --- a/src/common/rbac.ts +++ b/src/common/rbac.ts @@ -40,14 +40,14 @@ export interface KubeApiResourceData { export const apiResourceRecord: Record = { "clusterroles": { kind: "ClusterRole", group: "rbac.authorization.k8s.io" }, "clusterrolebindings": { kind: "ClusterRoleBinding", group: "rbac.authorization.k8s.io" }, - "configmaps": { kind: "ConfigMap" }, + "configmaps": { kind: "ConfigMap" }, //empty group means "core" "cronjobs": { kind: "CronJob", group: "batch" }, "customresourcedefinitions": { kind: "CustomResourceDefinition", group: "apiextensions.k8s.io" }, "daemonsets": { kind: "DaemonSet", group: "apps" }, "deployments": { kind: "Deployment", group: "apps" }, "endpoints": { kind: "Endpoint" }, "events": { kind: "Event" }, - "horizontalpodautoscalers": { kind: "HorizontalPodAutoscaler" }, + "horizontalpodautoscalers": { kind: "HorizontalPodAutoscaler", group: "autoscaling" }, "ingresses": { kind: "Ingress", group: "networking.k8s.io" }, "jobs": { kind: "Job", group: "batch" }, "namespaces": { kind: "Namespace" }, @@ -58,13 +58,13 @@ export const apiResourceRecord: Record = { "persistentvolumeclaims": { kind: "PersistentVolumeClaim" }, "pods": { kind: "Pod" }, "poddisruptionbudgets": { kind: "PodDisruptionBudget", group: "policy" }, - "podsecuritypolicies": { kind: "PodSecurityPolicy" }, + "podsecuritypolicies": { kind: "PodSecurityPolicy", group: "policy" }, "resourcequotas": { kind: "ResourceQuota" }, "replicasets": { kind: "ReplicaSet", group: "apps" }, "roles": { kind: "Role", group: "rbac.authorization.k8s.io" }, "rolebindings": { kind: "RoleBinding", group: "rbac.authorization.k8s.io" }, "secrets": { kind: "Secret" }, - "serviceaccounts": { kind: "ServiceAccount", group: "core" }, + "serviceaccounts": { kind: "ServiceAccount" }, "services": { kind: "Service" }, "statefulsets": { kind: "StatefulSet", group: "apps" }, "storageclasses": { kind: "StorageClass", group: "storage.k8s.io" }, From 600d2c8607a5b5d27473bc6017e6fe2825954f20 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 19 Jul 2021 13:40:47 -0400 Subject: [PATCH 074/131] Log as errors the places where getPortFrom can fail (#3425) Signed-off-by: Sebastian Malton --- src/main/proxy/lens-proxy.ts | 6 ++++-- src/main/routes/port-forward-route.ts | 17 ++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/proxy/lens-proxy.ts b/src/main/proxy/lens-proxy.ts index 480a4b047a..9ffff0fd6a 100644 --- a/src/main/proxy/lens-proxy.ts +++ b/src/main/proxy/lens-proxy.ts @@ -61,7 +61,8 @@ export class LensProxy extends Singleton { if (req.url.startsWith(`${apiPrefix}?`)) { handleWsUpgrade(req, socket, head); } else { - this.handleProxyUpgrade(proxy, req, socket, head); + this.handleProxyUpgrade(proxy, req, socket, head) + .catch(error => logger.error(`[LENS-PROXY]: failed to handle proxy upgrade: ${error}`)); } }); } @@ -194,7 +195,8 @@ export class LensProxy extends Singleton { logger.debug(`Retrying proxy request to url: ${reqId}`); setTimeout(() => { this.retryCounters.set(reqId, retryCount + 1); - this.handleRequest(proxy, req, res); + this.handleRequest(proxy, req, res) + .catch(error => logger.error(`[LENS-PROXY]: failed to handle request on proxy error: ${error}`)); }, timeoutMs); } } diff --git a/src/main/routes/port-forward-route.ts b/src/main/routes/port-forward-route.ts index cfd88e771f..e97b4171cb 100644 --- a/src/main/routes/port-forward-route.ts +++ b/src/main/routes/port-forward-route.ts @@ -134,11 +134,22 @@ export class PortForwardRoute { name: resourceName, port, }); - const started = await portForward.start(); - if (!started) { + try { + const started = await portForward.start(); + + if (!started) { + logger.warn("[PORT-FORWARD-ROUTE]: failed to start a port-forward", { namespace, port, resourceType, resourceName }); + + return respondJson(response, { + message: "Failed to open port-forward" + }, 400); + } + } catch (error) { + logger.warn(`[PORT-FORWARD-ROUTE]: failed to open a port-forward: ${error}`, { namespace, port, resourceType, resourceName }); + return respondJson(response, { - message: "Failed to open port-forward" + message: error?.toString() || "Failed to open port-forward", }, 400); } } From 4873b856476bd778bc32c408ad261a471ea02b20 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 19 Jul 2021 16:15:58 -0400 Subject: [PATCH 075/131] release v5.1.2 (#3431) Signed-off-by: Sebastian Malton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 067cef3a94..e7ff4d9abc 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.1.1", + "version": "5.1.2", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From 078583412ba462e78fe5d6fe3239e84320c63db4 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 00:33:22 -0400 Subject: [PATCH 076/131] Use all namespaces for cluster overview warnings (#3441) Signed-off-by: Sebastian Malton --- src/renderer/components/+cluster/cluster-issues.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 90a8a1a74b..f35f8bea61 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -22,7 +22,7 @@ import "./cluster-issues.scss"; import React from "react"; -import { observer } from "mobx-react"; +import { disposeOnUnmount, observer } from "mobx-react"; import { computed, makeObservable } from "mobx"; import { Icon } from "../icon"; import { SubHeader } from "../layout/sub-header"; @@ -35,6 +35,7 @@ import { Spinner } from "../spinner"; import { ThemeStore } from "../../theme.store"; import { lookupApiLink } from "../../api/kube-api"; import { kubeSelectedUrlParam, showDetails } from "../kube-object"; +import { kubeWatchApi } from "../../api/kube-watch-api"; interface Props { className?: string; @@ -65,6 +66,10 @@ export class ClusterIssues extends React.Component { constructor(props: Props) { super(props); makeObservable(this); + + disposeOnUnmount(this, [ + kubeWatchApi.subscribeStores([eventStore, nodesStore]) + ]); } @computed get warnings() { From 70448159d3f8cf1b5b2389a34411080bc2acc4ae Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 00:33:38 -0400 Subject: [PATCH 077/131] Failing to loadAll should only warn (#3442) Signed-off-by: Sebastian Malton --- src/renderer/kube-object.store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index d94ba51bdb..881a9ae6f3 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -195,7 +195,7 @@ export abstract class KubeObjectStore extends ItemSt if (error.message) { Notifications.error(error.message); } - console.error("[KubeObjectStore] loadAll failed", this.api.apiBase, error); + console.warn("[KubeObjectStore] loadAll failed", this.api.apiBase, error); this.resetOnError(error); this.failedLoading = true; } finally { From c590bccffaf6ba4fde7d35cfdeb794cc6b7d6117 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 00:34:23 -0400 Subject: [PATCH 078/131] Fix permissions issue on ServiceDetails under RBAC (#3433) - Only watch the namespace that the service is in instead of all of them Signed-off-by: Sebastian Malton --- src/renderer/components/+network-services/service-details.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 6450850076..ec10cd5f94 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -39,9 +39,12 @@ interface Props extends KubeObjectDetailsProps { @observer export class ServiceDetails extends React.Component { componentDidMount() { + const { object: service } = this.props; + disposeOnUnmount(this, [ kubeWatchApi.subscribeStores([endpointStore], { preload: true, + namespaces: [service.getNs()], }), ]); } From c4aeaf5bb71e73d2747f5d5ff8f0b54645d8799f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 00:40:02 -0400 Subject: [PATCH 079/131] Fix styling on cluster roles and bindings (#3434) Signed-off-by: Sebastian Malton --- .../+user-management/+cluster-role-bindings/details.scss | 4 ++-- .../+user-management/+cluster-role-bindings/details.tsx | 2 +- .../+user-management/+cluster-roles/add-dialog.scss | 4 ++-- .../+user-management/+cluster-roles/add-dialog.tsx | 6 +++--- .../components/+user-management/+cluster-roles/details.scss | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/details.scss b/src/renderer/components/+user-management/+cluster-role-bindings/details.scss index 1efb4412a2..1afe80f236 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/details.scss +++ b/src/renderer/components/+user-management/+cluster-role-bindings/details.scss @@ -19,5 +19,5 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.RoleBindingDetails { -} \ No newline at end of file +.ClusterRoleBindingDetails { +} diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx index 08087324cc..7dea8c083e 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/details.tsx @@ -81,7 +81,7 @@ export class ClusterRoleBindingDetails extends React.Component { const subjects = clusterRoleBinding.getSubjects(); return ( -
+
diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss b/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss index 4b94e11c4b..cc0e3fcdd6 100644 --- a/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog.scss @@ -19,8 +19,8 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.AddRoleDialog { +.AddClusterRoleDialog { .AceEditor { min-height: 200px; } -} \ No newline at end of file +} diff --git a/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx b/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx index 4006334903..f6866355b3 100644 --- a/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx +++ b/src/renderer/components/+user-management/+cluster-roles/add-dialog.tsx @@ -76,12 +76,12 @@ export class AddClusterRoleDialog extends React.Component { return ( - Create ClusterRole} + Create ClusterRole} done={AddClusterRoleDialog.close} > Date: Tue, 20 Jul 2021 09:57:51 +0300 Subject: [PATCH 080/131] Bump @types/tar from 4.0.4 to 4.0.5 (#3443) Bumps [@types/tar](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/tar) from 4.0.4 to 4.0.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/tar) --- updated-dependencies: - dependency-name: "@types/tar" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e7ff4d9abc..c1ff6091d2 100644 --- a/package.json +++ b/package.json @@ -299,7 +299,7 @@ "@types/sharp": "^0.28.3", "@types/shelljs": "^0.8.8", "@types/spdy": "^3.4.4", - "@types/tar": "^4.0.4", + "@types/tar": "^4.0.5", "@types/tcp-port-used": "^1.0.0", "@types/tempy": "^0.3.0", "@types/url-parse": "^1.4.3", diff --git a/yarn.lock b/yarn.lock index 4ac75156c9..8f10484d9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2004,10 +2004,10 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== -"@types/tar@^4.0.3", "@types/tar@^4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.4.tgz#d680de60855e7778a51c672b755869a3b8d2889f" - integrity sha512-0Xv+xcmkTsOZdIF4yCnd7RkOOyfyqPaqJ7RZFKnwdxfDbkN3eAAE9sHl8zJFqBz4VhxolW9EErbjR1oyH7jK2A== +"@types/tar@^4.0.3", "@types/tar@^4.0.5": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.5.tgz#5f953f183e36a15c6ce3f336568f6051b7b183f3" + integrity sha512-cgwPhNEabHaZcYIy5xeMtux2EmYBitfqEceBUi2t5+ETy4dW6kswt6WX4+HqLeiiKOo42EXbGiDmVJ2x+vi37Q== dependencies: "@types/minipass" "*" "@types/node" "*" From cf3aceb465a3980e3a4af1337f9f478e2f38ff4e Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 03:19:52 -0400 Subject: [PATCH 081/131] Sort cronjob last scheduled time by number (#3430) Signed-off-by: Sebastian Malton --- src/renderer/components/+workloads-cronjobs/cronjobs.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index b1a812cc4f..b1c5e3424c 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -37,6 +37,7 @@ import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import { ConfirmDialog } from "../confirm-dialog/confirm-dialog"; import { Notifications } from "../notifications/notifications"; import type { CronJobsRouteParams } from "../../../common/routes"; +import moment from "moment"; enum columnId { name = "name", @@ -65,7 +66,11 @@ export class CronJobs extends React.Component { [columnId.namespace]: (cronJob: CronJob) => cronJob.getNs(), [columnId.suspend]: (cronJob: CronJob) => cronJob.getSuspendFlag(), [columnId.active]: (cronJob: CronJob) => cronJobStore.getActiveJobsNum(cronJob), - [columnId.lastSchedule]: (cronJob: CronJob) => cronJob.getLastScheduleTime(), + [columnId.lastSchedule]: (cronJob: CronJob) => ( + cronJob.status?.lastScheduleTime + ? moment().diff(cronJob.status.lastScheduleTime) + : 0 + ), [columnId.age]: (cronJob: CronJob) => cronJob.getTimeDiffFromNow(), }} searchFilters={[ From bf4e0397431fbbd052c901b0c594a26e3b190315 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 10:31:46 +0300 Subject: [PATCH 082/131] Bump ts-jest from 26.3.0 to 26.5.6 (#3449) Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 26.3.0 to 26.5.6. - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/master/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v26.3.0...v26.5.6) --- updated-dependencies: - dependency-name: ts-jest dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 142 +++++++++------------------------------------------ 2 files changed, 26 insertions(+), 118 deletions(-) diff --git a/package.json b/package.json index c1ff6091d2..f4d7d8a699 100644 --- a/package.json +++ b/package.json @@ -366,7 +366,7 @@ "spectron": "11.0.0", "style-loader": "^2.0.0", "tailwindcss": "^2.1.2", - "ts-jest": "26.3.0", + "ts-jest": "26.5.6", "ts-loader": "^7.0.5", "ts-node": "^8.10.2", "type-fest": "^1.0.2", diff --git a/yarn.lock b/yarn.lock index 8f10484d9a..ec84b175ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -766,27 +766,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" - integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^1.1.1" - "@types/yargs" "^15.0.0" - chalk "^3.0.0" - -"@jest/types@^26.3.0": - version "26.3.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71" - integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^26.6.1": version "26.6.1" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.1.tgz#2638890e8031c0bc8b4681e0357ed986e2f866c5" @@ -1565,14 +1544,6 @@ dependencies: "@types/istanbul-lib-coverage" "*" -"@types/istanbul-reports@^1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2" - integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw== - dependencies: - "@types/istanbul-lib-coverage" "*" - "@types/istanbul-lib-report" "*" - "@types/istanbul-reports@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821" @@ -1588,14 +1559,6 @@ jest-diff "^26.0.0" pretty-format "^26.0.0" -"@types/jest@26.x": - version "26.0.13" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.13.tgz#5a7b9d5312f5dd521a38329c38ee9d3802a0b85e" - integrity sha512-sCzjKow4z9LILc6DhBvn5AkIfmQzDZkgtVVKmGwVrs5tuid38ws281D4l+7x1kP487+FlKDh5kfMZ8WSPAdmdA== - dependencies: - jest-diff "^25.2.1" - pretty-format "^25.2.1" - "@types/jest@^26.0.22": version "26.0.22" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6" @@ -5039,11 +5002,6 @@ didyoumean@^1.2.1: resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8= -diff-sequences@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" - integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== - diff-sequences@^26.5.0: version "26.5.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.5.0.tgz#ef766cf09d43ed40406611f11c6d8d9dd8b2fefd" @@ -8411,16 +8369,6 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" -jest-diff@^25.2.1: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" - integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== - dependencies: - chalk "^3.0.0" - diff-sequences "^25.2.6" - jest-get-type "^25.2.6" - pretty-format "^25.5.0" - jest-diff@^26.0.0: version "26.6.1" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.1.tgz#38aa194979f454619bb39bdee299fb64ede5300c" @@ -8492,11 +8440,6 @@ jest-fetch-mock@^3.0.3: cross-fetch "^3.0.4" promise-polyfill "^8.1.3" -jest-get-type@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" - integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== - jest-get-type@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" @@ -8717,19 +8660,7 @@ jest-snapshot@^26.6.2: pretty-format "^26.6.2" semver "^7.3.2" -jest-util@26.x: - version "26.3.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" - integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw== - dependencies: - "@jest/types" "^26.3.0" - "@types/node" "*" - chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - micromatch "^4.0.2" - -jest-util@^26.6.2: +jest-util@^26.1.0, jest-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== @@ -8947,10 +8878,10 @@ json3@^3.3.2: resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== -json5@2.x, json5@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" - integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== +json5@2.x, json5@^2.1.2, json5@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" + integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== dependencies: minimist "^1.2.5" @@ -8966,13 +8897,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -9557,11 +9481,6 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.memoize@4.x: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" @@ -9592,7 +9511,7 @@ lodash.without@~4.4.0: resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" integrity sha1-PNRXSgC2e643OpS3SHcmQFB7eqw= -lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10: +lodash@4.x, lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0, lodash@~4.17.10: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -11849,16 +11768,6 @@ pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^25.2.1, pretty-format@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" - integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== - dependencies: - "@jest/types" "^25.5.0" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - pretty-format@^26.0.0, pretty-format@^26.6.1: version "26.6.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.1.tgz#af9a2f63493a856acddeeb11ba6bcf61989660a8" @@ -12226,7 +12135,7 @@ react-input-autosize@^2.2.2: dependencies: prop-types "^15.5.8" -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -14508,22 +14417,21 @@ ts-essentials@^4.0.0: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-4.0.0.tgz#506c42b270bbd0465574b90416533175b09205ab" integrity sha512-uQJX+SRY9mtbKU+g9kl5Fi7AEMofPCvHfJkQlaygpPmHPZrtgaBqbWFOYyiA47RhnSwwnXdepUJrgqUYxoUyhQ== -ts-jest@26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.3.0.tgz#6b2845045347dce394f069bb59358253bc1338a9" - integrity sha512-Jq2uKfx6bPd9+JDpZNMBJMdMQUC3sJ08acISj8NXlVgR2d5OqslEHOR2KHMgwymu8h50+lKIm0m0xj/ioYdW2Q== +ts-jest@26.5.6: + version "26.5.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" + integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== dependencies: - "@types/jest" "26.x" bs-logger "0.x" buffer-from "1.x" fast-json-stable-stringify "2.x" - jest-util "26.x" + jest-util "^26.1.0" json5 "2.x" - lodash.memoize "4.x" + lodash "4.x" make-error "1.x" mkdirp "1.x" semver "7.x" - yargs-parser "18.x" + yargs-parser "20.x" ts-loader@^7.0.5: version "7.0.5" @@ -15697,13 +15605,10 @@ yaml@^1.10.2, yaml@^1.7.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yargs-parser@18.x, yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@20.x, yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^13.1.0, yargs-parser@^13.1.2: version "13.1.2" @@ -15721,10 +15626,13 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" - integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" yargs-parser@^7.0.0: version "7.0.0" From 686ae32dcb7b75b9f096101486aff3d8cb195ce0 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 03:32:15 -0400 Subject: [PATCH 083/131] When viewing a specific release, select its namespace (#3440) Signed-off-by: Sebastian Malton --- src/renderer/components/+apps-releases/release.store.ts | 4 ++++ src/renderer/components/+apps-releases/releases.tsx | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/renderer/components/+apps-releases/release.store.ts b/src/renderer/components/+apps-releases/release.store.ts index 9b450a7465..9d704566e7 100644 --- a/src/renderer/components/+apps-releases/release.store.ts +++ b/src/renderer/components/+apps-releases/release.store.ts @@ -55,12 +55,16 @@ export class ReleaseStore extends ItemStore { this.loadFromContextNamespaces(); } this.releaseSecrets.replace(newSecrets); + }, { + fireImmediately: true, }); } watchSelectedNamespaces(): (() => void) { return reaction(() => namespaceStore.context.contextNamespaces, namespaces => { this.loadAll(namespaces); + }, { + fireImmediately: true, }); } diff --git a/src/renderer/components/+apps-releases/releases.tsx b/src/renderer/components/+apps-releases/releases.tsx index 453983e8d0..c39807d73e 100644 --- a/src/renderer/components/+apps-releases/releases.tsx +++ b/src/renderer/components/+apps-releases/releases.tsx @@ -36,6 +36,7 @@ import { secretsStore } from "../+config-secrets/secrets.store"; import { NamespaceSelectFilter } from "../+namespaces/namespace-select-filter"; import type { ReleaseRouteParams } from "../../../common/routes"; import { releaseURL } from "../../../common/routes"; +import { namespaceStore } from "../+namespaces/namespace.store"; enum columnId { name = "name", @@ -54,6 +55,12 @@ interface Props extends RouteComponentProps { @observer export class HelmReleases extends Component { componentDidMount() { + const { match: { params: { namespace } } } = this.props; + + if (namespace) { + namespaceStore.selectNamespaces(namespace); + } + disposeOnUnmount(this, [ releaseStore.watchAssociatedSecrets(), releaseStore.watchSelectedNamespaces(), From 10ab882fedb95e0a79a3aac85417a3d7e2ac90cd Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Tue, 20 Jul 2021 12:23:45 +0300 Subject: [PATCH 084/131] Replace ... to smaller … HTML character (#3450) Signed-off-by: Alex Andreev --- src/renderer/components/cluster-manager/cluster-status.scss | 6 +++++- src/renderer/components/cluster-manager/cluster-status.tsx | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/cluster-manager/cluster-status.scss b/src/renderer/components/cluster-manager/cluster-status.scss index 86527eb75f..77d6e7693f 100644 --- a/src/renderer/components/cluster-manager/cluster-status.scss +++ b/src/renderer/components/cluster-manager/cluster-status.scss @@ -33,10 +33,14 @@ max-width: 70vw; max-height: 40vh; white-space: pre-line; + + p { + margin-bottom: $margin; + } } .Spinner { - --spinner-size: 48px; + --spinner-size: 38px; --spinner-border: calc(var(--spinner-size) / 10); } diff --git a/src/renderer/components/cluster-manager/cluster-status.tsx b/src/renderer/components/cluster-manager/cluster-status.tsx index 30065e2dd0..366a809b59 100644 --- a/src/renderer/components/cluster-manager/cluster-status.tsx +++ b/src/renderer/components/cluster-manager/cluster-status.tsx @@ -102,7 +102,7 @@ export class ClusterStatus extends React.Component { <>
-            

{this.isReconnecting ? "Reconnecting..." : "Connecting..."}

+

{this.isReconnecting ? "Reconnecting" : "Connecting"}…

{authOutput.map(({ data, error }, index) => { return

{data}

; })} From 2ea589a5998123b4a4705995116a958ebd8ea433 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Tue, 20 Jul 2021 15:01:58 +0300 Subject: [PATCH 085/131] Removing depenendency @types/shelljs (#3448) Signed-off-by: Alex Andreev --- package.json | 1 - yarn.lock | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/package.json b/package.json index f4d7d8a699..e722ef45ab 100644 --- a/package.json +++ b/package.json @@ -297,7 +297,6 @@ "@types/request-promise-native": "^1.0.17", "@types/semver": "^7.2.0", "@types/sharp": "^0.28.3", - "@types/shelljs": "^0.8.8", "@types/spdy": "^3.4.4", "@types/tar": "^4.0.5", "@types/tcp-port-used": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index ec84b175ab..74d0e68e39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1417,14 +1417,6 @@ dependencies: "@types/node" "*" -"@types/glob@*": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.2.tgz#06ca26521353a545d94a0adc74f38a59d232c987" - integrity sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - "@types/glob@^7.1.1": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" @@ -1923,14 +1915,6 @@ dependencies: "@types/node" "*" -"@types/shelljs@^0.8.8": - version "0.8.8" - resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.8.tgz#e439c69929b88a2c8123c1a55e09eb708315addf" - integrity sha512-lD3LWdg6j8r0VRBFahJVaxoW0SIcswxKaFUrmKl33RJVeeoNYQAz4uqCJ5Z6v4oIBOsC5GozX+I5SorIKiTcQA== - dependencies: - "@types/glob" "*" - "@types/node" "*" - "@types/shot@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/shot/-/shot-4.0.0.tgz#7545500c489b65c69b5bc5446ba4fef3bd26af92" From 14b70a6c539c6508050eadd1441c04c609184448 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 08:39:44 -0400 Subject: [PATCH 086/131] Cleanup (#3338) --- src/common/__tests__/event-emitter.test.ts | 102 +++++++++++++++++++++ src/common/event-emitter.ts | 26 +++--- 2 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 src/common/__tests__/event-emitter.test.ts diff --git a/src/common/__tests__/event-emitter.test.ts b/src/common/__tests__/event-emitter.test.ts new file mode 100644 index 0000000000..b54df2af98 --- /dev/null +++ b/src/common/__tests__/event-emitter.test.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { EventEmitter } from "../event-emitter"; + +describe("EventEmitter", () => { + it("should stop early if a listener returns false", () => { + let called = false; + const e = new EventEmitter<[]>(); + + e.addListener(() => false, {}); + e.addListener(() => { called = true; }, {}); + e.emit(); + + expect(called).toBe(false); + }); + + it("shouldn't stop early if a listener returns 0", () => { + let called = false; + const e = new EventEmitter<[]>(); + + e.addListener(() => 0 as any, {}); + e.addListener(() => { called = true; }, {}); + e.emit(); + + expect(called).toBe(true); + }); + + it("prepended listeners should be called before others", () => { + const callOrder: number[] = []; + const e = new EventEmitter<[]>(); + + e.addListener(() => { callOrder.push(1); }, {}); + e.addListener(() => { callOrder.push(2); }, {}); + e.addListener(() => { callOrder.push(3); }, { prepend: true }); + e.emit(); + + expect(callOrder).toStrictEqual([3, 1, 2]); + }); + + it("once listeners should be called only once", () => { + const callOrder: number[] = []; + const e = new EventEmitter<[]>(); + + e.addListener(() => { callOrder.push(1); }, {}); + e.addListener(() => { callOrder.push(2); }, {}); + e.addListener(() => { callOrder.push(3); }, { once: true }); + e.emit(); + e.emit(); + + expect(callOrder).toStrictEqual([1, 2, 3, 1, 2]); + }); + + it("removeListener should stop the listener from being called", () => { + const callOrder: number[] = []; + const e = new EventEmitter<[]>(); + const r = () => { callOrder.push(3); }; + + e.addListener(() => { callOrder.push(1); }, {}); + e.addListener(() => { callOrder.push(2); }, {}); + e.addListener(r); + + e.emit(); + e.removeListener(r); + e.emit(); + + expect(callOrder).toStrictEqual([1, 2, 3, 1, 2]); + }); + + it("removeAllListeners should stop the all listeners from being called", () => { + const callOrder: number[] = []; + const e = new EventEmitter<[]>(); + + e.addListener(() => { callOrder.push(1); }); + e.addListener(() => { callOrder.push(2); }); + e.addListener(() => { callOrder.push(3); }); + + e.emit(); + e.removeAllListeners(); + e.emit(); + + expect(callOrder).toStrictEqual([1, 2, 3]); + }); +}); diff --git a/src/common/event-emitter.ts b/src/common/event-emitter.ts index 5ff785d431..4e1fb45151 100644 --- a/src/common/event-emitter.ts +++ b/src/common/event-emitter.ts @@ -29,35 +29,31 @@ interface Options { type Callback = (...data: D) => void | boolean; export class EventEmitter { - protected listeners = new Map, Options>(); + protected listeners: [Callback, Options][] = []; addListener(callback: Callback, options: Options = {}) { - if (options.prepend) { - const listeners = [...this.listeners]; + const fn = options.prepend ? "unshift" : "push"; - listeners.unshift([callback, options]); - this.listeners = new Map(listeners); - } - else { - this.listeners.set(callback, options); - } + this.listeners[fn]([callback, options]); } removeListener(callback: Callback) { - this.listeners.delete(callback); + this.listeners = this.listeners.filter(([cb]) => cb !== callback); } removeAllListeners() { - this.listeners.clear(); + this.listeners.length = 0; } emit(...data: D) { - [...this.listeners].every(([callback, options]) => { - if (options.once) { + for (const [callback, { once }] of this.listeners) { + if (once) { this.removeListener(callback); } - return callback(...data) !== false; - }); + if (callback(...data) === false) { + break; + } + } } } From a4f252864b51bd76555cd4c9e746250cdcfbab77 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 09:30:02 -0400 Subject: [PATCH 087/131] Add ability to trim the value from (#3333) - If set (default = false) then the input will be trimmed of whitespace before validation and before submit - Use trim on all input's that use the systemName validator Signed-off-by: Sebastian Malton --- .../add-quota-dialog.tsx | 1 + .../+config-secrets/add-secret-dialog.tsx | 1 + .../+namespaces/add-namespace-dialog.tsx | 1 + .../+preferences/add-helm-repo-dialog.tsx | 1 + .../+service-accounts/create-dialog.tsx | 4 +++- .../cronjob-trigger-dialog.tsx | 1 + src/renderer/components/input/input.tsx | 24 ++++++++++--------- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx b/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx index 17dc3e5eae..b341d2c86a 100644 --- a/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx +++ b/src/renderer/components/+config-resource-quotas/add-quota-dialog.tsx @@ -175,6 +175,7 @@ export class AddQuotaDialog extends React.Component { this.quotaName = v.toLowerCase()} className="box grow" diff --git a/src/renderer/components/+config-secrets/add-secret-dialog.tsx b/src/renderer/components/+config-secrets/add-secret-dialog.tsx index bfd7a3262e..23adfaf804 100644 --- a/src/renderer/components/+config-secrets/add-secret-dialog.tsx +++ b/src/renderer/components/+config-secrets/add-secret-dialog.tsx @@ -223,6 +223,7 @@ export class AddSecretDialog extends React.Component { this.name = v} /> diff --git a/src/renderer/components/+namespaces/add-namespace-dialog.tsx b/src/renderer/components/+namespaces/add-namespace-dialog.tsx index 213891c233..2427c9fd9b 100644 --- a/src/renderer/components/+namespaces/add-namespace-dialog.tsx +++ b/src/renderer/components/+namespaces/add-namespace-dialog.tsx @@ -100,6 +100,7 @@ export class AddNamespaceDialog extends React.Component { required autoFocus iconLeft="layers" placeholder="Namespace" + trim validators={systemName} value={namespace} onChange={v => this.namespace = v.toLowerCase()} /> diff --git a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx index 44ef32b9af..36388088e1 100644 --- a/src/renderer/components/+preferences/add-helm-repo-dialog.tsx +++ b/src/renderer/components/+preferences/add-helm-repo-dialog.tsx @@ -177,6 +177,7 @@ export class AddHelmRepoDialog extends React.Component { this.helmRepo.name = v} /> diff --git a/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx b/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx index e812068506..1f50e717c6 100644 --- a/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx +++ b/src/renderer/components/+user-management/+service-accounts/create-dialog.tsx @@ -88,8 +88,10 @@ export class CreateServiceAccountDialog extends React.Component { this.name = v.toLowerCase()} /> diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx index f4006bd89f..0e2f98d16a 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx @@ -123,6 +123,7 @@ export class CronJobTriggerDialog extends Component { this.jobName = v.toLowerCase()} diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index d182a8c202..91b58c24b5 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -41,11 +41,13 @@ export type { InputValidator }; type InputElement = HTMLInputElement | HTMLTextAreaElement; type InputElementProps = InputHTMLAttributes & TextareaHTMLAttributes & DOMAttributes; -export type InputProps = Omit & { +export type InputProps = Omit & { theme?: "round-black" | "round"; className?: string; - value?: T; - autoSelectOnFocus?: boolean + value?: string; + trim?: boolean; + autoSelectOnFocus?: boolean; + defaultValue?: string; multiLine?: boolean; // use text-area as input field maxRows?: number; // when multiLine={true} define max rows size dirty?: boolean; // show validation errors even if the field wasn't touched yet @@ -55,8 +57,8 @@ export type InputProps = Omit): void; - onSubmit?(value: T, evt: React.KeyboardEvent): void; + onChange?(value: string, evt: React.ChangeEvent): void; + onSubmit?(value: string, evt: React.KeyboardEvent): void; }; interface State { @@ -73,6 +75,7 @@ const defaultProps: Partial = { maxRows: 10000, showValidationLine: true, validators: [], + defaultValue: "", }; export class Input extends React.Component { @@ -102,12 +105,10 @@ export class Input extends React.Component { } getValue(): string { - const { value, defaultValue = "" } = this.props; + const { trim, value, defaultValue } = this.props; + const rawValue = value ?? this.input?.value ?? defaultValue; - if (value !== undefined) return value; // controlled input - if (this.input) return this.input.value; // uncontrolled input - - return defaultValue as string; + return trim ? rawValue.trim() : rawValue; } focus() { @@ -138,7 +139,8 @@ export class Input extends React.Component { private validationId: string; - async validate(value = this.getValue()) { + async validate() { + const value = this.getValue(); let validationId = (this.validationId = ""); // reset every time for async validators const asyncValidators: Promise[] = []; const errors: React.ReactNode[] = []; From f03cc59688e6aaa7147334f9d076ab643bd410f6 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 09:32:37 -0400 Subject: [PATCH 088/131] Wait for a file to be stable before computing diff (#3427) Signed-off-by: Sebastian Malton --- src/main/catalog-sources/kubeconfig-sync.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/catalog-sources/kubeconfig-sync.ts b/src/main/catalog-sources/kubeconfig-sync.ts index 6a6f4120f9..e60d9400de 100644 --- a/src/main/catalog-sources/kubeconfig-sync.ts +++ b/src/main/catalog-sources/kubeconfig-sync.ts @@ -264,6 +264,12 @@ async function watchFileChanges(filePath: string): Promise<[IComputedValue>(); const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1])))); From aeae2dcf984b2dc81cdbe638a3fabf99c05ff63a Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Tue, 20 Jul 2021 19:19:39 +0300 Subject: [PATCH 089/131] Enable codeql analysis (#3455) --- .github/workflows/codeql-analysis.yml | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000..fc2692be70 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '41 3 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 27f2bd7181cfca05d3010a7d189a89345c7e9bf9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 20 Jul 2021 15:25:18 -0400 Subject: [PATCH 090/131] Wait for shell to bring up prompt before sending commands (#3337) --- src/renderer/api/terminal-api.ts | 65 ++++++++++++------- .../components/dock/terminal.store.ts | 35 +++++++--- 2 files changed, 70 insertions(+), 30 deletions(-) diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 3b2c8a1cd9..347dfac224 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -19,11 +19,13 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { stringify } from "querystring"; import { boundMethod, base64, EventEmitter } from "../utils"; import { WebSocketApi } from "./websocket-api"; import isEqual from "lodash/isEqual"; import { isDevelopment } from "../../common/vars"; +import url from "url"; +import { makeObservable, observable } from "mobx"; +import type { ParsedUrlQueryInput } from "querystring"; export enum TerminalChannels { STDIN = 0, @@ -55,7 +57,9 @@ export class TerminalApi extends WebSocketApi { protected size: { Width: number; Height: number }; public onReady = new EventEmitter<[]>(); - public isReady = false; + @observable public isReady = false; + @observable public shellRunCommandsFinished = false; + public readonly url: string; constructor(protected options: TerminalApiQuery) { super({ @@ -63,34 +67,33 @@ export class TerminalApi extends WebSocketApi { flushOnOpen: false, pingIntervalSeconds: 30, }); - } + makeObservable(this); - async getUrl() { - let { port } = location; - const { hostname, protocol } = location; - const { id, node } = this.options; - const wss = `ws${protocol === "https:" ? "s" : ""}://`; - const query: TerminalApiQuery = { id }; + const { hostname, protocol, port } = location; + const query: ParsedUrlQueryInput = { + id: options.id, + }; - if (port) { - port = `:${port}`; + if (options.node) { + query.node = options.node; + query.type = options.type || "node"; } - if (node) { - query.node = node; - query.type = "node"; - } - - return `${wss}${hostname}${port}/api?${stringify(query)}`; + this.url = url.format({ + protocol: protocol.includes("https") ? "wss" : "ws", + hostname, + port, + pathname: "/api", + query, + slashes: true, + }); } - async connect() { - const apiUrl = await this.getUrl(); - + connect() { this.emitStatus("Connecting ..."); this.onData.addListener(this._onReady, { prepend: true }); - - return super.connect(apiUrl); + this.onData.addListener(this._onShellRunCommandsFinished); + super.connect(this.url); } destroy() { @@ -106,6 +109,24 @@ export class TerminalApi extends WebSocketApi { this.onReady.removeAllListeners(); } + _onShellRunCommandsFinished = (data: string) => { + if (!data) { + return; + } + + /** + * This is a heuistic for ditermining when a shell has finished executing + * its own rc file (or RunCommands file) such as `.bashrc` or `.zshrc`. + * + * This heuistic assumes that the prompt line of a terminal is a single line + * and ends with a whitespace character. + */ + if (data.match(/\r?\n/) === null && data.match(/\s$/)) { + this.shellRunCommandsFinished = true; + this.onData.removeListener(this._onShellRunCommandsFinished); + } + }; + @boundMethod protected _onReady(data: string) { if (!data) return true; diff --git a/src/renderer/components/dock/terminal.store.ts b/src/renderer/components/dock/terminal.store.ts index fb3359368d..dd5251bac2 100644 --- a/src/renderer/components/dock/terminal.store.ts +++ b/src/renderer/components/dock/terminal.store.ts @@ -19,12 +19,13 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { autorun, observable } from "mobx"; -import { autoBind, Singleton } from "../../utils"; +import { autorun, observable, when } from "mobx"; +import { autoBind, noop, Singleton } from "../../utils"; import { Terminal } from "./terminal"; import { TerminalApi } from "../../api/terminal-api"; import { dockStore, DockTab, DockTabCreateSpecific, TabId, TabKind } from "./dock.store"; import { WebSocketApiState } from "../../api/websocket-api"; +import { Notifications } from "../notifications"; export interface ITerminalTab extends DockTab { node?: string; // activate node shell mode @@ -64,7 +65,7 @@ export class TerminalStore extends Singleton { }); } - async connect(tabId: TabId) { + connect(tabId: TabId) { if (this.isConnected(tabId)) { return; } @@ -104,18 +105,36 @@ export class TerminalStore extends Singleton { return this.connections.get(tabId)?.readyState === WebSocketApiState.CLOSED; } - sendCommand(command: string, options: { enter?: boolean; newTab?: boolean; tabId?: TabId } = {}) { + async sendCommand(command: string, options: { enter?: boolean; newTab?: boolean; tabId?: TabId } = {}) { const { enter, newTab, tabId } = options; - const { selectTab, getTabById } = dockStore; - const tab = tabId && getTabById(tabId); - if (tab) selectTab(tabId); - if (newTab) createTerminalTab(); + if (tabId) { + dockStore.selectTab(tabId); + } + + if (newTab) { + const tab = createTerminalTab(); + + await when(() => this.connections.has(tab.id)); + + const rcIsFinished = when(() => this.connections.get(tab.id).shellRunCommandsFinished); + const notifyVeryLong = setTimeout(() => { + rcIsFinished.cancel(); + Notifications.info("Terminal shell is taking a long time to complete startup. Please check your .rc file. Bypassing shell completion check.", { + timeout: 4_000, + }); + }, 10_000); + + await rcIsFinished.catch(noop); + clearTimeout(notifyVeryLong); + } const terminalApi = this.connections.get(dockStore.selectedTabId); if (terminalApi) { terminalApi.sendCommand(command + (enter ? "\r" : "")); + } else { + console.warn("The selected tab is does not have a connection. Cannot send command.", { tabId: dockStore.selectedTabId, command }); } } From 66820a20fcb7f691670b98b1dbaa2e949ecdf4e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 15:35:55 -0400 Subject: [PATCH 091/131] Bump @types/progress-bar-webpack-plugin from 2.1.0 to 2.1.2 (#3342) Bumps [@types/progress-bar-webpack-plugin](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/progress-bar-webpack-plugin) from 2.1.0 to 2.1.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/progress-bar-webpack-plugin) --- updated-dependencies: - dependency-name: "@types/progress-bar-webpack-plugin" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 40 ++++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index e722ef45ab..d7b29bea77 100644 --- a/package.json +++ b/package.json @@ -281,7 +281,7 @@ "@types/module-alias": "^2.0.0", "@types/node": "12.20", "@types/npm": "^2.0.31", - "@types/progress-bar-webpack-plugin": "^2.1.0", + "@types/progress-bar-webpack-plugin": "^2.1.2", "@types/proper-lockfile": "^4.1.1", "@types/randomcolor": "^0.5.5", "@types/react": "^17.0.0", diff --git a/yarn.lock b/yarn.lock index 74d0e68e39..15ed4d41d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1199,11 +1199,6 @@ "@babel/runtime" "^7.12.5" "@testing-library/dom" "^7.28.1" -"@types/anymatch@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" - integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== - "@types/aria-query@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" @@ -1718,13 +1713,13 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.1.tgz#b6e98083f13faa1e5231bfa3bdb1b0feff536b6d" integrity sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ== -"@types/progress-bar-webpack-plugin@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/progress-bar-webpack-plugin/-/progress-bar-webpack-plugin-2.1.0.tgz#f95b7e1199b5f5e8156321d2411f6fb06c833559" - integrity sha512-HCyeEuuFzsXvIkbchGKJUhXRBGio7BlvVn0ULuBRE50UofuFy2Q8HvR+Q8C2w+QlBUoM+AjerGJU8DhyqYVw7g== +"@types/progress-bar-webpack-plugin@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@types/progress-bar-webpack-plugin/-/progress-bar-webpack-plugin-2.1.2.tgz#1097946f06efcc087fb2563b153ee61240d9e1dd" + integrity sha512-zSmm7M96PAwwD/YZp7ML9KOtfaRRC2HcU8mrddySWLlSpIGQPynVoc7V97lS0U4tIxlXl02FDQbLSzZq52BEAg== dependencies: "@types/progress" "*" - "@types/webpack" "*" + "@types/webpack" "^4" "@types/progress@*": version "2.0.3" @@ -1951,6 +1946,11 @@ resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== +"@types/tapable@^1": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" + integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== + "@types/tar@^4.0.3", "@types/tar@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tar/-/tar-4.0.5.tgz#5f953f183e36a15c6ce3f336568f6051b7b183f3" @@ -2054,16 +2054,16 @@ "@types/source-list-map" "*" source-map "^0.7.3" -"@types/webpack@*", "@types/webpack@^4.41.17", "@types/webpack@^4.41.8": - version "4.41.17" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.17.tgz#0a69005e644d657c85b7d6ec1c826a71bebd1c93" - integrity sha512-6FfeCidTSHozwKI67gIVQQ5Mp0g4X96c2IXxX75hYEQJwST/i6NyZexP//zzMOBb+wG9jJ7oO8fk9yObP2HWAw== +"@types/webpack@*", "@types/webpack@^4", "@types/webpack@^4.41.17", "@types/webpack@^4.41.8": + version "4.41.30" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.30.tgz#fd3db6d0d41e145a8eeeafcd3c4a7ccde9068ddc" + integrity sha512-GUHyY+pfuQ6haAfzu4S14F+R5iGRwN6b2FRNJY7U0NilmFAqbsOfK6j1HwuLBAqwRIT+pVdNDJGJ6e8rpp0KHA== dependencies: - "@types/anymatch" "*" "@types/node" "*" - "@types/tapable" "*" + "@types/tapable" "^1" "@types/uglify-js" "*" "@types/webpack-sources" "*" + anymatch "^3.0.0" source-map "^0.6.0" "@types/ws@^6.0.1": @@ -2606,6 +2606,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + anymatch@^3.0.3, anymatch@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" From b7a24f452032d14f9c40cb9d8e2f2c0d1e454b6f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 21 Jul 2021 00:19:23 -0400 Subject: [PATCH 092/131] Default to empty array for 500Beta13 migration (#3461) Signed-off-by: Sebastian Malton --- src/migrations/cluster-store/5.0.0-beta.13.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/migrations/cluster-store/5.0.0-beta.13.ts b/src/migrations/cluster-store/5.0.0-beta.13.ts index 25e0cd1543..e6f260db1b 100644 --- a/src/migrations/cluster-store/5.0.0-beta.13.ts +++ b/src/migrations/cluster-store/5.0.0-beta.13.ts @@ -108,7 +108,7 @@ export default { run(store) { const folder = path.resolve(app.getPath("userData"), "lens-local-storage"); - const oldClusters: ClusterModel[] = store.get("clusters"); + const oldClusters: ClusterModel[] = store.get("clusters") ?? []; const clusters = new Map(); for (const { id: oldId, ...cluster } of oldClusters) { From fe33296ba5faa45b7cdbd8edcd09d81c8a0e702d Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 21 Jul 2021 00:19:46 -0400 Subject: [PATCH 093/131] Bundled extensions are always compatible in dev mode (#3459) Signed-off-by: Sebastian Malton --- src/extensions/extension-compatibility.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extensions/extension-compatibility.ts b/src/extensions/extension-compatibility.ts index cc5db5515e..3d958072dc 100644 --- a/src/extensions/extension-compatibility.ts +++ b/src/extensions/extension-compatibility.ts @@ -20,7 +20,7 @@ */ import semver from "semver"; -import { appSemVer } from "../common/vars"; +import { appSemVer, isProduction } from "../common/vars"; import type { LensExtensionManifest } from "./lens-extension"; export function isCompatibleExtension(manifest: LensExtensionManifest): boolean { @@ -33,5 +33,5 @@ export function isCompatibleExtension(manifest: LensExtensionManifest): boolean } export function isCompatibleBundledExtension(manifest: LensExtensionManifest): boolean { - return manifest.version === appSemVer.raw; + return !isProduction || manifest.version === appSemVer.raw; } From d52c4c39992b2ca4f909886b21bcfe8f145679e9 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 21 Jul 2021 08:08:39 -0400 Subject: [PATCH 094/131] Fix onRun not executing everywhere :hover is defined for HotbarIcon (#3316) Signed-off-by: Sebastian Malton --- .../components/hotbar/hotbar-icon.tsx | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index b8447baf80..176abcc996 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -74,32 +74,28 @@ export const HotbarIcon = observer(({menuItems = [], size = 40, tooltip, ...prop setMenuOpen(!menuOpen); }; - const renderIcon = () => { - return ( - 0 })}> + {tooltip && {tooltip}} +
{ if (!disabled) { onClick?.(event); } }} > - {material && } - - ); - }; - - return ( -
0 })}> - {tooltip && {tooltip}} -
- {renderIcon()} + + {material && } + {children}
Date: Wed, 21 Jul 2021 08:08:48 -0400 Subject: [PATCH 095/131] Bump mac-ca from 1.0.4 to 1.0.6 (#3464) Bumps [mac-ca](https://github.com/jfromaniello/mac-ca) from 1.0.4 to 1.0.6. - [Release notes](https://github.com/jfromaniello/mac-ca/releases) - [Commits](https://github.com/jfromaniello/mac-ca/compare/v1.0.4...v1.0.6) --- updated-dependencies: - dependency-name: mac-ca dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index d7b29bea77..4157712350 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "jsdom": "^16.4.0", "jsonpath": "^1.0.2", "lodash": "^4.17.15", - "mac-ca": "^1.0.4", + "mac-ca": "^1.0.6", "marked": "^2.0.3", "md5-file": "^5.0.0", "mobx": "^6.3.0", diff --git a/yarn.lock b/yarn.lock index 15ed4d41d7..22ed022d72 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9593,12 +9593,12 @@ lz-string@^1.4.4: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= -mac-ca@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mac-ca/-/mac-ca-1.0.4.tgz#4c8ae50f003dec4bc63f4688791f9321ff84e5f5" - integrity sha512-6Yf4IKnH2Irv9nm2QjoQg7DcNY+wYygYeU2EM+xWmQ0M7cwboPv9O3U2eZ378KwAIWJUiVh5BC9QzmcqmTyrPw== +mac-ca@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/mac-ca/-/mac-ca-1.0.6.tgz#89860edfeebcc4593567044281ab3500961ec15f" + integrity sha512-uuCaT+41YtIQlDDvbigP1evK1iUk97zRirP9+8rZJz8x0eIQZG8Z7YQegMTsCiMesLPb6LBgCS95uyAvVA1tmg== dependencies: - node-forge "^0.7.5" + node-forge "^0.10.0" make-dir@^1.0.0, make-dir@^1.3.0: version "1.3.0" @@ -10242,11 +10242,6 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-forge@^0.7.5: - version "0.7.6" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" - integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== - node-forge@^0.8.2: version "0.8.5" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee" From 4fe0a7d73e53c0c5f4f46538b1e3c2a8ff25e648 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Wed, 21 Jul 2021 15:51:46 +0300 Subject: [PATCH 096/131] Catalog render optimizations (#3422) --- __mocks__/windowMock.ts | 32 +++ package.json | 3 +- src/common/user-store/user-store.ts | 6 +- .../+catalog/catalog-tree.module.css | 5 + src/renderer/components/+catalog/catalog.tsx | 197 ++++++++---------- .../cluster-manager/cluster-manager.scss | 6 +- .../cluster-manager/cluster-manager.tsx | 4 - .../cluster-manager/cluster-view.tsx | 2 + src/renderer/components/input/input.scss | 1 - src/renderer/components/menu/menu-actions.tsx | 51 ++--- src/renderer/components/menu/menu.tsx | 2 + .../__tests__/render-delay.test.tsx | 38 ++++ .../components/render-delay/render-delay.tsx | 63 ++++++ types/dom.d.ts | 5 + 14 files changed, 268 insertions(+), 147 deletions(-) create mode 100644 __mocks__/windowMock.ts create mode 100644 src/renderer/components/render-delay/__tests__/render-delay.test.tsx create mode 100644 src/renderer/components/render-delay/render-delay.tsx diff --git a/__mocks__/windowMock.ts b/__mocks__/windowMock.ts new file mode 100644 index 0000000000..baf8fa1a65 --- /dev/null +++ b/__mocks__/windowMock.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +Object.defineProperty(window, "requestIdleCallback", { + writable: true, + value: jest.fn().mockImplementation(callback => callback()), +}); + +Object.defineProperty(window, "cancelIdleCallback", { + writable: true, + value: jest.fn(), +}); + +export default {}; diff --git a/package.json b/package.json index 4157712350..af393433de 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,8 @@ }, "moduleNameMapper": { "\\.(css|scss)$": "/__mocks__/styleMock.ts", - "\\.(svg)$": "/__mocks__/imageMock.ts" + "\\.(svg)$": "/__mocks__/imageMock.ts", + "src/(.*)": "/__mocks__/windowMock.ts" }, "modulePathIgnorePatterns": [ "/dist", diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index beeb2078e1..7121357671 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -137,7 +137,11 @@ export class UserStore extends BaseStore /* implements UserStore * Toggles the hidden configuration of a table's column */ toggleTableColumnVisibility(tableId: string, columnId: string) { - this.hiddenTableColumns.get(tableId)?.toggle(columnId); + if (!this.hiddenTableColumns.get(tableId)) { + this.hiddenTableColumns.set(tableId, new ObservableToggleSet()); + } + + this.hiddenTableColumns.get(tableId).toggle(columnId); } @action diff --git a/src/renderer/components/+catalog/catalog-tree.module.css b/src/renderer/components/+catalog/catalog-tree.module.css index 4787fd71b8..0b16983dbc 100644 --- a/src/renderer/components/+catalog/catalog-tree.module.css +++ b/src/renderer/components/+catalog/catalog-tree.module.css @@ -46,6 +46,11 @@ border-radius: 2px; } +.content:active { + color: white; + background-color: var(--blue); +} + .group { margin-left: 0px; } diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 76339e79a3..e70a8d5f47 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -42,6 +42,9 @@ import { CatalogEntityDetails } from "./catalog-entity-details"; import { catalogURL, CatalogViewRouteParam } from "../../../common/routes"; import { CatalogMenu } from "./catalog-menu"; import { HotbarIcon } from "../hotbar/hotbar-icon"; +import { RenderDelay } from "../render-delay/render-delay"; +import { CatalogTopbar } from "../cluster-manager/catalog-topbar"; +import type { TableSortCallback } from "../table"; export const previousActiveTab = createAppStorage("catalog-previous-active-tab", ""); @@ -144,7 +147,9 @@ export class Catalog extends React.Component { }; renderNavigation() { - return ; + return ( + + ); } renderItemMenu = (item: CatalogEntityItem) => { @@ -172,110 +177,73 @@ export class Catalog extends React.Component { renderIcon(item: CatalogEntityItem) { return ( - + + + ); } - renderSingleCategoryList() { - return ( - ) => item.name, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, - }} - searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, - ]} - renderTableHeader={[ - { title: "", className: css.iconCell, id: "icon" }, - { title: "Name", className: css.nameCell, sortBy: sortBy.name, id: "name" }, - { title: "Source", className: css.sourceCell, sortBy: sortBy.source, id: "source" }, - { title: "Labels", className: css.labelsCell, id: "labels" }, - { title: "Status", className: css.statusCell, sortBy: sortBy.status, id: "status" }, - ]} - customizeTableRowProps={(item: CatalogEntityItem) => ({ - disabled: !item.enabled, - })} - renderTableContents={(item: CatalogEntityItem) => [ - this.renderIcon(item), - item.name, - item.source, - item.getLabelBadges(), - { title: item.phase, className: cssNames(css[item.phase]) } - ]} - onDetails={this.onDetails} - renderItemMenu={this.renderItemMenu} - /> - ); - } + renderList() { + const { activeCategory } = this.catalogEntityStore; + const tableId = activeCategory ? `catalog-items-${activeCategory.metadata.name.replace(" ", "")}` : "catalog-items"; + let sortingCallbacks: { [sortBy: string]: TableSortCallback } = { + [sortBy.name]: (item: CatalogEntityItem) => item.name, + [sortBy.source]: (item: CatalogEntityItem) => item.source, + [sortBy.status]: (item: CatalogEntityItem) => item.phase, + }; - renderAllCategoriesList() { - return ( - ) => item.name, - [sortBy.kind]: (item: CatalogEntityItem) => item.kind, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, - }} - searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, - ]} - renderTableHeader={[ - { title: "", className: css.iconCell, id: "icon" }, - { title: "Name", className: css.nameCell, sortBy: sortBy.name, id: "name" }, - { title: "Kind", className: css.kindCell, sortBy: sortBy.kind, id: "kind" }, - { title: "Source", className: css.sourceCell, sortBy: sortBy.source, id: "source" }, - { title: "Labels", className: css.labelsCell, id: "labels" }, - { title: "Status", className: css.statusCell, sortBy: sortBy.status, id: "status" }, - ]} - customizeTableRowProps={(item: CatalogEntityItem) => ({ - disabled: !item.enabled, - })} - renderTableContents={(item: CatalogEntityItem) => [ - this.renderIcon(item), - item.name, - item.kind, - item.source, - item.getLabelBadges(), - { title: item.phase, className: cssNames(css[item.phase]) } - ]} - detailsItem={this.catalogEntityStore.selectedItem} - onDetails={this.onDetails} - renderItemMenu={this.renderItemMenu} - /> - ); - } + sortingCallbacks = activeCategory ? sortingCallbacks : { + ...sortingCallbacks, + [sortBy.kind]: (item: CatalogEntityItem) => item.kind, + }; - renderCategoryList() { if (this.activeTab === undefined) { return null; } - return this.catalogEntityStore.activeCategory ? this.renderSingleCategoryList() : this.renderAllCategoriesList(); + return ( + ) => entity.searchFields, + ]} + renderTableHeader={[ + { title: "", className: css.iconCell, id: "icon" }, + { title: "Name", className: css.nameCell, sortBy: sortBy.name, id: "name" }, + !activeCategory && { title: "Kind", className: css.kindCell, sortBy: sortBy.kind, id: "kind" }, + { title: "Source", className: css.sourceCell, sortBy: sortBy.source, id: "source" }, + { title: "Labels", className: css.labelsCell, id: "labels" }, + { title: "Status", className: css.statusCell, sortBy: sortBy.status, id: "status" }, + ].filter(Boolean)} + customizeTableRowProps={(item: CatalogEntityItem) => ({ + disabled: !item.enabled, + })} + renderTableContents={(item: CatalogEntityItem) => [ + this.renderIcon(item), + item.name, + !activeCategory && item.kind, + item.source, + item.getLabelBadges(), + { title: item.phase, className: cssNames(css[item.phase]) } + ].filter(Boolean)} + onDetails={this.onDetails} + renderItemMenu={this.renderItemMenu} + /> + ); } render() { @@ -284,21 +252,28 @@ export class Catalog extends React.Component { } return ( - -
- { this.renderCategoryList() } -
- { - this.catalogEntityStore.selectedItem - ? this.catalogEntityStore.selectedItemId = null} - /> - : - } -
+ <> + + +
+ { this.renderList() } +
+ { + this.catalogEntityStore.selectedItem + ? this.catalogEntityStore.selectedItemId = null} + /> + : ( + + + + ) + } +
+ ); } } diff --git a/src/renderer/components/cluster-manager/cluster-manager.scss b/src/renderer/components/cluster-manager/cluster-manager.scss index dddbb1a5e5..e82c0d3aca 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.scss +++ b/src/renderer/components/cluster-manager/cluster-manager.scss @@ -35,10 +35,6 @@ position: relative; display: flex; flex-direction: column; - - > * { - z-index: 1; - } } .HotbarMenu { @@ -52,7 +48,7 @@ #lens-views { position: absolute; left: 0; - top: 0; + top: 40px; // Move below top bar right: 0; bottom: 0; display: flex; diff --git a/src/renderer/components/cluster-manager/cluster-manager.tsx b/src/renderer/components/cluster-manager/cluster-manager.tsx index df5ae5d486..4bd14e7805 100644 --- a/src/renderer/components/cluster-manager/cluster-manager.tsx +++ b/src/renderer/components/cluster-manager/cluster-manager.tsx @@ -34,8 +34,6 @@ import { Extensions } from "../+extensions"; import { HotbarMenu } from "../hotbar/hotbar-menu"; import { EntitySettings } from "../+entity-settings"; import { Welcome } from "../+welcome"; -import { ClusterTopbar } from "./cluster-topbar"; -import { CatalogTopbar } from "./catalog-topbar"; import * as routes from "../../../common/routes"; @observer @@ -43,8 +41,6 @@ export class ClusterManager extends React.Component { render() { return (
- -
diff --git a/src/renderer/components/cluster-manager/cluster-view.tsx b/src/renderer/components/cluster-manager/cluster-view.tsx index db48e051c8..3d0fea8e55 100644 --- a/src/renderer/components/cluster-manager/cluster-view.tsx +++ b/src/renderer/components/cluster-manager/cluster-view.tsx @@ -34,6 +34,7 @@ import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; import { navigate } from "../../navigation"; import { catalogURL, ClusterViewRouteParams } from "../../../common/routes"; import { previousActiveTab } from "../+catalog"; +import { ClusterTopbar } from "./cluster-topbar"; interface Props extends RouteComponentProps { } @@ -104,6 +105,7 @@ export class ClusterView extends React.Component { render() { return (
+ {this.renderStatus()}
); diff --git a/src/renderer/components/input/input.scss b/src/renderer/components/input/input.scss index b683f46d3a..33369694a3 100644 --- a/src/renderer/components/input/input.scss +++ b/src/renderer/components/input/input.scss @@ -61,7 +61,6 @@ width: 0; height: 2px; background: $primary; - transition: width 250ms; } } diff --git a/src/renderer/components/menu/menu-actions.tsx b/src/renderer/components/menu/menu-actions.tsx index a69f226e5e..28747fec99 100644 --- a/src/renderer/components/menu/menu-actions.tsx +++ b/src/renderer/components/menu/menu-actions.tsx @@ -30,6 +30,7 @@ import { Icon, IconProps } from "../icon"; import { Menu, MenuItem, MenuProps } from "../menu"; import uniqueId from "lodash/uniqueId"; import isString from "lodash/isString"; +import { RenderDelay } from "../render-delay/render-delay"; export interface MenuActionsProps extends Partial { className?: string; @@ -124,30 +125,32 @@ export class MenuActions extends React.Component { return ( <> {this.renderTriggerIcon()} - - {children} - {updateAction && ( - - - Edit - - )} - {removeAction && ( - - - Remove - - )} - + + + {children} + {updateAction && ( + + + Edit + + )} + {removeAction && ( + + + Remove + + )} + + ); } diff --git a/src/renderer/components/menu/menu.tsx b/src/renderer/components/menu/menu.tsx index 228db7ceb4..30780e7ac4 100644 --- a/src/renderer/components/menu/menu.tsx +++ b/src/renderer/components/menu/menu.tsx @@ -260,6 +260,8 @@ export class Menu extends React.Component { } onBlur() { + if (!this.isOpen) return; // Prevents triggering document.activeElement for each instance + if (document.activeElement?.tagName == "IFRAME") { this.close(); } diff --git a/src/renderer/components/render-delay/__tests__/render-delay.test.tsx b/src/renderer/components/render-delay/__tests__/render-delay.test.tsx new file mode 100644 index 0000000000..f81003e588 --- /dev/null +++ b/src/renderer/components/render-delay/__tests__/render-delay.test.tsx @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import React from "react"; +import "@testing-library/jest-dom/extend-expect"; +import { render } from "@testing-library/react"; +import { RenderDelay } from "../render-delay"; + +describe("", () => { + it("renders w/o errors", () => { + const { container } = render(); + + expect(container).toBeInstanceOf(HTMLElement); + }); + + it("renders it's child", () => { + const { getByText } = render(); + + expect(getByText("My button")).toBeInTheDocument(); + }); +}); diff --git a/src/renderer/components/render-delay/render-delay.tsx b/src/renderer/components/render-delay/render-delay.tsx new file mode 100644 index 0000000000..b90cb9148a --- /dev/null +++ b/src/renderer/components/render-delay/render-delay.tsx @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import { makeObservable, observable } from "mobx"; +import { observer } from "mobx-react"; +import { boundMethod } from "../../utils"; + +interface Props { + placeholder?: React.ReactNode; + children: unknown; +} + +@observer +export class RenderDelay extends React.Component { + @observable isVisible = false; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + + componentDidMount() { + const guaranteedFireTime = 1000; + + window.requestIdleCallback(this.showContents, { timeout: guaranteedFireTime }); + } + + componentWillUnmount() { + window.cancelIdleCallback(this.showContents); + } + + @boundMethod + showContents() { + this.isVisible = true; + } + + render() { + if (!this.isVisible) { + return this.props.placeholder || null; + } + + return this.props.children; + } +} diff --git a/types/dom.d.ts b/types/dom.d.ts index bb829706f0..72a2488aca 100644 --- a/types/dom.d.ts +++ b/types/dom.d.ts @@ -24,4 +24,9 @@ declare global { interface Element { scrollIntoViewIfNeeded(opt_center?: boolean): void; } + + interface Window { + requestIdleCallback(callback: () => void, options: { timeout: number }); + cancelIdleCallback(callback: () => void); + } } From 42566b5bd743e788639296d038eebe47651812ce Mon Sep 17 00:00:00 2001 From: chh <1474479+chenhunghan@users.noreply.github.com> Date: Wed, 21 Jul 2021 17:08:29 +0300 Subject: [PATCH 097/131] Turn on tailwind JIT mode, and upgrade to 2.2.4 (#3297) Co-authored-by: Alex Andreev --- package.json | 2 +- tailwind.config.js | 1 + yarn.lock | 201 ++++++++++++++++++++++++++------------------- 3 files changed, 117 insertions(+), 87 deletions(-) diff --git a/package.json b/package.json index af393433de..32fc8cd60d 100644 --- a/package.json +++ b/package.json @@ -365,7 +365,7 @@ "sharp": "^0.26.1", "spectron": "11.0.0", "style-loader": "^2.0.0", - "tailwindcss": "^2.1.2", + "tailwindcss": "^2.2.4", "ts-jest": "26.5.6", "ts-loader": "^7.0.5", "ts-node": "^8.10.2", diff --git a/tailwind.config.js b/tailwind.config.js index 7c1c91de4e..040f2dce23 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -20,6 +20,7 @@ */ module.exports = { + mode: "jit", purge: ["src/**/*.tsx"], darkMode: "class", theme: { diff --git a/yarn.lock b/yarn.lock index 22ed022d72..aaef73ee8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -475,12 +475,12 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@fullhuman/postcss-purgecss@^3.1.3": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-3.1.3.tgz#47af7b87c9bfb3de4bc94a38f875b928fffdf339" - integrity sha512-kwOXw8fZ0Lt1QmeOOrd+o4Ibvp4UTEBFQbzvWldjlKv5n+G9sXfIPn1hh63IQIL8K8vbvv1oYMJiIUbuy9bGaA== +"@fullhuman/postcss-purgecss@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@fullhuman/postcss-purgecss/-/postcss-purgecss-4.0.3.tgz#55d71712ec1c7a88e0d1ba5f10ce7fb6aa05beb4" + integrity sha512-/EnQ9UDWGGqHkn1UKAwSgh+gJHPKmD+Z+5dQ4gWT4qq2NUyez3zqAfZNwFH3eSgmgO+wjTXfhlLchx2M9/K+7Q== dependencies: - purgecss "^3.1.3" + purgecss "^4.0.3" "@hapi/b64@5.x.x": version "5.0.0" @@ -2606,7 +2606,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.0: +anymatch@^3.0.0, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -2710,6 +2710,11 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +arg@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.0.tgz#a20e2bb5710e82950a516b3f933fee5ed478be90" + integrity sha512-4P8Zm2H+BRS+c/xX1LrHw0qKpEhdlZjLCgWy+d78T9vqa2Z2SiD2wMrYuWIAFy5IZUD7nnNXroRttz+0RzlrzQ== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -3694,6 +3699,14 @@ chalk@^4.0.0, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -3727,7 +3740,7 @@ chartjs-color@^2.1.0: chartjs-color-string "^0.6.0" color-convert "^1.9.3" -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.1: +"chokidar@>=3.0.0 <4.0.0": version "3.5.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== @@ -3791,6 +3804,21 @@ chokidar@^3.4.1, chokidar@^3.4.3: optionalDependencies: fsevents "~2.1.2" +chokidar@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -4392,6 +4420,17 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cosmiconfig@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" + integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + crc32-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4" @@ -6493,6 +6532,15 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -6586,7 +6634,7 @@ fsevents@^2.1.2, fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== -fsevents@~2.3.1: +fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -6740,21 +6788,6 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -6770,6 +6803,20 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.0.tgz#f851b59b388e788f3a44d63fab50382b2859c33c" + integrity sha512-Hdd4287VEJcZXUwv1l8a+vXC1GjOQqXe+VS30w/ypihpcnu9M1n3xeYeJu5CBpeEQj2nAab2xxz28GuA3vp4Ww== + dependencies: + is-glob "^4.0.1" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -7878,11 +7925,6 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= - is-electron@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-electron/-/is-electron-2.2.0.tgz#8943084f09e8b731b3a7a0298a7b5d56f6b7eef0" @@ -7900,11 +7942,6 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= - is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -7937,13 +7974,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= - dependencies: - is-extglob "^1.0.0" - is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -10049,7 +10079,7 @@ mock-fs@^4.14.0: resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== -modern-normalize@^1.0.0: +modern-normalize@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/modern-normalize/-/modern-normalize-1.1.0.tgz#da8e80140d9221426bd4f725c6e11283d34f90b7" integrity sha512-2lMlY1Yc1+CUy0gw4H95uNN7vjbpoED7NNRSBHE25nWfLBdmMzFCsPshlzbxHz+gYMcBEUN8V4pU16prcdPSgA== @@ -10756,10 +10786,10 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== -object-hash@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.1.1.tgz#9447d0279b4fcf80cff3259bf66a1dc73afabe09" - integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ== +object-hash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== object-inspect@^1.10.3, object-inspect@^1.9.0: version "1.10.3" @@ -11255,16 +11285,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -11547,16 +11567,6 @@ postcss-filter-plugins@^3.0.1: dependencies: postcss "^6.0.14" -postcss-functions@^3: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-functions/-/postcss-functions-3.0.0.tgz#0e94d01444700a481de20de4d55fb2640564250e" - integrity sha1-DpTQFERwCkgd4g3k1V+yZAVkJQ4= - dependencies: - glob "^7.1.2" - object-assign "^4.1.1" - postcss "^6.0.9" - postcss-value-parser "^3.3.0" - postcss-icss-keyframes@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/postcss-icss-keyframes/-/postcss-icss-keyframes-0.2.1.tgz#80c4455e0112b0f2f9c3c05ac7515062bb9ff295" @@ -11593,7 +11603,7 @@ postcss-load-config@^2.0.0: cosmiconfig "^5.0.0" import-cwd "^2.0.0" -postcss-load-config@^3.0.1: +postcss-load-config@^3.0.1, postcss-load-config@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.0.tgz#d39c47091c4aec37f50272373a6a648ef5e97829" integrity sha512-ipM8Ds01ZUophjDTQYSVP70slFSYg3T0/zyfII5vzhN6V57YSxMgG5syXuwi5VtS8wSf3iL30v0uBdoIVx4Q0g== @@ -11664,6 +11674,14 @@ postcss-selector-parser@^6.0.4: cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea" + integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-value-parser@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" @@ -11674,7 +11692,7 @@ postcss-value-parser@^4.1.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== -postcss@^6.0.14, postcss@^6.0.2, postcss@^6.0.9: +postcss@^6.0.14, postcss@^6.0.2: version "6.0.23" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag== @@ -11970,10 +11988,10 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -purgecss@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-3.1.3.tgz#26987ec09d12eeadc318e22f6e5a9eb0be094f41" - integrity sha512-hRSLN9mguJ2lzlIQtW4qmPS2kh6oMnA9RxdIYK8sz18QYqd6ePp4GNDl18oWHA1f2v2NEQIh51CO8s/E3YGckQ== +purgecss@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/purgecss/-/purgecss-4.0.3.tgz#8147b429f9c09db719e05d64908ea8b672913742" + integrity sha512-PYOIn5ibRIP34PBU9zohUcCI09c7drPJJtTDAc0Q6QlRz2/CHQ8ywGLdE7ZhxU2VTqB7p5wkvj5Qcm05Rz3Jmw== dependencies: commander "^6.0.0" glob "^7.0.0" @@ -12429,6 +12447,13 @@ readdirp@~3.5.0: dependencies: picomatch "^2.2.1" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -13996,38 +14021,42 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -tailwindcss@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.1.2.tgz#29402bf73a445faedd03df6d3b177e7b52b7c4a1" - integrity sha512-T5t+wwd+/hsOyRw2HJuFuv0LTUm3MUdHm2DJ94GPVgzqwPPFa9XxX0KlwLWupUuiOUj6uiKURCzYPHFcuPch/w== +tailwindcss@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-2.2.4.tgz#6a2e259b1e26125aeaa7cdc479963fd217c308b0" + integrity sha512-OdBCPgazNNsknSP+JfrPzkay9aqKjhKtFhbhgxHgvEFdHy/GuRPo2SCJ4w1SFTN8H6FPI4m6qD/Jj20NWY1GkA== dependencies: - "@fullhuman/postcss-purgecss" "^3.1.3" + "@fullhuman/postcss-purgecss" "^4.0.3" + arg "^5.0.0" bytes "^3.0.0" - chalk "^4.1.0" - chokidar "^3.5.1" + chalk "^4.1.1" + chokidar "^3.5.2" color "^3.1.3" + cosmiconfig "^7.0.0" detective "^5.2.0" didyoumean "^1.2.1" dlv "^1.1.3" fast-glob "^3.2.5" - fs-extra "^9.1.0" + fs-extra "^10.0.0" + glob-parent "^6.0.0" html-tags "^3.1.0" + is-glob "^4.0.1" lodash "^4.17.21" lodash.topath "^4.5.2" - modern-normalize "^1.0.0" + modern-normalize "^1.1.0" node-emoji "^1.8.1" normalize-path "^3.0.0" - object-hash "^2.1.1" - parse-glob "^3.0.4" - postcss-functions "^3" + object-hash "^2.2.0" postcss-js "^3.0.3" + postcss-load-config "^3.1.0" postcss-nested "5.0.5" - postcss-selector-parser "^6.0.4" + postcss-selector-parser "^6.0.6" postcss-value-parser "^4.1.0" pretty-hrtime "^1.0.3" quick-lru "^5.1.1" reduce-css-calc "^2.1.8" resolve "^1.20.0" + tmp "^0.2.1" tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" @@ -14270,7 +14299,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmp@^0.2.0: +tmp@^0.2.0, tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== @@ -15587,7 +15616,7 @@ yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.2, yaml@^1.7.2: +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== From cdb7c95b8f5954fec9380e781c4d10d0d88e0870 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 21 Jul 2021 10:22:32 -0400 Subject: [PATCH 098/131] Fix typings for KubeObjectDetailsRegistration (#3460) --- src/extensions/registries/kube-object-detail-registry.ts | 6 +++--- src/renderer/components/kube-object/kube-object-details.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/extensions/registries/kube-object-detail-registry.ts b/src/extensions/registries/kube-object-detail-registry.ts index 844f5fc0ee..9df1d094f1 100644 --- a/src/extensions/registries/kube-object-detail-registry.ts +++ b/src/extensions/registries/kube-object-detail-registry.ts @@ -24,14 +24,14 @@ import type { KubeObjectDetailsProps } from "../renderer-api/components"; import type { KubeObject } from "../renderer-api/k8s-api"; import { BaseRegistry } from "./base-registry"; -export interface KubeObjectDetailComponents { - Details: React.ComponentType>; +export interface KubeObjectDetailComponents { + Details: React.ComponentType>; } export interface KubeObjectDetailRegistration { kind: string; apiVersions: string[]; - components: KubeObjectDetailComponents; + components: KubeObjectDetailComponents; priority?: number; } diff --git a/src/renderer/components/kube-object/kube-object-details.tsx b/src/renderer/components/kube-object/kube-object-details.tsx index 1f0c494b4e..4075361368 100644 --- a/src/renderer/components/kube-object/kube-object-details.tsx +++ b/src/renderer/components/kube-object/kube-object-details.tsx @@ -82,7 +82,7 @@ export function getDetailsUrl(selfLink: string, resetSelected = false, mergeGlob return `?${params}`; } -export interface KubeObjectDetailsProps { +export interface KubeObjectDetailsProps { className?: string; object: T; } From b5e6bc36fe5c70cbfa96362212c9513aa239d747 Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Wed, 21 Jul 2021 11:50:43 -0400 Subject: [PATCH 099/131] Fix kube auth proxy race condition (#3458) * fix race condition by relying on mobx when() to indicate when the kube auth proxy is ready Signed-off-by: Jim Ehrismann * tweak KubeAuthProxy.run() return behaviour Signed-off-by: Jim Ehrismann --- src/main/context-handler.ts | 4 +++- src/main/kube-auth-proxy.ts | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index 04c2842a37..7e09d29f5c 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -146,8 +146,10 @@ export class ContextHandler { proxyEnv.HTTPS_PROXY = this.cluster.preferences.httpsProxy; } this.kubeAuthProxy = new KubeAuthProxy(this.cluster, proxyEnv); - await this.kubeAuthProxy.run(); + this.kubeAuthProxy.run(); } + + await this.kubeAuthProxy.whenReady; } stopServer() { diff --git a/src/main/kube-auth-proxy.ts b/src/main/kube-auth-proxy.ts index 87d4f9f67c..ac3bdf8dd1 100644 --- a/src/main/kube-auth-proxy.ts +++ b/src/main/kube-auth-proxy.ts @@ -27,6 +27,7 @@ import { Kubectl } from "./kubectl"; import logger from "./logger"; import * as url from "url"; import { getPortFrom } from "./utils/get-port"; +import { makeObservable, observable, when } from "mobx"; export interface KubeAuthProxyLog { data: string; @@ -47,8 +48,11 @@ export class KubeAuthProxy { protected env: NodeJS.ProcessEnv = null; protected proxyProcess: ChildProcess; protected kubectl: Kubectl; + @observable protected ready: boolean; constructor(cluster: Cluster, env: NodeJS.ProcessEnv) { + makeObservable(this); + this.ready = false; this.env = env; this.cluster = cluster; this.kubectl = Kubectl.bundled(); @@ -58,9 +62,13 @@ export class KubeAuthProxy { return url.parse(this.cluster.apiUrl).hostname; } + get whenReady() { + return when(() => this.ready); + } + public async run(): Promise { if (this.proxyProcess) { - return; + return this.whenReady; } const proxyBin = await this.kubectl.getPath(); @@ -103,7 +111,9 @@ export class KubeAuthProxy { this.sendIpcLogMessage({ data: data.toString() }); }); - return waitUntilUsed(this.port, 500, 10000); + await waitUntilUsed(this.port, 500, 10000); + + this.ready = true; } protected parseError(data: string) { @@ -132,6 +142,7 @@ export class KubeAuthProxy { } public exit() { + this.ready = false; if (!this.proxyProcess) return; logger.debug("[KUBE-AUTH]: stopping local proxy", this.cluster.getMeta()); this.proxyProcess.kill(); From 8bdcdbe76702172a1764b4a5560c926a45efe3a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jul 2021 15:28:12 -0400 Subject: [PATCH 100/131] Bump nodemon from 2.0.4 to 2.0.12 (#3465) Bumps [nodemon](https://github.com/remy/nodemon) from 2.0.4 to 2.0.12. - [Release notes](https://github.com/remy/nodemon/releases) - [Commits](https://github.com/remy/nodemon/compare/v2.0.4...v2.0.12) --- updated-dependencies: - dependency-name: nodemon dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 129 +++++++++++---------------------------------------- 2 files changed, 29 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index 32fc8cd60d..8ad309a059 100644 --- a/package.json +++ b/package.json @@ -345,7 +345,7 @@ "mini-css-extract-plugin": "^1.6.0", "node-loader": "^1.0.3", "node-sass": "^4.14.1", - "nodemon": "^2.0.4", + "nodemon": "^2.0.12", "open": "^7.3.1", "patch-package": "^6.2.2", "postcss": "^8.2.14", diff --git a/yarn.lock b/yarn.lock index aaef73ee8a..2bc6562fda 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2614,7 +2614,7 @@ anymatch@^3.0.0, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -anymatch@^3.0.3, anymatch@~3.1.1: +anymatch@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== @@ -3740,20 +3740,20 @@ chartjs-color@^2.1.0: chartjs-color-string "^0.6.0" color-convert "^1.9.3" -"chokidar@>=3.0.0 <4.0.0": - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.2.2, chokidar@^3.4.1, chokidar@^3.4.3, chokidar@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== dependencies: - anymatch "~3.1.1" + anymatch "~3.1.2" braces "~3.0.2" - glob-parent "~5.1.0" + glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" - readdirp "~3.5.0" + readdirp "~3.6.0" optionalDependencies: - fsevents "~2.3.1" + fsevents "~2.3.2" chokidar@^2.1.8: version "2.1.8" @@ -3774,51 +3774,6 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.2.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.0.tgz#b30611423ce376357c765b9b8f904b9fba3c0be8" - integrity sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.4.0" - optionalDependencies: - fsevents "~2.1.2" - -chokidar@^3.4.1, chokidar@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" - integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.1.2" - -chokidar@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -4746,10 +4701,10 @@ debug@3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" @@ -4760,27 +4715,13 @@ debug@4.1.0: dependencies: ms "^2.1.1" -debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - -debug@^3.1.0: +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" -debug@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -6629,12 +6570,12 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2, fsevents@~2.1.2: +fsevents@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== -fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -6796,7 +6737,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== @@ -10401,10 +10342,10 @@ node-sass@^4.14.1: stdout-stream "^1.4.0" "true-case-path" "^1.0.2" -nodemon@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416" - integrity sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ== +nodemon@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.12.tgz#5dae4e162b617b91f1873b3bfea215dd71e144d5" + integrity sha512-egCTmNZdObdBxUBw6ZNwvZ/xzk24CKRs5K6d+5zbmrMr7rOpPmfPeF6OxM3DDpaRx331CQRFEktn+wrFFfBSOA== dependencies: chokidar "^3.2.2" debug "^3.2.6" @@ -10414,8 +10355,8 @@ nodemon@^2.0.4: semver "^5.7.1" supports-color "^5.5.0" touch "^3.1.0" - undefsafe "^2.0.2" - update-notifier "^4.0.0" + undefsafe "^2.0.3" + update-notifier "^4.1.0" noop-logger@^0.1.1: version "0.1.1" @@ -12433,20 +12374,6 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.4.0.tgz#9fdccdf9e9155805449221ac645e8303ab5b9ada" - integrity sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ== - dependencies: - picomatch "^2.2.1" - -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -14697,7 +14624,7 @@ unbox-primitive@^1.0.1: has-symbols "^1.0.2" which-boxed-primitive "^1.0.2" -undefsafe@^2.0.2: +undefsafe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.3.tgz#6b166e7094ad46313b2202da7ecc2cd7cc6e7aae" integrity sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A== @@ -14822,10 +14749,10 @@ update-notifier@^2.2.0, update-notifier@^2.3.0, update-notifier@^2.5.0: semver-diff "^2.0.0" xdg-basedir "^3.0.0" -update-notifier@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.0.tgz#4866b98c3bc5b5473c020b1250583628f9a328f3" - integrity sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew== +update-notifier@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" + integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== dependencies: boxen "^4.2.0" chalk "^3.0.0" From 84c767a3d22a1518911159d080d2bf323e685a2f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 22 Jul 2021 09:55:34 -0400 Subject: [PATCH 101/131] release v5.1.3 (#3477) Signed-off-by: Sebastian Malton --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ad309a059..d1f8a44576 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.1.2", + "version": "5.1.3", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", From c6a4d55d86752f7d4e0ae8634bd971b7d84dc1e6 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Thu, 22 Jul 2021 16:57:14 +0300 Subject: [PATCH 102/131] Improving metric queries (#3424) --- src/main/cluster.ts | 2 + src/renderer/api/endpoints/cluster.api.ts | 44 ++++++------- src/renderer/api/endpoints/daemon-set.api.ts | 23 ++++++- src/renderer/api/endpoints/deployment.api.ts | 17 +++++ src/renderer/api/endpoints/ingress.api.ts | 23 +++---- src/renderer/api/endpoints/job.api.ts | 23 ++++++- src/renderer/api/endpoints/metrics.api.ts | 9 +++ src/renderer/api/endpoints/namespaces.api.ts | 21 +++++- src/renderer/api/endpoints/nodes.api.ts | 27 ++++---- .../endpoints/persistent-volume-claims.api.ts | 17 ++--- src/renderer/api/endpoints/pods.api.ts | 42 ++++++------ src/renderer/api/endpoints/replica-set.api.ts | 18 +++++- .../api/endpoints/stateful-set.api.ts | 18 +++++- .../+cluster/cluster-overview.store.ts | 4 +- .../+namespaces/namespace-details.tsx | 36 +++++++++-- .../+network-ingresses/ingress-details.tsx | 19 +++--- .../+network-ingresses/ingress.store.ts | 23 +------ .../components/+nodes/node-details.tsx | 26 +++++--- src/renderer/components/+nodes/nodes.store.ts | 64 ++----------------- src/renderer/components/+nodes/nodes.tsx | 43 ++++++++++--- .../volume-claim-details.tsx | 20 +++--- .../volume-claim.store.ts | 24 +------ .../daemonset-details.tsx | 28 +++++--- .../+workloads-daemonsets/daemonsets.store.ts | 20 ++---- .../deployment-details.tsx | 28 +++++--- .../deployments.store.ts | 19 ++---- .../+workloads-jobs/job-details.tsx | 31 ++++++++- .../+workloads-pods/pod-details.tsx | 30 +++------ .../components/+workloads-pods/pods.store.ts | 18 +----- .../replicaset-details.tsx | 25 ++++++-- .../replicasets.store.ts | 19 ++---- .../statefulset-details.tsx | 28 +++++--- .../statefulset.store.ts | 20 ++---- 33 files changed, 452 insertions(+), 357 deletions(-) diff --git a/src/main/cluster.ts b/src/main/cluster.ts index 58dbbf944f..6d3331c447 100644 --- a/src/main/cluster.ts +++ b/src/main/cluster.ts @@ -62,6 +62,8 @@ export enum ClusterMetricsResourceType { VolumeClaim = "VolumeClaim", ReplicaSet = "ReplicaSet", DaemonSet = "DaemonSet", + Job = "Job", + Namespace = "Namespace" } export type ClusterRefreshOptions = { diff --git a/src/renderer/api/endpoints/cluster.api.ts b/src/renderer/api/endpoints/cluster.api.ts index 75f15b5a5b..f0879fa501 100644 --- a/src/renderer/api/endpoints/cluster.api.ts +++ b/src/renderer/api/endpoints/cluster.api.ts @@ -26,30 +26,30 @@ import { KubeApi } from "../kube-api"; export class ClusterApi extends KubeApi { static kind = "Cluster"; static namespaced = true; +} - async getMetrics(nodeNames: string[], params?: IMetricsReqParams): Promise { - const nodes = nodeNames.join("|"); - const opts = { category: "cluster", nodes }; +export function getMetricsByNodeNames(nodeNames: string[], params?: IMetricsReqParams): Promise { + const nodes = nodeNames.join("|"); + const opts = { category: "cluster", nodes }; - return metricsApi.getMetrics({ - memoryUsage: opts, - workloadMemoryUsage: opts, - memoryRequests: opts, - memoryLimits: opts, - memoryCapacity: opts, - memoryAllocatableCapacity: opts, - cpuUsage: opts, - cpuRequests: opts, - cpuLimits: opts, - cpuCapacity: opts, - cpuAllocatableCapacity: opts, - podUsage: opts, - podCapacity: opts, - podAllocatableCapacity: opts, - fsSize: opts, - fsUsage: opts - }, params); - } + return metricsApi.getMetrics({ + memoryUsage: opts, + workloadMemoryUsage: opts, + memoryRequests: opts, + memoryLimits: opts, + memoryCapacity: opts, + memoryAllocatableCapacity: opts, + cpuUsage: opts, + cpuRequests: opts, + cpuLimits: opts, + cpuCapacity: opts, + cpuAllocatableCapacity: opts, + podUsage: opts, + podCapacity: opts, + podAllocatableCapacity: opts, + fsSize: opts, + fsUsage: opts + }, params); } export enum ClusterStatus { diff --git a/src/renderer/api/endpoints/daemon-set.api.ts b/src/renderer/api/endpoints/daemon-set.api.ts index 43419b5ac1..cb584806d4 100644 --- a/src/renderer/api/endpoints/daemon-set.api.ts +++ b/src/renderer/api/endpoints/daemon-set.api.ts @@ -20,11 +20,12 @@ */ import get from "lodash/get"; -import type { IPodContainer } from "./pods.api"; import { IAffinity, WorkloadKubeObject } from "../workload-kube-object"; import { autoBind } from "../../utils"; import { KubeApi } from "../kube-api"; +import { metricsApi } from "./metrics.api"; import type { KubeJsonApiData } from "../kube-json-api"; +import type { IPodContainer, IPodMetrics } from "./pods.api"; export class DaemonSet extends WorkloadKubeObject { static kind = "DaemonSet"; @@ -97,6 +98,24 @@ export class DaemonSet extends WorkloadKubeObject { } } -export const daemonSetApi = new KubeApi({ +export class DaemonSetApi extends KubeApi { +} + +export function getMetricsForDaemonSets(daemonsets: DaemonSet[], namespace: string, selector = ""): Promise { + const podSelector = daemonsets.map(daemonset => `${daemonset.getName()}-[[:alnum:]]{5}`).join("|"); + const opts = { category: "pods", pods: podSelector, namespace, selector }; + + return metricsApi.getMetrics({ + cpuUsage: opts, + memoryUsage: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); +} + +export const daemonSetApi = new DaemonSetApi({ objectConstructor: DaemonSet, }); diff --git a/src/renderer/api/endpoints/deployment.api.ts b/src/renderer/api/endpoints/deployment.api.ts index ef5b1c9ca7..0d5bef48dd 100644 --- a/src/renderer/api/endpoints/deployment.api.ts +++ b/src/renderer/api/endpoints/deployment.api.ts @@ -24,6 +24,8 @@ import moment from "moment"; import { IAffinity, WorkloadKubeObject } from "../workload-kube-object"; import { autoBind } from "../../utils"; import { KubeApi } from "../kube-api"; +import { metricsApi } from "./metrics.api"; +import type { IPodMetrics } from "./pods.api"; import type { KubeJsonApiData } from "../kube-json-api"; export class DeploymentApi extends KubeApi { @@ -68,6 +70,21 @@ export class DeploymentApi extends KubeApi { } } +export function getMetricsForDeployments(deployments: Deployment[], namespace: string, selector = ""): Promise { + const podSelector = deployments.map(deployment => `${deployment.getName()}-[[:alnum:]]{9,}-[[:alnum:]]{5}`).join("|"); + const opts = { category: "pods", pods: podSelector, namespace, selector }; + + return metricsApi.getMetrics({ + cpuUsage: opts, + memoryUsage: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); +} + interface IContainerProbe { httpGet?: { path?: string; diff --git a/src/renderer/api/endpoints/ingress.api.ts b/src/renderer/api/endpoints/ingress.api.ts index a65af0e326..31745f30c4 100644 --- a/src/renderer/api/endpoints/ingress.api.ts +++ b/src/renderer/api/endpoints/ingress.api.ts @@ -26,18 +26,19 @@ import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; export class IngressApi extends KubeApi { - getMetrics(ingress: string, namespace: string): Promise { - const opts = { category: "ingress", ingress, namespace }; +} - return metricsApi.getMetrics({ - bytesSentSuccess: opts, - bytesSentFailure: opts, - requestDurationSeconds: opts, - responseDurationSeconds: opts - }, { - namespace, - }); - } +export function getMetricsForIngress(ingress: string, namespace: string): Promise { + const opts = { category: "ingress", ingress }; + + return metricsApi.getMetrics({ + bytesSentSuccess: opts, + bytesSentFailure: opts, + requestDurationSeconds: opts, + responseDurationSeconds: opts + }, { + namespace, + }); } export interface IIngressMetrics { diff --git a/src/renderer/api/endpoints/job.api.ts b/src/renderer/api/endpoints/job.api.ts index f9e3188021..823a04bef9 100644 --- a/src/renderer/api/endpoints/job.api.ts +++ b/src/renderer/api/endpoints/job.api.ts @@ -22,10 +22,11 @@ import get from "lodash/get"; import { autoBind } from "../../utils"; import { IAffinity, WorkloadKubeObject } from "../workload-kube-object"; -import type { IPodContainer } from "./pods.api"; import { KubeApi } from "../kube-api"; +import { metricsApi } from "./metrics.api"; import type { JsonApiParams } from "../json-api"; import type { KubeJsonApiData } from "../kube-json-api"; +import type { IPodContainer, IPodMetrics } from "./pods.api"; export class Job extends WorkloadKubeObject { static kind = "Job"; @@ -129,6 +130,24 @@ export class Job extends WorkloadKubeObject { } } -export const jobApi = new KubeApi({ +export class JobApi extends KubeApi { +} + +export function getMetricsForJobs(jobs: Job[], namespace: string, selector = ""): Promise { + const podSelector = jobs.map(job => `${job.getName()}-[[:alnum:]]{5}`).join("|"); + const opts = { category: "pods", pods: podSelector, namespace, selector }; + + return metricsApi.getMetrics({ + cpuUsage: opts, + memoryUsage: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); +} + +export const jobApi = new JobApi({ objectConstructor: Job, }); diff --git a/src/renderer/api/endpoints/metrics.api.ts b/src/renderer/api/endpoints/metrics.api.ts index 3f5b6e66e7..b0e3c0bf9d 100644 --- a/src/renderer/api/endpoints/metrics.api.ts +++ b/src/renderer/api/endpoints/metrics.api.ts @@ -60,6 +60,15 @@ export interface IMetricsReqParams { namespace?: string; // rbac-proxy validation param } +export interface IResourceMetrics { + [metric: string]: T; + cpuUsage: T; + memoryUsage: T; + fsUsage: T; + networkReceive: T; + networkTransmit: T; +} + export const metricsApi = { async getMetrics(query: T, reqParams: IMetricsReqParams = {}): Promise { const { range = 3600, step = 60, namespace } = reqParams; diff --git a/src/renderer/api/endpoints/namespaces.api.ts b/src/renderer/api/endpoints/namespaces.api.ts index 63dd6a2ab0..208edb023f 100644 --- a/src/renderer/api/endpoints/namespaces.api.ts +++ b/src/renderer/api/endpoints/namespaces.api.ts @@ -22,6 +22,8 @@ import { KubeApi } from "../kube-api"; import { KubeObject } from "../kube-object"; import { autoBind } from "../../utils"; +import { metricsApi } from "./metrics.api"; +import type { IPodMetrics } from "./pods.api"; import type { KubeJsonApiData } from "../kube-json-api"; export enum NamespaceStatus { @@ -50,6 +52,23 @@ export class Namespace extends KubeObject { } } -export const namespacesApi = new KubeApi({ +export class NamespaceApi extends KubeApi { +} + +export function getMetricsForNamespace(namespace: string, selector = ""): Promise { + const opts = { category: "pods", pods: ".*", namespace, selector }; + + return metricsApi.getMetrics({ + cpuUsage: opts, + memoryUsage: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); +} + +export const namespacesApi = new NamespaceApi({ objectConstructor: Namespace, }); diff --git a/src/renderer/api/endpoints/nodes.api.ts b/src/renderer/api/endpoints/nodes.api.ts index d2c35761ca..21f7776c58 100644 --- a/src/renderer/api/endpoints/nodes.api.ts +++ b/src/renderer/api/endpoints/nodes.api.ts @@ -26,20 +26,21 @@ import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; export class NodesApi extends KubeApi { - getMetrics(): Promise { - const opts = { category: "nodes" }; +} - return metricsApi.getMetrics({ - memoryUsage: opts, - workloadMemoryUsage: opts, - memoryCapacity: opts, - memoryAllocatableCapacity: opts, - cpuUsage: opts, - cpuCapacity: opts, - fsSize: opts, - fsUsage: opts - }); - } +export function getMetricsForAllNodes(): Promise { + const opts = { category: "nodes"}; + + return metricsApi.getMetrics({ + memoryUsage: opts, + workloadMemoryUsage: opts, + memoryCapacity: opts, + memoryAllocatableCapacity: opts, + cpuUsage: opts, + cpuCapacity: opts, + fsSize: opts, + fsUsage: opts + }); } export interface INodeMetrics { diff --git a/src/renderer/api/endpoints/persistent-volume-claims.api.ts b/src/renderer/api/endpoints/persistent-volume-claims.api.ts index e8b50683c9..356122569a 100644 --- a/src/renderer/api/endpoints/persistent-volume-claims.api.ts +++ b/src/renderer/api/endpoints/persistent-volume-claims.api.ts @@ -27,14 +27,15 @@ import { KubeApi } from "../kube-api"; import type { KubeJsonApiData } from "../kube-json-api"; export class PersistentVolumeClaimsApi extends KubeApi { - getMetrics(pvcName: string, namespace: string): Promise { - return metricsApi.getMetrics({ - diskUsage: { category: "pvc", pvc: pvcName, namespace }, - diskCapacity: { category: "pvc", pvc: pvcName, namespace } - }, { - namespace - }); - } +} + +export function getMetricsForPvc(pvc: PersistentVolumeClaim): Promise { + return metricsApi.getMetrics({ + diskUsage: { category: "pvc", pvc: pvc.getName() }, + diskCapacity: { category: "pvc", pvc: pvc.getName() } + }, { + namespace: pvc.getNs() + }); } export interface IPvcMetrics { diff --git a/src/renderer/api/endpoints/pods.api.ts b/src/renderer/api/endpoints/pods.api.ts index 5b85a3d42b..e4d33c75bc 100644 --- a/src/renderer/api/endpoints/pods.api.ts +++ b/src/renderer/api/endpoints/pods.api.ts @@ -31,38 +31,38 @@ export class PodsApi extends KubeApi { return this.request.get(path, { query }); } +} - getMetrics(pods: Pod[], namespace: string, selector = "pod, namespace"): Promise { - const podSelector = pods.map(pod => pod.getName()).join("|"); - const opts = { category: "pods", pods: podSelector, namespace, selector }; +export function getMetricsForPods(pods: Pod[], namespace: string, selector = "pod, namespace"): Promise { + const podSelector = pods.map(pod => pod.getName()).join("|"); + const opts = { category: "pods", pods: podSelector, namespace, selector }; - return metricsApi.getMetrics({ - cpuUsage: opts, - cpuRequests: opts, - cpuLimits: opts, - memoryUsage: opts, - memoryRequests: opts, - memoryLimits: opts, - fsUsage: opts, - networkReceive: opts, - networkTransmit: opts, - }, { - namespace, - }); - } + return metricsApi.getMetrics({ + cpuUsage: opts, + cpuRequests: opts, + cpuLimits: opts, + memoryUsage: opts, + memoryRequests: opts, + memoryLimits: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); } export interface IPodMetrics { [metric: string]: T; cpuUsage: T; - cpuRequests: T; - cpuLimits: T; memoryUsage: T; - memoryRequests: T; - memoryLimits: T; fsUsage: T; networkReceive: T; networkTransmit: T; + cpuRequests?: T; + cpuLimits?: T; + memoryRequests?: T; + memoryLimits?: T; } // Reference: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#read-log-pod-v1-core diff --git a/src/renderer/api/endpoints/replica-set.api.ts b/src/renderer/api/endpoints/replica-set.api.ts index e2311c6b87..4632e1bfad 100644 --- a/src/renderer/api/endpoints/replica-set.api.ts +++ b/src/renderer/api/endpoints/replica-set.api.ts @@ -22,8 +22,9 @@ import get from "lodash/get"; import { autoBind } from "../../utils"; import { WorkloadKubeObject } from "../workload-kube-object"; -import type { IPodContainer, Pod } from "./pods.api"; import { KubeApi } from "../kube-api"; +import { metricsApi } from "./metrics.api"; +import type { IPodContainer, IPodMetrics, Pod } from "./pods.api"; import type { KubeJsonApiData } from "../kube-json-api"; export class ReplicaSetApi extends KubeApi { @@ -49,6 +50,21 @@ export class ReplicaSetApi extends KubeApi { } } +export function getMetricsForReplicaSets(replicasets: ReplicaSet[], namespace: string, selector = ""): Promise { + const podSelector = replicasets.map(replicaset => `${replicaset.getName()}-[[:alnum:]]{5}`).join("|"); + const opts = { category: "pods", pods: podSelector, namespace, selector }; + + return metricsApi.getMetrics({ + cpuUsage: opts, + memoryUsage: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); +} + export class ReplicaSet extends WorkloadKubeObject { static kind = "ReplicaSet"; static namespaced = true; diff --git a/src/renderer/api/endpoints/stateful-set.api.ts b/src/renderer/api/endpoints/stateful-set.api.ts index b91e06c25c..21f4c6e02e 100644 --- a/src/renderer/api/endpoints/stateful-set.api.ts +++ b/src/renderer/api/endpoints/stateful-set.api.ts @@ -20,10 +20,11 @@ */ import get from "lodash/get"; -import type { IPodContainer } from "./pods.api"; import { IAffinity, WorkloadKubeObject } from "../workload-kube-object"; import { autoBind } from "../../utils"; import { KubeApi } from "../kube-api"; +import { metricsApi } from "./metrics.api"; +import type { IPodContainer, IPodMetrics } from "./pods.api"; import type { KubeJsonApiData } from "../kube-json-api"; export class StatefulSetApi extends KubeApi { @@ -49,6 +50,21 @@ export class StatefulSetApi extends KubeApi { } } +export function getMetricsForStatefulSets(statefulSets: StatefulSet[], namespace: string, selector = ""): Promise { + const podSelector = statefulSets.map(statefulset => `${statefulset.getName()}-[[:digit:]]+`).join("|"); + const opts = { category: "pods", pods: podSelector, namespace, selector }; + + return metricsApi.getMetrics({ + cpuUsage: opts, + memoryUsage: opts, + fsUsage: opts, + networkReceive: opts, + networkTransmit: opts, + }, { + namespace, + }); +} + export class StatefulSet extends WorkloadKubeObject { static kind = "StatefulSet"; static namespaced = true; diff --git a/src/renderer/components/+cluster/cluster-overview.store.ts b/src/renderer/components/+cluster/cluster-overview.store.ts index 846f2466b4..547eb6832f 100644 --- a/src/renderer/components/+cluster/cluster-overview.store.ts +++ b/src/renderer/components/+cluster/cluster-overview.store.ts @@ -21,7 +21,7 @@ import { action, observable, reaction, when, makeObservable } from "mobx"; import { KubeObjectStore } from "../../kube-object.store"; -import { Cluster, clusterApi, IClusterMetrics } from "../../api/endpoints"; +import { Cluster, clusterApi, getMetricsByNodeNames, IClusterMetrics } from "../../api/endpoints"; import { autoBind, createStorage } from "../../utils"; import { IMetricsReqParams, normalizeMetrics } from "../../api/endpoints/metrics.api"; import { nodesStore } from "../+nodes/nodes.store"; @@ -101,7 +101,7 @@ export class ClusterOverviewStore extends KubeObjectStore implements Cl const { masterNodes, workerNodes } = nodesStore; const nodes = this.metricNodeRole === MetricNodeRole.MASTER && masterNodes.length ? masterNodes : workerNodes; - this.metrics = await clusterApi.getMetrics(nodes.map(node => node.getName()), params); + this.metrics = await getMetricsByNodeNames(nodes.map(node => node.getName()), params); this.metricsLoaded = true; } diff --git a/src/renderer/components/+namespaces/namespace-details.tsx b/src/renderer/components/+namespaces/namespace-details.tsx index 95103f553b..f61db41711 100644 --- a/src/renderer/components/+namespaces/namespace-details.tsx +++ b/src/renderer/components/+namespaces/namespace-details.tsx @@ -22,28 +22,44 @@ import "./namespace-details.scss"; import React from "react"; -import { computed, makeObservable } from "mobx"; -import { observer } from "mobx-react"; +import { computed, makeObservable, observable, reaction } from "mobx"; +import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import { cssNames } from "../../utils"; -import type { Namespace } from "../../api/endpoints"; +import { getMetricsForNamespace, IPodMetrics, Namespace } from "../../api/endpoints"; import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import { Link } from "react-router-dom"; import { Spinner } from "../spinner"; import { resourceQuotaStore } from "../+config-resource-quotas/resource-quotas.store"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { limitRangeStore } from "../+config-limit-ranges/limit-ranges.store"; +import { ResourceMetrics } from "../resource-metrics"; +import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../main/cluster"; +import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; interface Props extends KubeObjectDetailsProps { } @observer export class NamespaceDetails extends React.Component { + @observable metrics: IPodMetrics = null; + constructor(props: Props) { super(props); makeObservable(this); } + @disposeOnUnmount + clean = reaction(() => this.props.object, () => { + this.metrics = null; + }); + + componentDidMount() { + resourceQuotaStore.reloadAll(); + limitRangeStore.reloadAll(); + } + @computed get quotas() { const namespace = this.props.object.getName(); @@ -56,9 +72,8 @@ export class NamespaceDetails extends React.Component { return limitRangeStore.getAllByNs(namespace); } - componentDidMount() { - resourceQuotaStore.reloadAll(); - limitRangeStore.reloadAll(); + async loadMetrics() { + this.metrics = await getMetricsForNamespace(this.props.object.getName(), ""); } render() { @@ -66,9 +81,18 @@ export class NamespaceDetails extends React.Component { if (!namespace) return null; const status = namespace.getStatus(); + const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Namespace); return (
+ {!isMetricHidden && ( + + + + )} diff --git a/src/renderer/components/+network-ingresses/ingress-details.tsx b/src/renderer/components/+network-ingresses/ingress-details.tsx index 8e3c836b34..995e4d3b0f 100644 --- a/src/renderer/components/+network-ingresses/ingress-details.tsx +++ b/src/renderer/components/+network-ingresses/ingress-details.tsx @@ -23,16 +23,15 @@ import "./ingress-details.scss"; import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; -import { reaction } from "mobx"; +import { observable, reaction } from "mobx"; import { DrawerItem, DrawerTitle } from "../drawer"; import type { ILoadBalancerIngress, Ingress } from "../../api/endpoints"; import { Table, TableCell, TableHead, TableRow } from "../table"; -import { ingressStore } from "./ingress.store"; import { ResourceMetrics } from "../resource-metrics"; import type { KubeObjectDetailsProps } from "../kube-object"; import { IngressCharts } from "./ingress-charts"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; -import { getBackendServiceNamePort } from "../../api/endpoints/ingress.api"; +import { getBackendServiceNamePort, getMetricsForIngress, IIngressMetrics } from "../../api/endpoints/ingress.api"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; @@ -41,13 +40,17 @@ interface Props extends KubeObjectDetailsProps { @observer export class IngressDetails extends React.Component { + @observable metrics: IIngressMetrics = null; + @disposeOnUnmount clean = reaction(() => this.props.object, () => { - ingressStore.reset(); + this.metrics = null; }); - componentWillUnmount() { - ingressStore.reset(); + async loadMetrics() { + const { object: ingress } = this.props; + + this.metrics = await getMetricsForIngress(ingress.getName(), ingress.getNs()); } renderPaths(ingress: Ingress) { @@ -124,7 +127,7 @@ export class IngressDetails extends React.Component { const { spec, status } = ingress; const ingressPoints = status?.loadBalancer?.ingress; - const { metrics } = ingressStore; + const { metrics } = this; const metricTabs = [ "Network", "Duration", @@ -136,7 +139,7 @@ export class IngressDetails extends React.Component {
{!isMetricHidden && ( ingressStore.loadMetrics(ingress)} + loader={this.loadMetrics} tabs={metricTabs} object={ingress} params={{ metrics }} > diff --git a/src/renderer/components/+network-ingresses/ingress.store.ts b/src/renderer/components/+network-ingresses/ingress.store.ts index 3d17aefa3c..72190a5103 100644 --- a/src/renderer/components/+network-ingresses/ingress.store.ts +++ b/src/renderer/components/+network-ingresses/ingress.store.ts @@ -18,31 +18,12 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -import { observable, makeObservable } from "mobx"; -import { KubeObjectStore } from "../../kube-object.store"; -import { autoBind } from "../../utils"; -import { IIngressMetrics, Ingress, ingressApi } from "../../api/endpoints"; import { apiManager } from "../../api/api-manager"; +import { Ingress, ingressApi } from "../../api/endpoints"; +import { KubeObjectStore } from "../../kube-object.store"; export class IngressStore extends KubeObjectStore { api = ingressApi; - @observable metrics: IIngressMetrics = null; - - constructor() { - super(); - - makeObservable(this); - autoBind(this); - } - - async loadMetrics(ingress: Ingress) { - this.metrics = await this.api.getMetrics(ingress.getName(), ingress.getNs()); - } - - reset() { - this.metrics = null; - } } export const ingressStore = new IngressStore(); diff --git a/src/renderer/components/+nodes/node-details.tsx b/src/renderer/components/+nodes/node-details.tsx index a12109235b..56ce3e53e2 100644 --- a/src/renderer/components/+nodes/node-details.tsx +++ b/src/renderer/components/+nodes/node-details.tsx @@ -27,36 +27,46 @@ import kebabCase from "lodash/kebabCase"; import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem, DrawerItemLabels } from "../drawer"; import { Badge } from "../badge"; -import { nodesStore } from "./nodes.store"; import { ResourceMetrics } from "../resource-metrics"; import { podsStore } from "../+workloads-pods/pods.store"; import type { KubeObjectDetailsProps } from "../kube-object"; -import type { Node } from "../../api/endpoints"; +import { getMetricsByNodeNames, IClusterMetrics, Node } from "../../api/endpoints"; import { NodeCharts } from "./node-charts"; -import { reaction } from "mobx"; +import { makeObservable, observable, reaction } from "mobx"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; import { NodeDetailsResources } from "./node-details-resources"; import { DrawerTitle } from "../drawer/drawer-title"; +import { boundMethod } from "../../utils"; interface Props extends KubeObjectDetailsProps { } @observer export class NodeDetails extends React.Component { + @observable metrics: Partial; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + @disposeOnUnmount clean = reaction(() => this.props.object.getName(), () => { - nodesStore.nodeMetrics = null; + this.metrics = null; }); async componentDidMount() { podsStore.reloadAll(); } - componentWillUnmount() { - nodesStore.nodeMetrics = null; + @boundMethod + async loadMetrics() { + const { object: node } = this.props; + + this.metrics = await getMetricsByNodeNames([node.getName()]); } render() { @@ -68,7 +78,7 @@ export class NodeDetails extends React.Component { const conditions = node.getActiveConditions(); const taints = node.getTaints(); const childPods = podsStore.getPodsByNode(node.getName()); - const metrics = nodesStore.nodeMetrics; + const { metrics } = this; const metricTabs = [ "CPU", "Memory", @@ -81,7 +91,7 @@ export class NodeDetails extends React.Component {
{!isMetricHidden && podsStore.isLoaded && ( nodesStore.loadMetrics(node.getName())} + loader={this.loadMetrics} tabs={metricTabs} object={node} params={{ metrics }} > diff --git a/src/renderer/components/+nodes/nodes.store.ts b/src/renderer/components/+nodes/nodes.store.ts index 676643db62..43b5e32cca 100644 --- a/src/renderer/components/+nodes/nodes.store.ts +++ b/src/renderer/components/+nodes/nodes.store.ts @@ -18,22 +18,17 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - import { sum } from "lodash"; -import { action, computed, observable, makeObservable } from "mobx"; -import { clusterApi, IClusterMetrics, INodeMetrics, Node, nodesApi } from "../../api/endpoints"; -import { autoBind } from "../../utils"; -import { KubeObjectStore } from "../../kube-object.store"; +import { computed, makeObservable } from "mobx"; + import { apiManager } from "../../api/api-manager"; +import { Node, nodesApi } from "../../api/endpoints"; +import { KubeObjectStore } from "../../kube-object.store"; +import { autoBind } from "../../utils"; export class NodesStore extends KubeObjectStore { api = nodesApi; - @observable metrics: Partial = {}; - @observable nodeMetrics: Partial = null; - @observable metricsLoading = false; - @observable metricsLoaded = false; - constructor() { super(); @@ -41,23 +36,6 @@ export class NodesStore extends KubeObjectStore { autoBind(this); } - @action - async loadUsageMetrics() { - this.metricsLoading = true; - - try { - this.metrics = await nodesApi.getMetrics(); - this.metricsLoaded = true; - } finally { - this.metricsLoading = false; - } - } - - @action - async loadMetrics(nodeName: string) { - this.nodeMetrics = await clusterApi.getMetrics([nodeName]); - } - @computed get masterNodes() { return this.items.filter(node => node.getRoleLabels().includes("master")); } @@ -66,41 +44,9 @@ export class NodesStore extends KubeObjectStore { return this.items.filter(node => !node.getRoleLabels().includes("master")); } - getLastMetricValues(node: Node, metricNames: string[]): number[] { - if (!this.metricsLoaded) { - return []; - } - const nodeName = node.getName(); - - return metricNames.map(metricName => { - try { - const metric = this.metrics[metricName]; - const result = metric.data.result.find(result => { - return [ - result.metric.node, - result.metric.instance, - result.metric.kubernetes_node, - ].includes(nodeName); - }); - - return result ? parseFloat(result.values.slice(-1)[0][1]) : 0; - } catch (e) { - return 0; - } - }); - } - getWarningsCount(): number { return sum(this.items.map((node: Node) => node.getWarningConditions().length)); } - - reset() { - super.reset(); - this.metrics = {}; - this.nodeMetrics = null; - this.metricsLoading = false; - this.metricsLoaded = false; - } } export const nodesStore = new NodesStore(); diff --git a/src/renderer/components/+nodes/nodes.tsx b/src/renderer/components/+nodes/nodes.tsx index abd389d6e7..4b5672b816 100644 --- a/src/renderer/components/+nodes/nodes.tsx +++ b/src/renderer/components/+nodes/nodes.tsx @@ -28,7 +28,7 @@ import { TabLayout } from "../layout/tab-layout"; import { nodesStore } from "./nodes.store"; import { podsStore } from "../+workloads-pods/pods.store"; import { KubeObjectListLayout } from "../kube-object"; -import type { Node } from "../../api/endpoints/nodes.api"; +import { getMetricsForAllNodes, INodeMetrics, Node } from "../../api/endpoints/nodes.api"; import { LineProgress } from "../line-progress"; import { bytesToUnits } from "../../utils/convertMemory"; import { Tooltip, TooltipPosition } from "../tooltip"; @@ -39,6 +39,8 @@ import { Badge } from "../badge/badge"; import { kubeWatchApi } from "../../api/kube-watch-api"; import { eventStore } from "../+events/event.store"; import type { NodesRouteParams } from "../../../common/routes"; +import { observable } from "mobx"; +import isEmpty from "lodash/isEmpty"; enum columnId { name = "name", @@ -58,7 +60,8 @@ interface Props extends RouteComponentProps { @observer export class Nodes extends React.Component { - private metricsWatcher = interval(30, () => nodesStore.loadUsageMetrics()); + @observable metrics: Partial = {}; + private metricsWatcher = interval(30, async () => this.metrics = await getMetricsForAllNodes()); componentDidMount() { this.metricsWatcher.start(true); @@ -73,8 +76,32 @@ export class Nodes extends React.Component { this.metricsWatcher.stop(); } + getLastMetricValues(node: Node, metricNames: string[]): number[] { + if (isEmpty(this.metrics)) { + return []; + } + const nodeName = node.getName(); + + return metricNames.map(metricName => { + try { + const metric = this.metrics[metricName]; + const result = metric.data.result.find(result => { + return [ + result.metric.node, + result.metric.instance, + result.metric.kubernetes_node, + ].includes(nodeName); + }); + + return result ? parseFloat(result.values.slice(-1)[0][1]) : 0; + } catch (e) { + return 0; + } + }); + } + renderCpuUsage(node: Node) { - const metrics = nodesStore.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]); + const metrics = this.getLastMetricValues(node, ["cpuUsage", "cpuCapacity"]); if (!metrics || !metrics[1]) return ; const usage = metrics[0]; @@ -97,7 +124,7 @@ export class Nodes extends React.Component { } renderMemoryUsage(node: Node) { - const metrics = nodesStore.getLastMetricValues(node, ["workloadMemoryUsage", "memoryAllocatableCapacity"]); + const metrics = this.getLastMetricValues(node, ["workloadMemoryUsage", "memoryAllocatableCapacity"]); if (!metrics || !metrics[1]) return ; const usage = metrics[0]; @@ -116,7 +143,7 @@ export class Nodes extends React.Component { } renderDiskUsage(node: Node): any { - const metrics = nodesStore.getLastMetricValues(node, ["fsUsage", "fsSize"]); + const metrics = this.getLastMetricValues(node, ["fsUsage", "fsSize"]); if (!metrics || !metrics[1]) return ; const usage = metrics[0]; @@ -172,9 +199,9 @@ export class Nodes extends React.Component { isSelectable={false} sortingCallbacks={{ [columnId.name]: (node: Node) => node.getName(), - [columnId.cpu]: (node: Node) => nodesStore.getLastMetricValues(node, ["cpuUsage"]), - [columnId.memory]: (node: Node) => nodesStore.getLastMetricValues(node, ["memoryUsage"]), - [columnId.disk]: (node: Node) => nodesStore.getLastMetricValues(node, ["fsUsage"]), + [columnId.cpu]: (node: Node) => this.getLastMetricValues(node, ["cpuUsage"]), + [columnId.memory]: (node: Node) => this.getLastMetricValues(node, ["memoryUsage"]), + [columnId.disk]: (node: Node) => this.getLastMetricValues(node, ["fsUsage"]), [columnId.conditions]: (node: Node) => node.getNodeConditionText(), [columnId.taints]: (node: Node) => node.getTaints().length, [columnId.roles]: (node: Node) => node.getRoleLabels(), diff --git a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx index 3066aafea8..372d6bfb6e 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claim-details.tsx @@ -22,17 +22,16 @@ import "./volume-claim-details.scss"; import React, { Fragment } from "react"; -import { reaction } from "mobx"; +import { action, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; import { podsStore } from "../+workloads-pods/pods.store"; import { Link } from "react-router-dom"; -import { volumeClaimStore } from "./volume-claim.store"; import { ResourceMetrics } from "../resource-metrics"; import { VolumeClaimDiskChart } from "./volume-claim-disk-chart"; import { getDetailsUrl, KubeObjectDetailsProps, KubeObjectMeta } from "../kube-object"; -import type { PersistentVolumeClaim } from "../../api/endpoints"; +import { getMetricsForPvc, IPvcMetrics, PersistentVolumeClaim } from "../../api/endpoints"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; @@ -41,13 +40,18 @@ interface Props extends KubeObjectDetailsProps { @observer export class PersistentVolumeClaimDetails extends React.Component { + @observable metrics: IPvcMetrics = null; + @disposeOnUnmount clean = reaction(() => this.props.object, () => { - volumeClaimStore.reset(); + this.metrics = null; }); - componentWillUnmount() { - volumeClaimStore.reset(); + @action + async loadMetrics() { + const { object: volumeClaim } = this.props; + + this.metrics = await getMetricsForPvc(volumeClaim); } render() { @@ -57,7 +61,7 @@ export class PersistentVolumeClaimDetails extends React.Component { return null; } const { storageClassName, accessModes } = volumeClaim.spec; - const { metrics } = volumeClaimStore; + const { metrics } = this; const pods = volumeClaim.getPods(podsStore.items); const metricTabs = [ "Disk" @@ -68,7 +72,7 @@ export class PersistentVolumeClaimDetails extends React.Component {
{!isMetricHidden && ( volumeClaimStore.loadMetrics(volumeClaim)} + loader={this.loadMetrics} tabs={metricTabs} object={volumeClaim} params={{ metrics }} > diff --git a/src/renderer/components/+storage-volume-claims/volume-claim.store.ts b/src/renderer/components/+storage-volume-claims/volume-claim.store.ts index d703dd8d46..4beca645a5 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claim.store.ts +++ b/src/renderer/components/+storage-volume-claims/volume-claim.store.ts @@ -18,32 +18,12 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -import { action, observable, makeObservable } from "mobx"; -import { KubeObjectStore } from "../../kube-object.store"; -import { autoBind } from "../../utils"; -import { IPvcMetrics, PersistentVolumeClaim, pvcApi } from "../../api/endpoints"; import { apiManager } from "../../api/api-manager"; +import { PersistentVolumeClaim, pvcApi } from "../../api/endpoints"; +import { KubeObjectStore } from "../../kube-object.store"; export class VolumeClaimStore extends KubeObjectStore { api = pvcApi; - @observable metrics: IPvcMetrics = null; - - constructor() { - super(); - - makeObservable(this); - autoBind(this); - } - - @action - async loadMetrics(pvc: PersistentVolumeClaim) { - this.metrics = await pvcApi.getMetrics(pvc.getName(), pvc.getNs()); - } - - reset() { - this.metrics = null; - } } export const volumeClaimStore = new VolumeClaimStore(); diff --git a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx index 199385a1f4..4479995506 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonset-details.tsx @@ -31,31 +31,42 @@ import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities" import { daemonSetStore } from "./daemonsets.store"; import { podsStore } from "../+workloads-pods/pods.store"; import type { KubeObjectDetailsProps } from "../kube-object"; -import type { DaemonSet } from "../../api/endpoints"; +import { DaemonSet, getMetricsForDaemonSets, IPodMetrics } from "../../api/endpoints"; import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { reaction } from "mobx"; +import { makeObservable, observable, reaction } from "mobx"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; +import { boundMethod } from "../../utils"; interface Props extends KubeObjectDetailsProps { } @observer export class DaemonSetDetails extends React.Component { + @observable metrics: IPodMetrics = null; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + @disposeOnUnmount clean = reaction(() => this.props.object, () => { - daemonSetStore.reset(); + this.metrics = null; }); componentDidMount() { podsStore.reloadAll(); } - componentWillUnmount() { - daemonSetStore.reset(); + @boundMethod + async loadMetrics() { + const { object: daemonSet } = this.props; + + this.metrics = await getMetricsForDaemonSets([daemonSet], daemonSet.getNs(), ""); } render() { @@ -67,15 +78,14 @@ export class DaemonSetDetails extends React.Component { const images = daemonSet.getImages(); const nodeSelector = daemonSet.getNodeSelectors(); const childPods = daemonSetStore.getChildPods(daemonSet); - const metrics = daemonSetStore.metrics; const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.DaemonSet); return (
{!isMetricHidden && podsStore.isLoaded && ( daemonSetStore.loadMetrics(daemonSet)} - tabs={podMetricTabs} object={daemonSet} params={{ metrics }} + loader={this.loadMetrics} + tabs={podMetricTabs} object={daemonSet} params={{ metrics: this.metrics }} > @@ -110,7 +120,7 @@ export class DaemonSetDetails extends React.Component { - +
); diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts index 210e6bdcdd..095eb2b9d7 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.store.ts @@ -18,19 +18,17 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { makeObservable } from "mobx"; -import { observable, makeObservable } from "mobx"; -import { KubeObjectStore } from "../../kube-object.store"; -import { autoBind } from "../../utils"; -import { DaemonSet, daemonSetApi, IPodMetrics, Pod, podsApi, PodStatus } from "../../api/endpoints"; import { podsStore } from "../+workloads-pods/pods.store"; import { apiManager } from "../../api/api-manager"; +import { DaemonSet, daemonSetApi, Pod, PodStatus } from "../../api/endpoints"; +import { KubeObjectStore } from "../../kube-object.store"; +import { autoBind } from "../../utils"; export class DaemonSetStore extends KubeObjectStore { api = daemonSetApi; - @observable metrics: IPodMetrics = null; - constructor() { super(); @@ -38,12 +36,6 @@ export class DaemonSetStore extends KubeObjectStore { autoBind(this); } - async loadMetrics(daemonSet: DaemonSet) { - const pods = this.getChildPods(daemonSet); - - this.metrics = await podsApi.getMetrics(pods, daemonSet.getNs(), ""); - } - getChildPods(daemonSet: DaemonSet): Pod[] { return podsStore.getPodsByOwnerId(daemonSet.getId()); } @@ -67,10 +59,6 @@ export class DaemonSetStore extends KubeObjectStore { return status; } - - reset() { - this.metrics = null; - } } export const daemonSetStore = new DaemonSetStore(); diff --git a/src/renderer/components/+workloads-deployments/deployment-details.tsx b/src/renderer/components/+workloads-deployments/deployment-details.tsx index 87f4e08378..110c9a5679 100644 --- a/src/renderer/components/+workloads-deployments/deployment-details.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-details.tsx @@ -26,7 +26,7 @@ import kebabCase from "lodash/kebabCase"; import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; -import type { Deployment } from "../../api/endpoints"; +import { Deployment, getMetricsForDeployments, IPodMetrics } from "../../api/endpoints"; import { PodDetailsTolerations } from "../+workloads-pods/pod-details-tolerations"; import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities"; import { podsStore } from "../+workloads-pods/pods.store"; @@ -34,22 +34,30 @@ import type { KubeObjectDetailsProps } from "../kube-object"; import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; import { deploymentStore } from "./deployments.store"; import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; -import { reaction } from "mobx"; +import { makeObservable, observable, reaction } from "mobx"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { replicaSetStore } from "../+workloads-replicasets/replicasets.store"; import { DeploymentReplicaSets } from "./deployment-replicasets"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; +import { boundMethod } from "../../utils"; interface Props extends KubeObjectDetailsProps { } @observer export class DeploymentDetails extends React.Component { + @observable metrics: IPodMetrics = null; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + @disposeOnUnmount clean = reaction(() => this.props.object, () => { - deploymentStore.reset(); + this.metrics = null; }); componentDidMount() { @@ -57,8 +65,11 @@ export class DeploymentDetails extends React.Component { replicaSetStore.reloadAll(); } - componentWillUnmount() { - deploymentStore.reset(); + @boundMethod + async loadMetrics() { + const { object: deployment } = this.props; + + this.metrics = await getMetricsForDeployments([deployment], deployment.getNs(), ""); } render() { @@ -70,15 +81,14 @@ export class DeploymentDetails extends React.Component { const selectors = deployment.getSelectors(); const childPods = deploymentStore.getChildPods(deployment); const replicaSets = replicaSetStore.getReplicaSetsByOwner(deployment); - const metrics = deploymentStore.metrics; const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Deployment); return (
{!isMetricHidden && podsStore.isLoaded && ( deploymentStore.loadMetrics(deployment)} - tabs={podMetricTabs} object={deployment} params={{ metrics }} + loader={this.loadMetrics} + tabs={podMetricTabs} object={deployment} params={{ metrics: this.metrics }} > @@ -132,7 +142,7 @@ export class DeploymentDetails extends React.Component { - +
diff --git a/src/renderer/components/+workloads-deployments/deployments.store.ts b/src/renderer/components/+workloads-deployments/deployments.store.ts index 8d99731d4f..96149c8745 100644 --- a/src/renderer/components/+workloads-deployments/deployments.store.ts +++ b/src/renderer/components/+workloads-deployments/deployments.store.ts @@ -18,17 +18,16 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { makeObservable } from "mobx"; -import { observable, makeObservable } from "mobx"; -import { Deployment, deploymentApi, IPodMetrics, podsApi, PodStatus } from "../../api/endpoints"; -import { KubeObjectStore } from "../../kube-object.store"; -import { autoBind } from "../../utils"; import { podsStore } from "../+workloads-pods/pods.store"; import { apiManager } from "../../api/api-manager"; +import { Deployment, deploymentApi, PodStatus } from "../../api/endpoints"; +import { KubeObjectStore } from "../../kube-object.store"; +import { autoBind } from "../../utils"; export class DeploymentStore extends KubeObjectStore { api = deploymentApi; - @observable metrics: IPodMetrics = null; constructor() { super(); @@ -43,12 +42,6 @@ export class DeploymentStore extends KubeObjectStore { ], "desc"); } - async loadMetrics(deployment: Deployment) { - const pods = this.getChildPods(deployment); - - this.metrics = await podsApi.getMetrics(pods, deployment.getNs(), ""); - } - getStatuses(deployments?: Deployment[]) { const status = { failed: 0, pending: 0, running: 0 }; @@ -74,10 +67,6 @@ export class DeploymentStore extends KubeObjectStore { .getByLabel(deployment.getTemplateLabels()) .filter(pod => pod.getNs() === deployment.getNs()); } - - reset() { - this.metrics = null; - } } export const deploymentStore = new DeploymentStore(); diff --git a/src/renderer/components/+workloads-jobs/job-details.tsx b/src/renderer/components/+workloads-jobs/job-details.tsx index 02cd5b41d4..b0ba055122 100644 --- a/src/renderer/components/+workloads-jobs/job-details.tsx +++ b/src/renderer/components/+workloads-jobs/job-details.tsx @@ -33,20 +33,40 @@ import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities" import { podsStore } from "../+workloads-pods/pods.store"; import { jobStore } from "./job.store"; import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; -import type { Job } from "../../api/endpoints"; +import { getMetricsForJobs, IPodMetrics, Job } from "../../api/endpoints"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { lookupApiLink } from "../../api/kube-api"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; +import { makeObservable, observable } from "mobx"; +import { podMetricTabs, PodCharts } from "../+workloads-pods/pod-charts"; +import { ClusterMetricsResourceType } from "../../../main/cluster"; +import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; +import { ResourceMetrics } from "../resource-metrics"; +import { boundMethod } from "autobind-decorator"; interface Props extends KubeObjectDetailsProps { } @observer export class JobDetails extends React.Component { + @observable metrics: IPodMetrics = null; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + async componentDidMount() { podsStore.reloadAll(); } + @boundMethod + async loadMetrics() { + const { object: job } = this.props; + + this.metrics = await getMetricsForJobs([job], job.getNs(), ""); + } + render() { const { object: job } = this.props; @@ -57,9 +77,18 @@ export class JobDetails extends React.Component { const childPods = jobStore.getChildPods(job); const ownerRefs = job.getOwnerRefs(); const condition = job.getCondition(); + const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Job); return (
+ {!isMetricHidden && ( + + + + )} { diff --git a/src/renderer/components/+workloads-pods/pod-details.tsx b/src/renderer/components/+workloads-pods/pod-details.tsx index 2bb08deeff..6249288b77 100644 --- a/src/renderer/components/+workloads-pods/pod-details.tsx +++ b/src/renderer/components/+workloads-pods/pod-details.tsx @@ -25,18 +25,17 @@ import React from "react"; import kebabCase from "lodash/kebabCase"; import { disposeOnUnmount, observer } from "mobx-react"; import { Link } from "react-router-dom"; -import { autorun, observable, reaction, makeObservable } from "mobx"; -import { IPodMetrics, nodesApi, Pod, pvcApi, configMapApi } from "../../api/endpoints"; +import { observable, reaction, makeObservable } from "mobx"; +import { IPodMetrics, nodesApi, Pod, pvcApi, configMapApi, getMetricsForPods } from "../../api/endpoints"; import { DrawerItem, DrawerTitle } from "../drawer"; import { Badge } from "../badge"; -import { boundMethod, cssNames, interval, toJS } from "../../utils"; +import { boundMethod, cssNames, toJS } from "../../utils"; import { PodDetailsContainer } from "./pod-details-container"; import { PodDetailsAffinities } from "./pod-details-affinities"; import { PodDetailsTolerations } from "./pod-details-tolerations"; import { Icon } from "../icon"; import { PodDetailsSecrets } from "./pod-details-secrets"; import { ResourceMetrics } from "../resource-metrics"; -import { podsStore } from "./pods.store"; import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import { getItemMetrics } from "../../api/endpoints/metrics.api"; import { PodCharts, podMetricTabs } from "./pod-charts"; @@ -49,10 +48,9 @@ interface Props extends KubeObjectDetailsProps { @observer export class PodDetails extends React.Component { + @observable metrics: IPodMetrics; @observable containerMetrics: IPodMetrics; - private watcher = interval(60, () => this.loadMetrics()); - constructor(props: Props) { super(props); makeObservable(this); @@ -60,26 +58,19 @@ export class PodDetails extends React.Component { componentDidMount() { disposeOnUnmount(this, [ - autorun(() => { - this.containerMetrics = null; - this.loadMetrics(); - }), reaction(() => this.props.object, () => { - podsStore.reset(); + this.metrics = null; + this.containerMetrics = null; }) ]); - this.watcher.start(); - } - - componentWillUnmount() { - podsStore.reset(); } @boundMethod async loadMetrics() { const { object: pod } = this.props; - this.containerMetrics = await podsStore.loadContainerMetrics(pod); + this.metrics = await getMetricsForPods([pod], pod.getNs()); + this.containerMetrics = await getMetricsForPods([pod], pod.getNs(), "container, namespace"); } render() { @@ -92,15 +83,14 @@ export class PodDetails extends React.Component { const { nodeName } = spec; const nodeSelector = pod.getNodeSelectors(); const volumes = pod.getVolumes(); - const metrics = podsStore.metrics; const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.Pod); return (
{!isMetricHidden && ( podsStore.loadMetrics(pod)} - tabs={podMetricTabs} object={pod} params={{ metrics }} + loader={this.loadMetrics} + tabs={podMetricTabs} object={pod} params={{ metrics: this.metrics }} > diff --git a/src/renderer/components/+workloads-pods/pods.store.ts b/src/renderer/components/+workloads-pods/pods.store.ts index 62a6a720af..1708690a8f 100644 --- a/src/renderer/components/+workloads-pods/pods.store.ts +++ b/src/renderer/components/+workloads-pods/pods.store.ts @@ -20,17 +20,16 @@ */ import countBy from "lodash/countBy"; -import { action, observable, makeObservable } from "mobx"; +import { observable, makeObservable } from "mobx"; import { KubeObjectStore } from "../../kube-object.store"; import { autoBind, cpuUnitsToNumber, unitsToBytes } from "../../utils"; -import { IPodMetrics, Pod, PodMetrics, podMetricsApi, podsApi } from "../../api/endpoints"; +import { Pod, PodMetrics, podMetricsApi, podsApi } from "../../api/endpoints"; import { apiManager } from "../../api/api-manager"; import type { WorkloadKubeObject } from "../../api/workload-kube-object"; export class PodsStore extends KubeObjectStore { api = podsApi; - @observable metrics: IPodMetrics = null; @observable kubeMetrics = observable.array([]); constructor() { @@ -40,15 +39,6 @@ export class PodsStore extends KubeObjectStore { autoBind(this); } - @action - async loadMetrics(pod: Pod) { - this.metrics = await podsApi.getMetrics([pod], pod.getNs()); - } - - loadContainerMetrics(pod: Pod) { - return podsApi.getMetrics([pod], pod.getNs(), "container, namespace"); - } - async loadKubeMetrics(namespace?: string) { try { this.kubeMetrics.replace(await podMetricsApi.list({ namespace })); @@ -111,10 +101,6 @@ export class PodsStore extends KubeObjectStore { }; }, empty); } - - reset() { - this.metrics = null; - } } export const podsStore = new PodsStore(); diff --git a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx index 7818be6765..7c06b56840 100644 --- a/src/renderer/components/+workloads-replicasets/replicaset-details.tsx +++ b/src/renderer/components/+workloads-replicasets/replicaset-details.tsx @@ -21,7 +21,7 @@ import "./replicaset-details.scss"; import React from "react"; -import { reaction } from "mobx"; +import { makeObservable, observable, reaction } from "mobx"; import { DrawerItem } from "../drawer"; import { Badge } from "../badge"; import { replicaSetStore } from "./replicasets.store"; @@ -31,37 +31,48 @@ import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities" import { disposeOnUnmount, observer } from "mobx-react"; import { podsStore } from "../+workloads-pods/pods.store"; import type { KubeObjectDetailsProps } from "../kube-object"; -import type { ReplicaSet } from "../../api/endpoints"; +import { getMetricsForReplicaSets, IPodMetrics, ReplicaSet } from "../../api/endpoints"; import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; +import { boundMethod } from "../../utils"; interface Props extends KubeObjectDetailsProps { } @observer export class ReplicaSetDetails extends React.Component { + @observable metrics: IPodMetrics = null; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + @disposeOnUnmount clean = reaction(() => this.props.object, () => { - replicaSetStore.reset(); + this.metrics = null; }); async componentDidMount() { podsStore.reloadAll(); } - componentWillUnmount() { - replicaSetStore.reset(); + @boundMethod + async loadMetrics() { + const { object: replicaSet } = this.props; + + this.metrics = await getMetricsForReplicaSets([replicaSet], replicaSet.getNs(), ""); } render() { const { object: replicaSet } = this.props; if (!replicaSet) return null; - const { metrics } = replicaSetStore; + const { metrics } = this; const { status } = replicaSet; const { availableReplicas, replicas } = status; const selectors = replicaSet.getSelectors(); @@ -74,7 +85,7 @@ export class ReplicaSetDetails extends React.Component {
{!isMetricHidden && podsStore.isLoaded && ( replicaSetStore.loadMetrics(replicaSet)} + loader={this.loadMetrics} tabs={podMetricTabs} object={replicaSet} params={{ metrics }} > diff --git a/src/renderer/components/+workloads-replicasets/replicasets.store.ts b/src/renderer/components/+workloads-replicasets/replicasets.store.ts index 66794bc7ad..c7d7729889 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.store.ts +++ b/src/renderer/components/+workloads-replicasets/replicasets.store.ts @@ -18,18 +18,17 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { makeObservable } from "mobx"; -import { observable, makeObservable } from "mobx"; -import { autoBind } from "../../utils"; -import { KubeObjectStore } from "../../kube-object.store"; -import { Deployment, IPodMetrics, podsApi, ReplicaSet, replicaSetApi } from "../../api/endpoints"; import { podsStore } from "../+workloads-pods/pods.store"; import { apiManager } from "../../api/api-manager"; +import { Deployment, ReplicaSet, replicaSetApi } from "../../api/endpoints"; import { PodStatus } from "../../api/endpoints/pods.api"; +import { KubeObjectStore } from "../../kube-object.store"; +import { autoBind } from "../../utils"; export class ReplicaSetStore extends KubeObjectStore { api = replicaSetApi; - @observable metrics: IPodMetrics = null; constructor() { super(); @@ -38,12 +37,6 @@ export class ReplicaSetStore extends KubeObjectStore { autoBind(this); } - async loadMetrics(replicaSet: ReplicaSet) { - const pods = this.getChildPods(replicaSet); - - this.metrics = await podsApi.getMetrics(pods, replicaSet.getNs(), ""); - } - getChildPods(replicaSet: ReplicaSet) { return podsStore.getPodsByOwnerId(replicaSet.getId()); } @@ -73,10 +66,6 @@ export class ReplicaSetStore extends KubeObjectStore { !!replicaSet.getOwnerRefs().find(owner => owner.uid === deployment.getId()) ); } - - reset() { - this.metrics = null; - } } export const replicaSetStore = new ReplicaSetStore(); diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx index dafe8c16b6..f16578c703 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulset-details.tsx @@ -23,7 +23,7 @@ import "./statefulset-details.scss"; import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; -import { reaction } from "mobx"; +import { makeObservable, observable, reaction } from "mobx"; import { Badge } from "../badge"; import { DrawerItem } from "../drawer"; import { PodDetailsStatuses } from "../+workloads-pods/pod-details-statuses"; @@ -32,30 +32,41 @@ import { PodDetailsAffinities } from "../+workloads-pods/pod-details-affinities" import { podsStore } from "../+workloads-pods/pods.store"; import { statefulSetStore } from "./statefulset.store"; import type { KubeObjectDetailsProps } from "../kube-object"; -import type { StatefulSet } from "../../api/endpoints"; +import { getMetricsForStatefulSets, IPodMetrics, StatefulSet } from "../../api/endpoints"; import { ResourceMetrics, ResourceMetricsText } from "../resource-metrics"; import { PodCharts, podMetricTabs } from "../+workloads-pods/pod-charts"; import { PodDetailsList } from "../+workloads-pods/pod-details-list"; import { KubeObjectMeta } from "../kube-object/kube-object-meta"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../main/cluster"; +import { boundMethod } from "../../utils"; interface Props extends KubeObjectDetailsProps { } @observer export class StatefulSetDetails extends React.Component { + @observable metrics: IPodMetrics = null; + + constructor(props: Props) { + super(props); + makeObservable(this); + } + @disposeOnUnmount clean = reaction(() => this.props.object, () => { - statefulSetStore.reset(); + this.metrics = null; }); componentDidMount() { podsStore.reloadAll(); } - componentWillUnmount() { - statefulSetStore.reset(); + @boundMethod + async loadMetrics() { + const { object: statefulSet } = this.props; + + this.metrics = await getMetricsForStatefulSets([statefulSet], statefulSet.getNs(), ""); } render() { @@ -66,15 +77,14 @@ export class StatefulSetDetails extends React.Component { const selectors = statefulSet.getSelectors(); const nodeSelector = statefulSet.getNodeSelectors(); const childPods = statefulSetStore.getChildPods(statefulSet); - const metrics = statefulSetStore.metrics; const isMetricHidden = getActiveClusterEntity()?.isMetricHidden(ClusterMetricsResourceType.StatefulSet); return (
{!isMetricHidden && podsStore.isLoaded && ( statefulSetStore.loadMetrics(statefulSet)} - tabs={podMetricTabs} object={statefulSet} params={{ metrics }} + loader={() => this.loadMetrics} + tabs={podMetricTabs} object={statefulSet} params={{ metrics: this.metrics }} > @@ -108,7 +118,7 @@ export class StatefulSetDetails extends React.Component { - +
); diff --git a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts index 572e39447a..c1908c01a5 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulset.store.ts +++ b/src/renderer/components/+workloads-statefulsets/statefulset.store.ts @@ -18,17 +18,16 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { makeObservable } from "mobx"; -import { observable, makeObservable } from "mobx"; -import { autoBind } from "../../utils"; -import { KubeObjectStore } from "../../kube-object.store"; -import { IPodMetrics, podsApi, PodStatus, StatefulSet, statefulSetApi } from "../../api/endpoints"; import { podsStore } from "../+workloads-pods/pods.store"; import { apiManager } from "../../api/api-manager"; +import { PodStatus, StatefulSet, statefulSetApi } from "../../api/endpoints"; +import { KubeObjectStore } from "../../kube-object.store"; +import { autoBind } from "../../utils"; export class StatefulSetStore extends KubeObjectStore { api = statefulSetApi; - @observable metrics: IPodMetrics = null; constructor() { super(); @@ -37,13 +36,6 @@ export class StatefulSetStore extends KubeObjectStore { autoBind(this); } - - async loadMetrics(statefulSet: StatefulSet) { - const pods = this.getChildPods(statefulSet); - - this.metrics = await podsApi.getMetrics(pods, statefulSet.getNs(), ""); - } - getChildPods(statefulSet: StatefulSet) { return podsStore.getPodsByOwnerId(statefulSet.getId()); } @@ -67,10 +59,6 @@ export class StatefulSetStore extends KubeObjectStore { return status; } - - reset() { - this.metrics = null; - } } export const statefulSetStore = new StatefulSetStore(); From 64ac04186b80a7426f10950509000ebaa6679770 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 22 Jul 2021 10:42:27 -0400 Subject: [PATCH 103/131] Improve Table typing and remove annotations on callbacks (#2981) Signed-off-by: Sebastian Malton --- .../api/__tests__/api-manager.test.ts | 4 +- src/renderer/api/__tests__/crd.test.ts | 10 ++-- src/renderer/api/__tests__/kube-api.test.ts | 15 +++-- src/renderer/api/api-manager.ts | 27 +++++---- .../api/endpoints/resource-applier.api.ts | 4 +- src/renderer/api/kube-api.ts | 24 ++++---- src/renderer/api/kube-object.ts | 16 ++--- src/renderer/api/kube-watch-api.ts | 9 +-- .../+apps-helm-charts/helm-charts.tsx | 14 ++--- .../components/+apps-releases/releases.tsx | 28 ++++----- src/renderer/components/+catalog/catalog.tsx | 26 ++++---- .../components/+config-autoscalers/hpa.tsx | 16 ++--- .../+config-limit-ranges/limit-ranges.tsx | 15 +++-- .../components/+config-maps/config-maps.tsx | 15 +++-- .../pod-disruption-budgets.tsx | 18 +++--- .../resource-quotas.tsx | 13 ++-- .../+config-secrets/add-secret-dialog.tsx | 4 +- .../components/+config-secrets/secrets.tsx | 19 +++--- .../components/+custom-resources/crd-list.tsx | 13 ++-- .../+custom-resources/crd-resource.store.ts | 6 +- .../+custom-resources/crd-resources.tsx | 16 ++--- .../components/+custom-resources/crd.store.ts | 9 ++- src/renderer/components/+events/events.tsx | 40 ++++++------- .../components/+namespaces/namespace.store.ts | 2 +- .../components/+namespaces/namespaces.tsx | 18 +++--- .../+network-endpoints/endpoints.tsx | 13 ++-- .../+network-ingresses/ingresses.tsx | 15 +++-- .../+network-policies/network-policies.tsx | 11 ++-- .../components/+network-services/services.tsx | 25 ++++---- src/renderer/components/+nodes/nodes.tsx | 28 ++++----- .../pod-security-policies.tsx | 17 +++--- .../+storage-classes/storage-classes.tsx | 15 +++-- .../+storage-volume-claims/volume-claims.tsx | 21 ++++--- .../components/+storage-volumes/volumes.tsx | 17 +++--- .../+cluster-role-bindings/view.tsx | 13 ++-- .../+user-management/+cluster-roles/view.tsx | 9 ++- .../+user-management/+role-bindings/view.tsx | 15 +++-- .../+user-management/+roles/view.tsx | 11 ++-- .../+service-accounts/view.tsx | 10 ++-- .../cronjob-trigger-dialog.tsx | 4 +- .../+workloads-cronjobs/cronjobs.tsx | 18 +++--- .../+workloads-daemonsets/daemonsets.tsx | 14 ++--- .../+workloads-deployments/deployments.tsx | 20 +++---- .../components/+workloads-jobs/jobs.tsx | 13 ++-- .../+workloads-overview/overview-statuses.tsx | 7 ++- .../+workloads-pods/pod-tolerations.tsx | 19 +++--- .../components/+workloads-pods/pods.tsx | 28 ++++----- .../+workloads-replicasets/replicasets.tsx | 16 ++--- .../+workloads-statefulsets/statefulsets.tsx | 16 +++-- .../components/+workloads/workloads.stores.ts | 19 +++--- .../components/dock/edit-resource.store.ts | 2 +- .../item-object-list/item-list-layout.tsx | 59 ++++++++++--------- .../kube-object/kube-object-details.tsx | 10 +--- .../kube-object/kube-object-list-layout.tsx | 14 ++--- .../kube-object/kube-object-meta.tsx | 8 +-- src/renderer/components/table/table.tsx | 35 +++++++---- .../kube-object-detail-registry.tsx | 10 +++- src/renderer/item.store.ts | 42 ++++++------- src/renderer/kube-object.store.ts | 14 ++--- 59 files changed, 487 insertions(+), 482 deletions(-) diff --git a/src/renderer/api/__tests__/api-manager.test.ts b/src/renderer/api/__tests__/api-manager.test.ts index 8c7beb12c9..83034d20b6 100644 --- a/src/renderer/api/__tests__/api-manager.test.ts +++ b/src/renderer/api/__tests__/api-manager.test.ts @@ -22,8 +22,9 @@ import { ingressStore } from "../../components/+network-ingresses/ingress.store"; import { apiManager } from "../api-manager"; import { KubeApi } from "../kube-api"; +import { KubeObject } from "../kube-object"; -class TestApi extends KubeApi { +class TestApi extends KubeApi { protected async checkPreferredVersion() { return; @@ -36,6 +37,7 @@ describe("ApiManager", () => { const apiBase = "apis/v1/foo"; const fallbackApiBase = "/apis/extensions/v1beta1/foo"; const kubeApi = new TestApi({ + objectConstructor: KubeObject, apiBase, fallbackApiBases: [fallbackApiBase], checkPreferredVersion: true, diff --git a/src/renderer/api/__tests__/crd.test.ts b/src/renderer/api/__tests__/crd.test.ts index af7db4280c..459a7f67d9 100644 --- a/src/renderer/api/__tests__/crd.test.ts +++ b/src/renderer/api/__tests__/crd.test.ts @@ -20,7 +20,7 @@ */ import { CustomResourceDefinition } from "../endpoints"; -import type { IKubeObjectMetadata } from "../kube-object"; +import type { KubeObjectMetadata } from "../kube-object"; describe("Crds", () => { describe("getVersion", () => { @@ -28,7 +28,7 @@ describe("Crds", () => { const crd = new CustomResourceDefinition({ apiVersion: "foo", kind: "CustomResourceDefinition", - metadata: {} as IKubeObjectMetadata, + metadata: {} as KubeObjectMetadata, }); crd.spec = { @@ -48,7 +48,7 @@ describe("Crds", () => { const crd = new CustomResourceDefinition({ apiVersion: "foo", kind: "CustomResourceDefinition", - metadata: {} as IKubeObjectMetadata, + metadata: {} as KubeObjectMetadata, }); crd.spec = { @@ -73,7 +73,7 @@ describe("Crds", () => { const crd = new CustomResourceDefinition({ apiVersion: "foo", kind: "CustomResourceDefinition", - metadata: {} as IKubeObjectMetadata, + metadata: {} as KubeObjectMetadata, }); crd.spec = { @@ -99,7 +99,7 @@ describe("Crds", () => { const crd = new CustomResourceDefinition({ apiVersion: "foo", kind: "CustomResourceDefinition", - metadata: {} as IKubeObjectMetadata, + metadata: {} as KubeObjectMetadata, }); crd.spec = { diff --git a/src/renderer/api/__tests__/kube-api.test.ts b/src/renderer/api/__tests__/kube-api.test.ts index c7415cba73..1eb0199f89 100644 --- a/src/renderer/api/__tests__/kube-api.test.ts +++ b/src/renderer/api/__tests__/kube-api.test.ts @@ -20,6 +20,7 @@ */ import { KubeApi } from "../kube-api"; +import { KubeObject } from "../kube-object"; describe("KubeApi", () => { it("uses url from apiBase if apiBase contains the resource", async () => { @@ -29,7 +30,7 @@ describe("KubeApi", () => { body: JSON.stringify({ resources: [{ name: "ingresses" - }] as any [] + }] as any[] }) }; } else if (request.url === "/api-kube/apis/extensions/v1beta1") { @@ -38,13 +39,13 @@ describe("KubeApi", () => { body: JSON.stringify({ resources: [{ name: "ingresses" - }] as any [] + }] as any[] }) }; } else { return { body: JSON.stringify({ - resources: [] as any [] + resources: [] as any[] }) }; } @@ -53,6 +54,7 @@ describe("KubeApi", () => { const apiBase = "/apis/networking.k8s.io/v1/ingresses"; const fallbackApiBase = "/apis/extensions/v1beta1/ingresses"; const kubeApi = new KubeApi({ + objectConstructor: KubeObject, apiBase, fallbackApiBases: [fallbackApiBase], checkPreferredVersion: true, @@ -68,7 +70,7 @@ describe("KubeApi", () => { if (request.url === "/api-kube/apis/networking.k8s.io/v1") { return { body: JSON.stringify({ - resources: [] as any [] + resources: [] as any[] }) }; } else if (request.url === "/api-kube/apis/extensions/v1beta1") { @@ -76,13 +78,13 @@ describe("KubeApi", () => { body: JSON.stringify({ resources: [{ name: "ingresses" - }] as any [] + }] as any[] }) }; } else { return { body: JSON.stringify({ - resources: [] as any [] + resources: [] as any[] }) }; } @@ -91,6 +93,7 @@ describe("KubeApi", () => { const apiBase = "apis/networking.k8s.io/v1/ingresses"; const fallbackApiBase = "/apis/extensions/v1beta1/ingresses"; const kubeApi = new KubeApi({ + objectConstructor: KubeObject, apiBase, fallbackApiBases: [fallbackApiBase], checkPreferredVersion: true, diff --git a/src/renderer/api/api-manager.ts b/src/renderer/api/api-manager.ts index 9477446386..1e01bd4cfb 100644 --- a/src/renderer/api/api-manager.ts +++ b/src/renderer/api/api-manager.ts @@ -22,34 +22,35 @@ import type { KubeObjectStore } from "../kube-object.store"; import { action, observable, makeObservable } from "mobx"; -import { autoBind } from "../utils"; +import { autoBind, iter } from "../utils"; import { KubeApi, parseKubeApi } from "./kube-api"; +import type { KubeObject } from "./kube-object"; export class ApiManager { - private apis = observable.map(); - private stores = observable.map(); + private apis = observable.map>(); + private stores = observable.map>(); constructor() { makeObservable(this); autoBind(this); } - getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { + getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { if (typeof pathOrCallback === "string") { return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase); } - return Array.from(this.apis.values()).find(pathOrCallback ?? (() => true)); + return iter.find(this.apis.values(), pathOrCallback ?? (() => true)); } getApiByKind(kind: string, apiVersion: string) { - return Array.from(this.apis.values()).find((api) => api.kind === kind && api.apiVersionWithGroup === apiVersion); + return iter.find(this.apis.values(), api => api.kind === kind && api.apiVersionWithGroup === apiVersion); } - registerApi(apiBase: string, api: KubeApi) { + registerApi(apiBase: string, api: KubeApi) { if (!this.apis.has(apiBase)) { this.stores.forEach((store) => { - if(store.api === api) { + if (store.api === api) { this.stores.set(apiBase, store); } }); @@ -58,19 +59,19 @@ export class ApiManager { } } - protected resolveApi(api?: string | KubeApi): KubeApi | undefined { + protected resolveApi(api?: string | KubeApi): KubeApi | undefined { if (!api) { return undefined; } if (typeof api === "string") { - return this.getApi(api); + return this.getApi(api) as KubeApi; } return api; } - unregisterApi(api: string | KubeApi) { + unregisterApi(api: string | KubeApi) { if (typeof api === "string") this.apis.delete(api); else { const apis = Array.from(this.apis.entries()); @@ -81,13 +82,13 @@ export class ApiManager { } @action - registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) { + registerStore(store: KubeObjectStore, apis: KubeApi[] = [store.api]) { apis.forEach(api => { this.stores.set(api.apiBase, store); }); } - getStore(api: string | KubeApi): S { + getStore>(api: string | KubeApi): S | undefined { return this.stores.get(this.resolveApi(api)?.apiBase) as S; } } diff --git a/src/renderer/api/endpoints/resource-applier.api.ts b/src/renderer/api/endpoints/resource-applier.api.ts index 1756977c95..b10c10f7db 100644 --- a/src/renderer/api/endpoints/resource-applier.api.ts +++ b/src/renderer/api/endpoints/resource-applier.api.ts @@ -30,7 +30,7 @@ export const resourceApplierApi = { "kubectl.kubernetes.io/last-applied-configuration" ], - async update(resource: object | string): Promise { + async update(resource: object | string): Promise { if (typeof resource === "string") { resource = jsYaml.safeLoad(resource); } @@ -48,7 +48,7 @@ export const resourceApplierApi = { } }); - return items.length === 1 ? items[0] : items; + return items[0] as K ?? null; }); } }; diff --git a/src/renderer/api/kube-api.ts b/src/renderer/api/kube-api.ts index 21e370a207..20c4672727 100644 --- a/src/renderer/api/kube-api.ts +++ b/src/renderer/api/kube-api.ts @@ -28,7 +28,7 @@ import logger from "../../main/logger"; import { apiManager } from "./api-manager"; import { apiKube } from "./index"; import { createKubeApiURL, parseKubeApi } from "./kube-api-parse"; -import { IKubeObjectConstructor, KubeObject, KubeStatus } from "./kube-object"; +import { KubeObjectConstructor, KubeObject, KubeStatus } from "./kube-object"; import byline from "byline"; import type { IKubeWatchEvent } from "./kube-watch-api"; import { ReadableWebToNodeStream } from "../utils/readableStream"; @@ -49,7 +49,7 @@ export interface IKubeApiOptions { */ fallbackApiBases?: string[]; - objectConstructor?: IKubeObjectConstructor; + objectConstructor: KubeObjectConstructor; request?: KubeJsonApi; isNamespaced?: boolean; kind?: string; @@ -99,7 +99,7 @@ export interface IKubeApiCluster { } } -export function forCluster(cluster: IKubeApiCluster, kubeClass: IKubeObjectConstructor): KubeApi { +export function forCluster(cluster: IKubeApiCluster, kubeClass: KubeObjectConstructor): KubeApi { const request = new KubeJsonApi({ apiBase: apiKubePrefix, debug: isDevelopment, @@ -115,7 +115,7 @@ export function forCluster(cluster: IKubeApiCluster, kubeC }); } -export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { +export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { if (!object.metadata.selfLink) { object.metadata.selfLink = createKubeApiURL({ apiPrefix: api.apiPrefix, @@ -127,7 +127,7 @@ export function ensureObjectSelfLink(api: KubeApi, object: KubeJsonApiData) { } } -export type KubeApiWatchCallback = (data: IKubeWatchEvent, error: any) => void; +export type KubeApiWatchCallback = (data: IKubeWatchEvent, error: any) => void; export type KubeApiWatchOptions = { namespace: string; @@ -135,7 +135,7 @@ export type KubeApiWatchOptions = { abortController?: AbortController }; -export class KubeApi { +export class KubeApi { readonly kind: string; readonly apiBase: string; readonly apiPrefix: string; @@ -145,14 +145,14 @@ export class KubeApi { readonly apiResource: string; readonly isNamespaced: boolean; - public objectConstructor: IKubeObjectConstructor; + public objectConstructor: KubeObjectConstructor; protected request: KubeJsonApi; protected resourceVersions = new Map(); protected watchDisposer: () => void; constructor(protected options: IKubeApiOptions) { const { - objectConstructor = KubeObject as IKubeObjectConstructor, + objectConstructor, request = apiKube, kind = options.objectConstructor?.kind, isNamespaced = options.objectConstructor?.namespaced @@ -456,14 +456,14 @@ export class KubeApi { clearTimeout(timedRetry); timedRetry = setTimeout(() => { // we did not get any kubernetes errors so let's retry - this.watch({...opts, namespace, callback}); + this.watch({ ...opts, namespace, callback }); }, 1000); }); }); byline(nodeStream).on("data", (line) => { try { - const event: IKubeWatchEvent = JSON.parse(line); + const event: IKubeWatchEvent = JSON.parse(line); if (event.type === "ERROR" && event.object.kind === "Status") { errorReceived = true; @@ -474,7 +474,7 @@ export class KubeApi { this.modifyWatchEvent(event); callback(event, null); } catch (ignore) { - // ignore parse errors + // ignore parse errors } }); }) @@ -487,7 +487,7 @@ export class KubeApi { return abort; } - protected modifyWatchEvent(event: IKubeWatchEvent) { + protected modifyWatchEvent(event: IKubeWatchEvent) { switch (event.type) { case "ADDED": diff --git a/src/renderer/api/kube-object.ts b/src/renderer/api/kube-object.ts index 024651c4d9..2548cac3aa 100644 --- a/src/renderer/api/kube-object.ts +++ b/src/renderer/api/kube-object.ts @@ -31,13 +31,13 @@ import { resourceApplierApi } from "./endpoints/resource-applier.api"; import { hasOptionalProperty, hasTypedProperty, isObject, isString, bindPredicate, isTypedArray, isRecord } from "../../common/utils/type-narrowing"; import _ from "lodash"; -export type IKubeObjectConstructor = (new (data: KubeJsonApiData | any) => T) & { +export type KubeObjectConstructor = (new (data: KubeJsonApiData | any) => K) & { kind?: string; namespaced?: boolean; apiBase?: string; }; -export interface IKubeObjectMetadata { +export interface KubeObjectMetadata { uid: string; name: string; namespace?: string; @@ -63,7 +63,7 @@ export interface IKubeObjectMetadata { }[]; } -export interface IKubeStatus { +export interface KubeStatusData { kind: string; apiVersion: string; code: number; @@ -78,7 +78,7 @@ export class KubeStatus { public readonly message: string; public readonly reason: string; - constructor(data: IKubeStatus) { + constructor(data: KubeStatusData) { this.apiVersion = data.apiVersion; this.code = data.code; this.message = data.message || ""; @@ -86,9 +86,9 @@ export class KubeStatus { } } -export type IKubeMetaField = keyof IKubeObjectMetadata; +export type KubeMetaField = keyof KubeObjectMetadata; -export class KubeObject implements ItemObject { +export class KubeObject implements ItemObject { static readonly kind: string; static readonly namespaced: boolean; @@ -277,14 +277,14 @@ export class KubeObject(data: Partial): Promise { + async update(data: Partial): Promise { for (const field of KubeObject.nonEditableFields) { if (!_.isEqual(_.get(this, field), _.get(data, field))) { throw new Error(`Failed to update Kube Object: ${field} has been modified`); } } - return resourceApplierApi.update({ + return resourceApplierApi.update({ ...this.toPlainObject(), ...data, }); diff --git a/src/renderer/api/kube-watch-api.ts b/src/renderer/api/kube-watch-api.ts index 1a0f7e0b17..4d316b5caa 100644 --- a/src/renderer/api/kube-watch-api.ts +++ b/src/renderer/api/kube-watch-api.ts @@ -31,8 +31,9 @@ import { autoBind, Disposer, noop } from "../utils"; import type { KubeApi } from "./kube-api"; import type { KubeJsonApiData } from "./kube-json-api"; import { isDebugging, isProduction } from "../../common/vars"; +import type { KubeObject } from "./kube-object"; -export interface IKubeWatchEvent { +export interface IKubeWatchEvent { type: "ADDED" | "MODIFIED" | "DELETED" | "ERROR"; object?: T; } @@ -58,11 +59,11 @@ export class KubeWatchApi { autoBind(this); } - isAllowedApi(api: KubeApi): boolean { + isAllowedApi(api: KubeApi): boolean { return Boolean(this.context?.cluster.isAllowedResource(api.kind)); } - preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) { + preloadStores(stores: KubeObjectStore[], opts: { namespaces?: string[], loadOnce?: boolean } = {}) { const limitRequests = plimit(1); // load stores one by one to allow quick skipping when fast clicking btw pages const preloading: Promise[] = []; @@ -80,7 +81,7 @@ export class KubeWatchApi { }; } - subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): Disposer { + subscribeStores(stores: KubeObjectStore[], opts: IKubeWatchSubscribeStoreOptions = {}): Disposer { const { preload = true, waitUntilLoaded = true, loadOnce = false, } = opts; const subscribingNamespaces = opts.namespaces ?? this.context?.allNamespaces ?? []; const unsubscribeList: Function[] = []; diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index 455c95e7e8..1fc1dbf7b0 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -83,14 +83,14 @@ export class HelmCharts extends Component { store={helmChartStore} isSelectable={false} sortingCallbacks={{ - [columnId.name]: (chart: HelmChart) => chart.getName(), - [columnId.repo]: (chart: HelmChart) => chart.getRepository(), + [columnId.name]: chart => chart.getName(), + [columnId.repo]: chart => chart.getRepository(), }} searchFilters={[ - (chart: HelmChart) => chart.getName(), - (chart: HelmChart) => chart.getVersion(), - (chart: HelmChart) => chart.getAppVersion(), - (chart: HelmChart) => chart.getKeywords(), + chart => chart.getName(), + chart => chart.getVersion(), + chart => chart.getAppVersion(), + chart => chart.getKeywords(), ]} customizeHeader={({ searchProps }) => ({ searchProps: { @@ -106,7 +106,7 @@ export class HelmCharts extends Component { { title: "App Version", className: "app-version", id: columnId.appVersion }, { title: "Repository", className: "repository", sortBy: columnId.repo, id: columnId.repo }, ]} - renderTableContents={(chart: HelmChart) => [ + renderTableContents={chart => [
{ store={releaseStore} dependentStores={[secretsStore]} sortingCallbacks={{ - [columnId.name]: (release: HelmRelease) => release.getName(), - [columnId.namespace]: (release: HelmRelease) => release.getNs(), - [columnId.revision]: (release: HelmRelease) => release.getRevision(), - [columnId.chart]: (release: HelmRelease) => release.getChart(), - [columnId.status]: (release: HelmRelease) => release.getStatus(), - [columnId.updated]: (release: HelmRelease) => release.getUpdated(false, false), + [columnId.name]: release => release.getName(), + [columnId.namespace]: release => release.getNs(), + [columnId.revision]: release => release.getRevision(), + [columnId.chart]: release => release.getChart(), + [columnId.status]: release => release.getStatus(), + [columnId.updated]: release => release.getUpdated(false, false), }} searchFilters={[ - (release: HelmRelease) => release.getName(), - (release: HelmRelease) => release.getNs(), - (release: HelmRelease) => release.getChart(), - (release: HelmRelease) => release.getStatus(), - (release: HelmRelease) => release.getVersion(), + release => release.getName(), + release => release.getNs(), + release => release.getChart(), + release => release.getStatus(), + release => release.getVersion(), ]} customizeHeader={({ filters, searchProps, ...headerPlaceholders }) => ({ filters: ( @@ -149,7 +149,7 @@ export class HelmReleases extends Component { { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, { title: "Updated", className: "updated", sortBy: columnId.updated, id: columnId.updated }, ]} - renderTableContents={(release: HelmRelease) => [ + renderTableContents={release => [ release.getName(), release.getNs(), release.getChart(), @@ -159,13 +159,13 @@ export class HelmReleases extends Component { { title: release.getStatus(), className: kebabCase(release.getStatus()) }, release.getUpdated(), ]} - renderItemMenu={(release: HelmRelease) => ( + renderItemMenu={release => ( )} - customizeRemoveDialog={(selectedItems: HelmRelease[]) => ({ + customizeRemoveDialog={selectedItems => ({ message: this.renderRemoveDialogMessage(selectedItems) })} detailsItem={this.selectedRelease} diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index e70a8d5f47..4256170649 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -44,7 +44,6 @@ import { CatalogMenu } from "./catalog-menu"; import { HotbarIcon } from "../hotbar/hotbar-icon"; import { RenderDelay } from "../render-delay/render-delay"; import { CatalogTopbar } from "../cluster-manager/catalog-topbar"; -import type { TableSortCallback } from "../table"; export const previousActiveTab = createAppStorage("catalog-previous-active-tab", ""); @@ -168,7 +167,7 @@ export class Catalog extends React.Component { )) } - this.addToHotbar(item) }> + this.addToHotbar(item)}> Pin to Hotbar @@ -194,16 +193,6 @@ export class Catalog extends React.Component { renderList() { const { activeCategory } = this.catalogEntityStore; const tableId = activeCategory ? `catalog-items-${activeCategory.metadata.name.replace(" ", "")}` : "catalog-items"; - let sortingCallbacks: { [sortBy: string]: TableSortCallback } = { - [sortBy.name]: (item: CatalogEntityItem) => item.name, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, - }; - - sortingCallbacks = activeCategory ? sortingCallbacks : { - ...sortingCallbacks, - [sortBy.kind]: (item: CatalogEntityItem) => item.kind, - }; if (this.activeTab === undefined) { return null; @@ -217,9 +206,14 @@ export class Catalog extends React.Component { isConfigurable={true} className="CatalogItemList" store={this.catalogEntityStore} - sortingCallbacks={sortingCallbacks} + sortingCallbacks={{ + [sortBy.name]: item => item.name, + [sortBy.source]: item => item.source, + [sortBy.status]: item => item.phase, + [sortBy.kind]: item => item.kind, + }} searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, + entity => entity.searchFields, ]} renderTableHeader={[ { title: "", className: css.iconCell, id: "icon" }, @@ -229,10 +223,10 @@ export class Catalog extends React.Component { { title: "Labels", className: css.labelsCell, id: "labels" }, { title: "Status", className: css.statusCell, sortBy: sortBy.status, id: "status" }, ].filter(Boolean)} - customizeTableRowProps={(item: CatalogEntityItem) => ({ + customizeTableRowProps={item => ({ disabled: !item.enabled, })} - renderTableContents={(item: CatalogEntityItem) => [ + renderTableContents={item => [ this.renderIcon(item), item.name, !activeCategory && item.kind, diff --git a/src/renderer/components/+config-autoscalers/hpa.tsx b/src/renderer/components/+config-autoscalers/hpa.tsx index 539bbb18d7..35bf3d6375 100644 --- a/src/renderer/components/+config-autoscalers/hpa.tsx +++ b/src/renderer/components/+config-autoscalers/hpa.tsx @@ -64,15 +64,15 @@ export class HorizontalPodAutoscalers extends React.Component { tableId="configuration_hpa" className="HorizontalPodAutoscalers" store={hpaStore} sortingCallbacks={{ - [columnId.name]: (item: HorizontalPodAutoscaler) => item.getName(), - [columnId.namespace]: (item: HorizontalPodAutoscaler) => item.getNs(), - [columnId.minPods]: (item: HorizontalPodAutoscaler) => item.getMinPods(), - [columnId.maxPods]: (item: HorizontalPodAutoscaler) => item.getMaxPods(), - [columnId.replicas]: (item: HorizontalPodAutoscaler) => item.getReplicas(), - [columnId.age]: (item: HorizontalPodAutoscaler) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.minPods]: item => item.getMinPods(), + [columnId.maxPods]: item => item.getMaxPods(), + [columnId.replicas]: item => item.getReplicas(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: HorizontalPodAutoscaler) => item.getSearchFields() + item => item.getSearchFields() ]} renderHeaderTitle="Horizontal Pod Autoscalers" renderTableHeader={[ @@ -86,7 +86,7 @@ export class HorizontalPodAutoscalers extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", id: columnId.status }, ]} - renderTableContents={(hpa: HorizontalPodAutoscaler) => [ + renderTableContents={hpa => [ hpa.getName(), , hpa.getNs(), diff --git a/src/renderer/components/+config-limit-ranges/limit-ranges.tsx b/src/renderer/components/+config-limit-ranges/limit-ranges.tsx index 664654814c..e0bec106aa 100644 --- a/src/renderer/components/+config-limit-ranges/limit-ranges.tsx +++ b/src/renderer/components/+config-limit-ranges/limit-ranges.tsx @@ -27,7 +27,6 @@ import { KubeObjectListLayout } from "../kube-object/kube-object-list-layout"; import { limitRangeStore } from "./limit-ranges.store"; import React from "react"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; -import type { LimitRange } from "../../api/endpoints/limit-range.api"; import type { LimitRangeRouteParams } from "../../../common/routes"; enum columnId { @@ -49,22 +48,22 @@ export class LimitRanges extends React.Component { className="LimitRanges" store={limitRangeStore} sortingCallbacks={{ - [columnId.name]: (item: LimitRange) => item.getName(), - [columnId.namespace]: (item: LimitRange) => item.getNs(), - [columnId.age]: (item: LimitRange) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: LimitRange) => item.getName(), - (item: LimitRange) => item.getNs(), + item => item.getName(), + item => item.getNs(), ]} - renderHeaderTitle={"Limit Ranges"} + renderHeaderTitle="Limit Ranges" renderTableHeader={[ { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, { className: "warning", showWithColumn: columnId.name }, { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(limitRange: LimitRange) => [ + renderTableContents={limitRange => [ limitRange.getName(), , limitRange.getNs(), diff --git a/src/renderer/components/+config-maps/config-maps.tsx b/src/renderer/components/+config-maps/config-maps.tsx index fb757a69bf..b9fecc9fc9 100644 --- a/src/renderer/components/+config-maps/config-maps.tsx +++ b/src/renderer/components/+config-maps/config-maps.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import { configMapsStore } from "./config-maps.store"; -import type { ConfigMap } from "../../api/endpoints/configmap.api"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; import type { ConfigMapsRouteParams } from "../../../common/routes"; @@ -49,14 +48,14 @@ export class ConfigMaps extends React.Component { tableId="configuration_configmaps" className="ConfigMaps" store={configMapsStore} sortingCallbacks={{ - [columnId.name]: (item: ConfigMap) => item.getName(), - [columnId.namespace]: (item: ConfigMap) => item.getNs(), - [columnId.keys]: (item: ConfigMap) => item.getKeys(), - [columnId.age]: (item: ConfigMap) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.keys]: item => item.getKeys(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: ConfigMap) => item.getSearchFields(), - (item: ConfigMap) => item.getKeys() + item => item.getSearchFields(), + item => item.getKeys() ]} renderHeaderTitle="Config Maps" renderTableHeader={[ @@ -66,7 +65,7 @@ export class ConfigMaps extends React.Component { { title: "Keys", className: "keys", sortBy: columnId.keys, id: columnId.keys }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(configMap: ConfigMap) => [ + renderTableContents={configMap => [ configMap.getName(), , configMap.getNs(), diff --git a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx index abc87a6a4d..f662c96ab3 100644 --- a/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx +++ b/src/renderer/components/+config-pod-disruption-budgets/pod-disruption-budgets.tsx @@ -51,16 +51,16 @@ export class PodDisruptionBudgets extends React.Component { className="PodDisruptionBudgets" store={podDisruptionBudgetsStore} sortingCallbacks={{ - [columnId.name]: (pdb: PodDisruptionBudget) => pdb.getName(), - [columnId.namespace]: (pdb: PodDisruptionBudget) => pdb.getNs(), - [columnId.minAvailable]: (pdb: PodDisruptionBudget) => pdb.getMinAvailable(), - [columnId.maxUnavailable]: (pdb: PodDisruptionBudget) => pdb.getMaxUnavailable(), - [columnId.currentHealthy]: (pdb: PodDisruptionBudget) => pdb.getCurrentHealthy(), - [columnId.desiredHealthy]: (pdb: PodDisruptionBudget) => pdb.getDesiredHealthy(), - [columnId.age]: (pdb: PodDisruptionBudget) => pdb.getAge(), + [columnId.name]: pdb => pdb.getName(), + [columnId.namespace]: pdb => pdb.getNs(), + [columnId.minAvailable]: pdb => pdb.getMinAvailable(), + [columnId.maxUnavailable]: pdb => pdb.getMaxUnavailable(), + [columnId.currentHealthy]: pdb => pdb.getCurrentHealthy(), + [columnId.desiredHealthy]: pdb => pdb.getDesiredHealthy(), + [columnId.age]: pdb => pdb.getAge(), }} searchFilters={[ - (pdb: PodDisruptionBudget) => pdb.getSearchFields(), + pdb => pdb.getSearchFields(), ]} renderHeaderTitle="Pod Disruption Budgets" renderTableHeader={[ @@ -73,7 +73,7 @@ export class PodDisruptionBudgets extends React.Component { { title: "Desired Healthy", className: "desired-healthy", sortBy: columnId.desiredHealthy, id: columnId.desiredHealthy }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(pdb: PodDisruptionBudget) => { + renderTableContents={pdb => { return [ pdb.getName(), , diff --git a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx index d1ce05c098..e1f54f2499 100644 --- a/src/renderer/components/+config-resource-quotas/resource-quotas.tsx +++ b/src/renderer/components/+config-resource-quotas/resource-quotas.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; import { KubeObjectListLayout } from "../kube-object"; -import type { ResourceQuota } from "../../api/endpoints/resource-quota.api"; import { AddQuotaDialog } from "./add-quota-dialog"; import { resourceQuotaStore } from "./resource-quotas.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -50,13 +49,13 @@ export class ResourceQuotas extends React.Component { tableId="configuration_quotas" className="ResourceQuotas" store={resourceQuotaStore} sortingCallbacks={{ - [columnId.name]: (item: ResourceQuota) => item.getName(), - [columnId.namespace]: (item: ResourceQuota) => item.getNs(), - [columnId.age]: (item: ResourceQuota) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: ResourceQuota) => item.getSearchFields(), - (item: ResourceQuota) => item.getName(), + item => item.getSearchFields(), + item => item.getName(), ]} renderHeaderTitle="Resource Quotas" renderTableHeader={[ @@ -65,7 +64,7 @@ export class ResourceQuotas extends React.Component { { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(resourceQuota: ResourceQuota) => [ + renderTableContents={resourceQuota => [ resourceQuota.getName(), , resourceQuota.getNs(), diff --git a/src/renderer/components/+config-secrets/add-secret-dialog.tsx b/src/renderer/components/+config-secrets/add-secret-dialog.tsx index 23adfaf804..5c417023e2 100644 --- a/src/renderer/components/+config-secrets/add-secret-dialog.tsx +++ b/src/renderer/components/+config-secrets/add-secret-dialog.tsx @@ -33,7 +33,7 @@ import { SubTitle } from "../layout/sub-title"; import { NamespaceSelect } from "../+namespaces/namespace-select"; import { Select, SelectOption } from "../select"; import { Icon } from "../icon"; -import type { IKubeObjectMetadata } from "../../api/kube-object"; +import type { KubeObjectMetadata } from "../../api/kube-object"; import { base64 } from "../../utils"; import { Notifications } from "../notifications"; import upperFirst from "lodash/upperFirst"; @@ -127,7 +127,7 @@ export class AddSecretDialog extends React.Component { namespace, annotations: this.getDataFromFields(annotations), labels: this.getDataFromFields(labels), - } as IKubeObjectMetadata + } as KubeObjectMetadata }; try { diff --git a/src/renderer/components/+config-secrets/secrets.tsx b/src/renderer/components/+config-secrets/secrets.tsx index 3bfeada163..f00c20d67e 100644 --- a/src/renderer/components/+config-secrets/secrets.tsx +++ b/src/renderer/components/+config-secrets/secrets.tsx @@ -24,7 +24,6 @@ import "./secrets.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; -import type { Secret } from "../../api/endpoints"; import { AddSecretDialog } from "./add-secret-dialog"; import { KubeObjectListLayout } from "../kube-object"; import { Badge } from "../badge"; @@ -54,16 +53,16 @@ export class Secrets extends React.Component { tableId="configuration_secrets" className="Secrets" store={secretsStore} sortingCallbacks={{ - [columnId.name]: (item: Secret) => item.getName(), - [columnId.namespace]: (item: Secret) => item.getNs(), - [columnId.labels]: (item: Secret) => item.getLabels(), - [columnId.keys]: (item: Secret) => item.getKeys(), - [columnId.type]: (item: Secret) => item.type, - [columnId.age]: (item: Secret) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.labels]: item => item.getLabels(), + [columnId.keys]: item => item.getKeys(), + [columnId.type]: item => item.type, + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: Secret) => item.getSearchFields(), - (item: Secret) => item.getKeys(), + item => item.getSearchFields(), + item => item.getKeys(), ]} renderHeaderTitle="Secrets" renderTableHeader={[ @@ -75,7 +74,7 @@ export class Secrets extends React.Component { { title: "Type", className: "type", sortBy: columnId.type, id: columnId.type }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(secret: Secret) => [ + renderTableContents={secret => [ secret.getName(), , secret.getNs(), diff --git a/src/renderer/components/+custom-resources/crd-list.tsx b/src/renderer/components/+custom-resources/crd-list.tsx index e8bfee160d..70ae2c96fb 100644 --- a/src/renderer/components/+custom-resources/crd-list.tsx +++ b/src/renderer/components/+custom-resources/crd-list.tsx @@ -32,6 +32,7 @@ import type { CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { Select, SelectOption } from "../select"; import { createPageParam } from "../../navigation"; import { Icon } from "../icon"; +import type { TableSortCallbacks } from "../table"; export const crdGroupsUrlParam = createPageParam({ name: "groups", @@ -78,11 +79,11 @@ export class CrdList extends React.Component { render() { const { items, selectedGroups } = this; - const sortingCallbacks = { - [columnId.kind]: (crd: CustomResourceDefinition) => crd.getResourceKind(), - [columnId.group]: (crd: CustomResourceDefinition) => crd.getGroup(), - [columnId.version]: (crd: CustomResourceDefinition) => crd.getVersion(), - [columnId.scope]: (crd: CustomResourceDefinition) => crd.getScope(), + const sortingCallbacks: TableSortCallbacks = { + [columnId.kind]: crd => crd.getResourceKind(), + [columnId.group]: crd => crd.getGroup(), + [columnId.version]: crd => crd.getVersion(), + [columnId.scope]: crd => crd.getScope(), }; return ( @@ -137,7 +138,7 @@ export class CrdList extends React.Component { { title: "Scope", className: "scope", sortBy: columnId.scope, id: columnId.scope }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(crd: CustomResourceDefinition) => [ + renderTableContents={crd => [ {crd.getResourceKind()} , diff --git a/src/renderer/components/+custom-resources/crd-resource.store.ts b/src/renderer/components/+custom-resources/crd-resource.store.ts index 8717884785..335a015aa7 100644 --- a/src/renderer/components/+custom-resources/crd-resource.store.ts +++ b/src/renderer/components/+custom-resources/crd-resource.store.ts @@ -23,10 +23,10 @@ import type { KubeApi } from "../../api/kube-api"; import { KubeObjectStore } from "../../kube-object.store"; import type { KubeObject } from "../../api/kube-object"; -export class CRDResourceStore extends KubeObjectStore { - api: KubeApi; +export class CRDResourceStore extends KubeObjectStore { + api: KubeApi; - constructor(api: KubeApi) { + constructor(api: KubeApi) { super(); this.api = api; } diff --git a/src/renderer/components/+custom-resources/crd-resources.tsx b/src/renderer/components/+custom-resources/crd-resources.tsx index 0203bef3da..6042b82027 100644 --- a/src/renderer/components/+custom-resources/crd-resources.tsx +++ b/src/renderer/components/+custom-resources/crd-resources.tsx @@ -29,7 +29,7 @@ import { KubeObjectListLayout } from "../kube-object"; import type { KubeObject } from "../../api/kube-object"; import { autorun, computed, makeObservable } from "mobx"; import { crdStore } from "./crd.store"; -import type { TableSortCallback } from "../table"; +import type { TableSortCallbacks } from "../table"; import { apiManager } from "../../api/api-manager"; import { parseJsonPath } from "../../utils/jsonPath"; import type { CRDRouteParams } from "../../../common/routes"; @@ -80,14 +80,14 @@ export class CrdResources extends React.Component { if (!crd) return null; const isNamespaced = crd.isNamespaced(); const extraColumns = crd.getPrinterColumns(false); // Cols with priority bigger than 0 are shown in details - const sortingCallbacks: { [sortBy: string]: TableSortCallback } = { - [columnId.name]: (item: KubeObject) => item.getName(), - [columnId.namespace]: (item: KubeObject) => item.getNs(), - [columnId.age]: (item: KubeObject) => item.getTimeDiffFromNow(), + const sortingCallbacks: TableSortCallbacks = { + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }; extraColumns.forEach(column => { - sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1))); + sortingCallbacks[column.name] = item => jsonPath.value(item, parseJsonPath(column.jsonPath.slice(1))); }); return ( @@ -98,7 +98,7 @@ export class CrdResources extends React.Component { store={store} sortingCallbacks={sortingCallbacks} searchFilters={[ - (item: KubeObject) => item.getSearchFields(), + item => item.getSearchFields(), ]} renderHeaderTitle={crd.getResourceTitle()} customizeHeader={({ searchProps, ...headerPlaceholders }) => ({ @@ -123,7 +123,7 @@ export class CrdResources extends React.Component { }), { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(crdInstance: KubeObject) => [ + renderTableContents={crdInstance => [ crdInstance.getName(), isNamespaced && crdInstance.getNs(), ...extraColumns.map((column) => { diff --git a/src/renderer/components/+custom-resources/crd.store.ts b/src/renderer/components/+custom-resources/crd.store.ts index 613438bca7..25415e5ab3 100644 --- a/src/renderer/components/+custom-resources/crd.store.ts +++ b/src/renderer/components/+custom-resources/crd.store.ts @@ -26,13 +26,18 @@ import { crdApi, CustomResourceDefinition } from "../../api/endpoints/crd.api"; import { apiManager } from "../../api/api-manager"; import { KubeApi } from "../../api/kube-api"; import { CRDResourceStore } from "./crd-resource.store"; -import type { KubeObject } from "../../api/kube-object"; +import { KubeObject } from "../../api/kube-object"; function initStore(crd: CustomResourceDefinition) { const apiBase = crd.getResourceApiBase(); const kind = crd.getResourceKind(); const isNamespaced = crd.isNamespaced(); - const api = apiManager.getApi(apiBase) || new KubeApi({ apiBase, kind, isNamespaced }); + const api = apiManager.getApi(apiBase) ?? new KubeApi({ + objectConstructor: KubeObject, + apiBase, + kind, + isNamespaced, + }); if (!apiManager.getStore(api)) { apiManager.registerStore(new CRDResourceStore(api)); diff --git a/src/renderer/components/+events/events.tsx b/src/renderer/components/+events/events.tsx index f0d95bd41e..23aa985ab8 100644 --- a/src/renderer/components/+events/events.tsx +++ b/src/renderer/components/+events/events.tsx @@ -29,7 +29,7 @@ import { TabLayout } from "../layout/tab-layout"; import { EventStore, eventStore } from "./event.store"; import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object"; import type { KubeEvent } from "../../api/endpoints/events.api"; -import type { TableSortCallbacks, TableSortParams, TableProps } from "../table"; +import type { TableSortCallbacks, TableSortParams } from "../table"; import type { HeaderCustomizer } from "../item-object-list"; import { Tooltip } from "../tooltip"; import { Link } from "react-router-dom"; @@ -49,7 +49,7 @@ enum columnId { lastSeen = "last-seen", } -interface Props extends Partial { +interface Props extends Partial> { className?: IClassName; compact?: boolean; compactLimit?: number; @@ -69,19 +69,13 @@ export class Events extends React.Component { orderBy: "asc", }; - private sortingCallbacks: TableSortCallbacks = { - [columnId.namespace]: (event: KubeEvent) => event.getNs(), - [columnId.type]: (event: KubeEvent) => event.type, - [columnId.object]: (event: KubeEvent) => event.involvedObject.name, - [columnId.count]: (event: KubeEvent) => event.count, - [columnId.age]: (event: KubeEvent) => event.getTimeDiffFromNow(), - [columnId.lastSeen]: (event: KubeEvent) => this.now - new Date(event.lastTimestamp).getTime(), - }; - - private tableConfiguration: TableProps = { - sortSyncWithUrl: false, - sortByDefault: this.sorting, - onSort: params => this.sorting = params, + private sortingCallbacks: TableSortCallbacks = { + [columnId.namespace]: event => event.getNs(), + [columnId.type]: event => event.type, + [columnId.object]: event => event.involvedObject.name, + [columnId.count]: event => event.count, + [columnId.age]: event => event.getTimeDiffFromNow(), + [columnId.lastSeen]: event => this.now - new Date(event.lastTimestamp).getTime(), }; constructor(props: Props) { @@ -160,13 +154,17 @@ export class Events extends React.Component { isSelectable={false} items={visibleItems} virtual={!compact} - tableProps={this.tableConfiguration} + tableProps={{ + sortSyncWithUrl: false, + sortByDefault: this.sorting, + onSort: params => this.sorting = params, + }} sortingCallbacks={this.sortingCallbacks} searchFilters={[ - (event: KubeEvent) => event.getSearchFields(), - (event: KubeEvent) => event.message, - (event: KubeEvent) => event.getSource(), - (event: KubeEvent) => event.involvedObject.name, + event => event.getSearchFields(), + event => event.message, + event => event.getSource(), + event => event.involvedObject.name, ]} renderTableHeader={[ { title: "Type", className: "type", sortBy: columnId.type, id: columnId.type }, @@ -178,7 +176,7 @@ export class Events extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Last Seen", className: "last-seen", sortBy: columnId.lastSeen, id: columnId.lastSeen }, ]} - renderTableContents={(event: KubeEvent) => { + renderTableContents={event => { const { involvedObject, type, message } = event; const tooltipId = `message-${event.getId()}`; const isWarning = event.isWarning(); diff --git a/src/renderer/components/+namespaces/namespace.store.ts b/src/renderer/components/+namespaces/namespace.store.ts index e8db0948ca..cc4a8b98ce 100644 --- a/src/renderer/components/+namespaces/namespace.store.ts +++ b/src/renderer/components/+namespaces/namespace.store.ts @@ -109,7 +109,7 @@ export class NamespaceStore extends KubeObjectStore { return super.subscribe(); } - protected async loadItems(params: KubeObjectStoreLoadingParams) { + protected async loadItems(params: KubeObjectStoreLoadingParams): Promise { const { allowedNamespaces } = this; let namespaces = await super.loadItems(params).catch(() => []); diff --git a/src/renderer/components/+namespaces/namespaces.tsx b/src/renderer/components/+namespaces/namespaces.tsx index c80c839071..cc29fa05ea 100644 --- a/src/renderer/components/+namespaces/namespaces.tsx +++ b/src/renderer/components/+namespaces/namespaces.tsx @@ -22,7 +22,7 @@ import "./namespaces.scss"; import React from "react"; -import { Namespace, NamespaceStatus } from "../../api/endpoints"; +import { NamespaceStatus } from "../../api/endpoints"; import { AddNamespaceDialog } from "./add-namespace-dialog"; import { TabLayout } from "../layout/tab-layout"; import { Badge } from "../badge"; @@ -51,14 +51,14 @@ export class Namespaces extends React.Component { tableId="namespaces" className="Namespaces" store={namespaceStore} sortingCallbacks={{ - [columnId.name]: (ns: Namespace) => ns.getName(), - [columnId.labels]: (ns: Namespace) => ns.getLabels(), - [columnId.age]: (ns: Namespace) => ns.getTimeDiffFromNow(), - [columnId.status]: (ns: Namespace) => ns.getStatus(), + [columnId.name]: ns => ns.getName(), + [columnId.labels]: ns => ns.getLabels(), + [columnId.age]: ns => ns.getTimeDiffFromNow(), + [columnId.status]: ns => ns.getStatus(), }} searchFilters={[ - (item: Namespace) => item.getSearchFields(), - (item: Namespace) => item.getStatus() + item => item.getSearchFields(), + item => item.getStatus() ]} renderHeaderTitle="Namespaces" renderTableHeader={[ @@ -68,7 +68,7 @@ export class Namespaces extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(item: Namespace) => [ + renderTableContents={item => [ item.getName(), , item.getLabels().map(label => ), @@ -79,7 +79,7 @@ export class Namespaces extends React.Component { addTooltip: "Add Namespace", onAdd: () => AddNamespaceDialog.open(), }} - customizeTableRowProps={(item: Namespace) => ({ + customizeTableRowProps={item => ({ disabled: item.getStatus() === NamespaceStatus.TERMINATING, })} /> diff --git a/src/renderer/components/+network-endpoints/endpoints.tsx b/src/renderer/components/+network-endpoints/endpoints.tsx index 3ae66884fa..940ab89783 100644 --- a/src/renderer/components/+network-endpoints/endpoints.tsx +++ b/src/renderer/components/+network-endpoints/endpoints.tsx @@ -24,7 +24,6 @@ import "./endpoints.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router-dom"; -import type { Endpoint } from "../../api/endpoints/endpoint.api"; import { endpointStore } from "./endpoints.store"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -49,12 +48,12 @@ export class Endpoints extends React.Component { tableId="network_endpoints" className="Endpoints" store={endpointStore} sortingCallbacks={{ - [columnId.name]: (endpoint: Endpoint) => endpoint.getName(), - [columnId.namespace]: (endpoint: Endpoint) => endpoint.getNs(), - [columnId.age]: (endpoint: Endpoint) => endpoint.getTimeDiffFromNow(), + [columnId.name]: endpoint => endpoint.getName(), + [columnId.namespace]: endpoint => endpoint.getNs(), + [columnId.age]: endpoint => endpoint.getTimeDiffFromNow(), }} searchFilters={[ - (endpoint: Endpoint) => endpoint.getSearchFields() + endpoint => endpoint.getSearchFields() ]} renderHeaderTitle="Endpoints" renderTableHeader={[ @@ -64,7 +63,7 @@ export class Endpoints extends React.Component { { title: "Endpoints", className: "endpoints", id: columnId.endpoints }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(endpoint: Endpoint) => [ + renderTableContents={endpoint => [ endpoint.getName(), , endpoint.getNs(), @@ -72,7 +71,7 @@ export class Endpoints extends React.Component { endpoint.getAge(), ]} tableProps={{ - customRowHeights: (item: Endpoint, lineHeight, paddings) => { + customRowHeights: (item, lineHeight, paddings) => { const lines = item.getEndpointSubsets().length || 1; return lines * lineHeight + paddings; diff --git a/src/renderer/components/+network-ingresses/ingresses.tsx b/src/renderer/components/+network-ingresses/ingresses.tsx index 2a15bf5cb8..153a4eb62f 100644 --- a/src/renderer/components/+network-ingresses/ingresses.tsx +++ b/src/renderer/components/+network-ingresses/ingresses.tsx @@ -24,7 +24,6 @@ import "./ingresses.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router-dom"; -import type { Ingress } from "../../api/endpoints/ingress.api"; import { ingressStore } from "./ingress.store"; import { KubeObjectListLayout } from "../kube-object"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -50,13 +49,13 @@ export class Ingresses extends React.Component { tableId="network_ingresses" className="Ingresses" store={ingressStore} sortingCallbacks={{ - [columnId.name]: (ingress: Ingress) => ingress.getName(), - [columnId.namespace]: (ingress: Ingress) => ingress.getNs(), - [columnId.age]: (ingress: Ingress) => ingress.getTimeDiffFromNow(), + [columnId.name]: ingress => ingress.getName(), + [columnId.namespace]: ingress => ingress.getNs(), + [columnId.age]: ingress => ingress.getTimeDiffFromNow(), }} searchFilters={[ - (ingress: Ingress) => ingress.getSearchFields(), - (ingress: Ingress) => ingress.getPorts(), + ingress => ingress.getSearchFields(), + ingress => ingress.getPorts(), ]} renderHeaderTitle="Ingresses" renderTableHeader={[ @@ -67,7 +66,7 @@ export class Ingresses extends React.Component { { title: "Rules", className: "rules", id: columnId.rules }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(ingress: Ingress) => [ + renderTableContents={ingress => [ ingress.getName(), , ingress.getNs(), @@ -76,7 +75,7 @@ export class Ingresses extends React.Component { ingress.getAge(), ]} tableProps={{ - customRowHeights: (item: Ingress, lineHeight, paddings) => { + customRowHeights: (item, lineHeight, paddings) => { const lines = item.getRoutes().length || 1; return lines * lineHeight + paddings; diff --git a/src/renderer/components/+network-policies/network-policies.tsx b/src/renderer/components/+network-policies/network-policies.tsx index 6aa63eff23..65579eaa56 100644 --- a/src/renderer/components/+network-policies/network-policies.tsx +++ b/src/renderer/components/+network-policies/network-policies.tsx @@ -24,7 +24,6 @@ import "./network-policies.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router-dom"; -import type { NetworkPolicy } from "../../api/endpoints/network-policy.api"; import { KubeObjectListLayout } from "../kube-object"; import { networkPolicyStore } from "./network-policy.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -49,12 +48,12 @@ export class NetworkPolicies extends React.Component { tableId="network_policies" className="NetworkPolicies" store={networkPolicyStore} sortingCallbacks={{ - [columnId.name]: (item: NetworkPolicy) => item.getName(), - [columnId.namespace]: (item: NetworkPolicy) => item.getNs(), - [columnId.age]: (item: NetworkPolicy) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: NetworkPolicy) => item.getSearchFields(), + item => item.getSearchFields(), ]} renderHeaderTitle="Network Policies" renderTableHeader={[ @@ -64,7 +63,7 @@ export class NetworkPolicies extends React.Component { { title: "Policy Types", className: "type", id: columnId.types }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(item: NetworkPolicy) => [ + renderTableContents={item => [ item.getName(), , item.getNs(), diff --git a/src/renderer/components/+network-services/services.tsx b/src/renderer/components/+network-services/services.tsx index 502955b630..745236be61 100644 --- a/src/renderer/components/+network-services/services.tsx +++ b/src/renderer/components/+network-services/services.tsx @@ -24,7 +24,6 @@ import "./services.scss"; import React from "react"; import { observer } from "mobx-react"; import type { RouteComponentProps } from "react-router"; -import type { Service } from "../../api/endpoints/service.api"; import { KubeObjectListLayout } from "../kube-object"; import { Badge } from "../badge"; import { serviceStore } from "./services.store"; @@ -55,19 +54,19 @@ export class Services extends React.Component { tableId="network_services" className="Services" store={serviceStore} sortingCallbacks={{ - [columnId.name]: (service: Service) => service.getName(), - [columnId.namespace]: (service: Service) => service.getNs(), - [columnId.selector]: (service: Service) => service.getSelector(), - [columnId.ports]: (service: Service) => (service.spec.ports || []).map(({ port }) => port)[0], - [columnId.clusterIp]: (service: Service) => service.getClusterIp(), - [columnId.type]: (service: Service) => service.getType(), - [columnId.age]: (service: Service) => service.getTimeDiffFromNow(), - [columnId.status]: (service: Service) => service.getStatus(), + [columnId.name]: service => service.getName(), + [columnId.namespace]: service => service.getNs(), + [columnId.selector]: service => service.getSelector(), + [columnId.ports]: service => (service.spec.ports || []).map(({ port }) => port)[0], + [columnId.clusterIp]: service => service.getClusterIp(), + [columnId.type]: service => service.getType(), + [columnId.age]: service => service.getTimeDiffFromNow(), + [columnId.status]: service => service.getStatus(), }} searchFilters={[ - (service: Service) => service.getSearchFields(), - (service: Service) => service.getSelector().join(" "), - (service: Service) => service.getPorts().join(" "), + service => service.getSearchFields(), + service => service.getSelector().join(" "), + service => service.getPorts().join(" "), ]} renderHeaderTitle="Services" renderTableHeader={[ @@ -82,7 +81,7 @@ export class Services extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(service: Service) => [ + renderTableContents={service => [ service.getName(), , service.getNs(), diff --git a/src/renderer/components/+nodes/nodes.tsx b/src/renderer/components/+nodes/nodes.tsx index 4b5672b816..1225ea8c15 100644 --- a/src/renderer/components/+nodes/nodes.tsx +++ b/src/renderer/components/+nodes/nodes.tsx @@ -198,21 +198,21 @@ export class Nodes extends React.Component { dependentStores={[podsStore]} isSelectable={false} sortingCallbacks={{ - [columnId.name]: (node: Node) => node.getName(), - [columnId.cpu]: (node: Node) => this.getLastMetricValues(node, ["cpuUsage"]), - [columnId.memory]: (node: Node) => this.getLastMetricValues(node, ["memoryUsage"]), - [columnId.disk]: (node: Node) => this.getLastMetricValues(node, ["fsUsage"]), - [columnId.conditions]: (node: Node) => node.getNodeConditionText(), - [columnId.taints]: (node: Node) => node.getTaints().length, - [columnId.roles]: (node: Node) => node.getRoleLabels(), - [columnId.age]: (node: Node) => node.getTimeDiffFromNow(), - [columnId.version]: (node: Node) => node.getKubeletVersion(), + [columnId.name]: node => node.getName(), + [columnId.cpu]: node => this.getLastMetricValues(node, ["cpuUsage"]), + [columnId.memory]: node => this.getLastMetricValues(node, ["memoryUsage"]), + [columnId.disk]: node => this.getLastMetricValues(node, ["fsUsage"]), + [columnId.conditions]: node => node.getNodeConditionText(), + [columnId.taints]: node => node.getTaints().length, + [columnId.roles]: node => node.getRoleLabels(), + [columnId.age]: node => node.getTimeDiffFromNow(), + [columnId.version]: node => node.getKubeletVersion(), }} searchFilters={[ - (node: Node) => node.getSearchFields(), - (node: Node) => node.getRoleLabels(), - (node: Node) => node.getKubeletVersion(), - (node: Node) => node.getNodeConditionText(), + node => node.getSearchFields(), + node => node.getRoleLabels(), + node => node.getKubeletVersion(), + node => node.getNodeConditionText(), ]} renderHeaderTitle="Nodes" renderTableHeader={[ @@ -227,7 +227,7 @@ export class Nodes extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, ]} - renderTableContents={(node: Node) => { + renderTableContents={node => { const tooltipId = `node-taints-${node.getId()}`; return [ diff --git a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx index a74a5e8623..c578bf8e34 100644 --- a/src/renderer/components/+pod-security-policies/pod-security-policies.tsx +++ b/src/renderer/components/+pod-security-policies/pod-security-policies.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import { KubeObjectListLayout } from "../kube-object"; import { podSecurityPoliciesStore } from "./pod-security-policies.store"; -import type { PodSecurityPolicy } from "../../api/endpoints"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; enum columnId { @@ -45,15 +44,15 @@ export class PodSecurityPolicies extends React.Component { className="PodSecurityPolicies" store={podSecurityPoliciesStore} sortingCallbacks={{ - [columnId.name]: (item: PodSecurityPolicy) => item.getName(), - [columnId.volumes]: (item: PodSecurityPolicy) => item.getVolumes(), - [columnId.privileged]: (item: PodSecurityPolicy) => +item.isPrivileged(), - [columnId.age]: (item: PodSecurityPolicy) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.volumes]: item => item.getVolumes(), + [columnId.privileged]: item => +item.isPrivileged(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: PodSecurityPolicy) => item.getSearchFields(), - (item: PodSecurityPolicy) => item.getVolumes(), - (item: PodSecurityPolicy) => Object.values(item.getRules()), + item => item.getSearchFields(), + item => item.getVolumes(), + item => Object.values(item.getRules()), ]} renderHeaderTitle="Pod Security Policies" renderTableHeader={[ @@ -63,7 +62,7 @@ export class PodSecurityPolicies extends React.Component { { title: "Volumes", className: "volumes", sortBy: columnId.volumes, id: columnId.volumes }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(item: PodSecurityPolicy) => { + renderTableContents={item => { return [ item.getName(), , diff --git a/src/renderer/components/+storage-classes/storage-classes.tsx b/src/renderer/components/+storage-classes/storage-classes.tsx index 9ccfca9448..d81d32041e 100644 --- a/src/renderer/components/+storage-classes/storage-classes.tsx +++ b/src/renderer/components/+storage-classes/storage-classes.tsx @@ -24,7 +24,6 @@ import "./storage-classes.scss"; import React from "react"; import type { RouteComponentProps } from "react-router-dom"; import { observer } from "mobx-react"; -import type { StorageClass } from "../../api/endpoints/storage-class.api"; import { KubeObjectListLayout } from "../kube-object"; import { storageClassStore } from "./storage-class.store"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -51,14 +50,14 @@ export class StorageClasses extends React.Component { className="StorageClasses" store={storageClassStore} sortingCallbacks={{ - [columnId.name]: (item: StorageClass) => item.getName(), - [columnId.age]: (item: StorageClass) => item.getTimeDiffFromNow(), - [columnId.provisioner]: (item: StorageClass) => item.provisioner, - [columnId.reclaimPolicy]: (item: StorageClass) => item.reclaimPolicy, + [columnId.name]: item => item.getName(), + [columnId.age]: item => item.getTimeDiffFromNow(), + [columnId.provisioner]: item => item.provisioner, + [columnId.reclaimPolicy]: item => item.reclaimPolicy, }} searchFilters={[ - (item: StorageClass) => item.getSearchFields(), - (item: StorageClass) => item.provisioner, + item => item.getSearchFields(), + item => item.provisioner, ]} renderHeaderTitle="Storage Classes" renderTableHeader={[ @@ -69,7 +68,7 @@ export class StorageClasses extends React.Component { { title: "Default", className: "is-default", id: columnId.default }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(storageClass: StorageClass) => [ + renderTableContents={storageClass => [ storageClass.getName(), , storageClass.provisioner, diff --git a/src/renderer/components/+storage-volume-claims/volume-claims.tsx b/src/renderer/components/+storage-volume-claims/volume-claims.tsx index c222405756..60230033f9 100644 --- a/src/renderer/components/+storage-volume-claims/volume-claims.tsx +++ b/src/renderer/components/+storage-volume-claims/volume-claims.tsx @@ -25,7 +25,6 @@ import React from "react"; import { observer } from "mobx-react"; import { Link, RouteComponentProps } from "react-router-dom"; import { volumeClaimStore } from "./volume-claim.store"; -import type { PersistentVolumeClaim } from "../../api/endpoints/persistent-volume-claims.api"; import { podsStore } from "../+workloads-pods/pods.store"; import { getDetailsUrl, KubeObjectListLayout } from "../kube-object"; import { unitsToBytes } from "../../utils/convertMemory"; @@ -58,17 +57,17 @@ export class PersistentVolumeClaims extends React.Component { store={volumeClaimStore} dependentStores={[podsStore]} sortingCallbacks={{ - [columnId.name]: (pvc: PersistentVolumeClaim) => pvc.getName(), - [columnId.namespace]: (pvc: PersistentVolumeClaim) => pvc.getNs(), - [columnId.pods]: (pvc: PersistentVolumeClaim) => pvc.getPods(podsStore.items).map(pod => pod.getName()), - [columnId.status]: (pvc: PersistentVolumeClaim) => pvc.getStatus(), - [columnId.size]: (pvc: PersistentVolumeClaim) => unitsToBytes(pvc.getStorage()), - [columnId.storageClass]: (pvc: PersistentVolumeClaim) => pvc.spec.storageClassName, - [columnId.age]: (pvc: PersistentVolumeClaim) => pvc.getTimeDiffFromNow(), + [columnId.name]: pvc => pvc.getName(), + [columnId.namespace]: pvc => pvc.getNs(), + [columnId.pods]: pvc => pvc.getPods(podsStore.items).map(pod => pod.getName()), + [columnId.status]: pvc => pvc.getStatus(), + [columnId.size]: pvc => unitsToBytes(pvc.getStorage()), + [columnId.storageClass]: pvc => pvc.spec.storageClassName, + [columnId.age]: pvc => pvc.getTimeDiffFromNow(), }} searchFilters={[ - (item: PersistentVolumeClaim) => item.getSearchFields(), - (item: PersistentVolumeClaim) => item.getPods(podsStore.items).map(pod => pod.getName()), + item => item.getSearchFields(), + item => item.getPods(podsStore.items).map(pod => pod.getName()), ]} renderHeaderTitle="Persistent Volume Claims" renderTableHeader={[ @@ -81,7 +80,7 @@ export class PersistentVolumeClaims extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(pvc: PersistentVolumeClaim) => { + renderTableContents={pvc => { const pods = pvc.getPods(podsStore.items); const { storageClassName } = pvc.spec; const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({ diff --git a/src/renderer/components/+storage-volumes/volumes.tsx b/src/renderer/components/+storage-volumes/volumes.tsx index 8b60fc9afc..ce1e313e9b 100644 --- a/src/renderer/components/+storage-volumes/volumes.tsx +++ b/src/renderer/components/+storage-volumes/volumes.tsx @@ -24,7 +24,6 @@ import "./volumes.scss"; import React from "react"; import { observer } from "mobx-react"; import { Link, RouteComponentProps } from "react-router-dom"; -import type { PersistentVolume } from "../../api/endpoints/persistent-volume.api"; import { getDetailsUrl, KubeObjectListLayout } from "../kube-object"; import { stopPropagation } from "../../utils"; import { volumesStore } from "./volumes.store"; @@ -54,15 +53,15 @@ export class PersistentVolumes extends React.Component { className="PersistentVolumes" store={volumesStore} sortingCallbacks={{ - [columnId.name]: (item: PersistentVolume) => item.getName(), - [columnId.storageClass]: (item: PersistentVolume) => item.getStorageClass(), - [columnId.capacity]: (item: PersistentVolume) => item.getCapacity(true), - [columnId.status]: (item: PersistentVolume) => item.getStatus(), - [columnId.age]: (item: PersistentVolume) => item.getTimeDiffFromNow(), + [columnId.name]: item => item.getName(), + [columnId.storageClass]: item => item.getStorageClass(), + [columnId.capacity]: item => item.getCapacity(true), + [columnId.status]: item => item.getStatus(), + [columnId.age]: item => item.getTimeDiffFromNow(), }} searchFilters={[ - (item: PersistentVolume) => item.getSearchFields(), - (item: PersistentVolume) => item.getClaimRefName(), + item => item.getSearchFields(), + item => item.getClaimRefName(), ]} renderHeaderTitle="Persistent Volumes" renderTableHeader={[ @@ -74,7 +73,7 @@ export class PersistentVolumes extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(volume: PersistentVolume) => { + renderTableContents={volume => { const { claimRef, storageClassName } = volume.spec; const storageClassDetailsUrl = getDetailsUrl(storageClassApi.getUrl({ name: storageClassName diff --git a/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx b/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx index 2cf5b95037..cb1f542fd1 100644 --- a/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx +++ b/src/renderer/components/+user-management/+cluster-role-bindings/view.tsx @@ -24,7 +24,6 @@ import "./view.scss"; import { observer } from "mobx-react"; import React from "react"; import type { RouteComponentProps } from "react-router"; -import type { ClusterRoleBinding } from "../../../api/endpoints"; import { KubeObjectListLayout } from "../../kube-object"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; import { ClusterRoleBindingDialog } from "./dialog"; @@ -55,13 +54,13 @@ export class ClusterRoleBindings extends React.Component { store={clusterRoleBindingsStore} dependentStores={[clusterRolesStore, serviceAccountsStore]} sortingCallbacks={{ - [columnId.name]: (binding: ClusterRoleBinding) => binding.getName(), - [columnId.bindings]: (binding: ClusterRoleBinding) => binding.getSubjectNames(), - [columnId.age]: (binding: ClusterRoleBinding) => binding.getTimeDiffFromNow(), + [columnId.name]: binding => binding.getName(), + [columnId.bindings]: binding => binding.getSubjectNames(), + [columnId.age]: binding => binding.getTimeDiffFromNow(), }} searchFilters={[ - (binding: ClusterRoleBinding) => binding.getSearchFields(), - (binding: ClusterRoleBinding) => binding.getSubjectNames(), + binding => binding.getSearchFields(), + binding => binding.getSubjectNames(), ]} renderHeaderTitle="Cluster Role Bindings" renderTableHeader={[ @@ -70,7 +69,7 @@ export class ClusterRoleBindings extends React.Component { { title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(binding: ClusterRoleBinding) => [ + renderTableContents={binding => [ binding.getName(), , binding.getSubjectNames(), diff --git a/src/renderer/components/+user-management/+cluster-roles/view.tsx b/src/renderer/components/+user-management/+cluster-roles/view.tsx index c58636e950..271802c10b 100644 --- a/src/renderer/components/+user-management/+cluster-roles/view.tsx +++ b/src/renderer/components/+user-management/+cluster-roles/view.tsx @@ -24,7 +24,6 @@ import "./view.scss"; import { observer } from "mobx-react"; import React from "react"; import type { RouteComponentProps } from "react-router"; -import type { ClusterRole } from "../../../api/endpoints"; import { KubeObjectListLayout } from "../../kube-object"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; import { AddClusterRoleDialog } from "./add-dialog"; @@ -51,11 +50,11 @@ export class ClusterRoles extends React.Component { className="ClusterRoles" store={clusterRolesStore} sortingCallbacks={{ - [columnId.name]: (clusterRole: ClusterRole) => clusterRole.getName(), - [columnId.age]: (clusterRole: ClusterRole) => clusterRole.getTimeDiffFromNow(), + [columnId.name]: clusterRole => clusterRole.getName(), + [columnId.age]: clusterRole => clusterRole.getTimeDiffFromNow(), }} searchFilters={[ - (clusterRole: ClusterRole) => clusterRole.getSearchFields(), + clusterRole => clusterRole.getSearchFields(), ]} renderHeaderTitle="Cluster Roles" renderTableHeader={[ @@ -63,7 +62,7 @@ export class ClusterRoles extends React.Component { { className: "warning", showWithColumn: columnId.name }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(clusterRole: ClusterRole) => [ + renderTableContents={clusterRole => [ clusterRole.getName(), , clusterRole.getAge(), diff --git a/src/renderer/components/+user-management/+role-bindings/view.tsx b/src/renderer/components/+user-management/+role-bindings/view.tsx index fa32c599d6..247e72482c 100644 --- a/src/renderer/components/+user-management/+role-bindings/view.tsx +++ b/src/renderer/components/+user-management/+role-bindings/view.tsx @@ -23,7 +23,6 @@ import "./view.scss"; import { observer } from "mobx-react"; import React from "react"; import type { RouteComponentProps } from "react-router"; -import type { RoleBinding } from "../../../api/endpoints"; import { KubeObjectListLayout } from "../../kube-object"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; import { RoleBindingDialog } from "./dialog"; @@ -55,14 +54,14 @@ export class RoleBindings extends React.Component { store={roleBindingsStore} dependentStores={[rolesStore, clusterRolesStore, serviceAccountsStore]} sortingCallbacks={{ - [columnId.name]: (binding: RoleBinding) => binding.getName(), - [columnId.namespace]: (binding: RoleBinding) => binding.getNs(), - [columnId.bindings]: (binding: RoleBinding) => binding.getSubjectNames(), - [columnId.age]: (binding: RoleBinding) => binding.getTimeDiffFromNow(), + [columnId.name]: binding => binding.getName(), + [columnId.namespace]: binding => binding.getNs(), + [columnId.bindings]: binding => binding.getSubjectNames(), + [columnId.age]: binding => binding.getTimeDiffFromNow(), }} searchFilters={[ - (binding: RoleBinding) => binding.getSearchFields(), - (binding: RoleBinding) => binding.getSubjectNames(), + binding => binding.getSearchFields(), + binding => binding.getSubjectNames(), ]} renderHeaderTitle="Role Bindings" renderTableHeader={[ @@ -72,7 +71,7 @@ export class RoleBindings extends React.Component { { title: "Bindings", className: "bindings", sortBy: columnId.bindings, id: columnId.bindings }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(binding: RoleBinding) => [ + renderTableContents={binding => [ binding.getName(), , binding.getNs(), diff --git a/src/renderer/components/+user-management/+roles/view.tsx b/src/renderer/components/+user-management/+roles/view.tsx index b6894b0571..dd71c83b93 100644 --- a/src/renderer/components/+user-management/+roles/view.tsx +++ b/src/renderer/components/+user-management/+roles/view.tsx @@ -24,7 +24,6 @@ import "./view.scss"; import { observer } from "mobx-react"; import React from "react"; import type { RouteComponentProps } from "react-router"; -import type { Role } from "../../../api/endpoints"; import { KubeObjectListLayout } from "../../kube-object"; import { KubeObjectStatusIcon } from "../../kube-object-status-icon"; import { AddRoleDialog } from "./add-dialog"; @@ -51,12 +50,12 @@ export class Roles extends React.Component { className="Roles" store={rolesStore} sortingCallbacks={{ - [columnId.name]: (role: Role) => role.getName(), - [columnId.namespace]: (role: Role) => role.getNs(), - [columnId.age]: (role: Role) => role.getTimeDiffFromNow(), + [columnId.name]: role => role.getName(), + [columnId.namespace]: role => role.getNs(), + [columnId.age]: role => role.getTimeDiffFromNow(), }} searchFilters={[ - (role: Role) => role.getSearchFields(), + role => role.getSearchFields(), ]} renderHeaderTitle="Roles" renderTableHeader={[ @@ -65,7 +64,7 @@ export class Roles extends React.Component { { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(role: Role) => [ + renderTableContents={role => [ role.getName(), , role.getNs(), diff --git a/src/renderer/components/+user-management/+service-accounts/view.tsx b/src/renderer/components/+user-management/+service-accounts/view.tsx index 65798af192..c8bdaaa656 100644 --- a/src/renderer/components/+user-management/+service-accounts/view.tsx +++ b/src/renderer/components/+user-management/+service-accounts/view.tsx @@ -54,12 +54,12 @@ export class ServiceAccounts extends React.Component { tableId="access_service_accounts" className="ServiceAccounts" store={serviceAccountsStore} sortingCallbacks={{ - [columnId.name]: (account: ServiceAccount) => account.getName(), - [columnId.namespace]: (account: ServiceAccount) => account.getNs(), - [columnId.age]: (account: ServiceAccount) => account.getTimeDiffFromNow(), + [columnId.name]: account => account.getName(), + [columnId.namespace]: account => account.getNs(), + [columnId.age]: account => account.getTimeDiffFromNow(), }} searchFilters={[ - (account: ServiceAccount) => account.getSearchFields(), + account => account.getSearchFields(), ]} renderHeaderTitle="Service Accounts" renderTableHeader={[ @@ -68,7 +68,7 @@ export class ServiceAccounts extends React.Component { { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(account: ServiceAccount) => [ + renderTableContents={account => [ account.getName(), , account.getNs(), diff --git a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx index 0e2f98d16a..6938185479 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjob-trigger-dialog.tsx @@ -31,7 +31,7 @@ import { Notifications } from "../notifications"; import { cssNames } from "../../utils"; import { Input } from "../input"; import { systemName, maxLength } from "../input/input_validators"; -import type { IKubeObjectMetadata } from "../../api/kube-object"; +import type { KubeObjectMetadata } from "../../api/kube-object"; interface Props extends Partial { } @@ -104,7 +104,7 @@ export class CronJobTriggerDialog extends Component { name: cronjob.metadata.name, uid: cronjob.metadata.uid, }], - } as IKubeObjectMetadata + } as KubeObjectMetadata }); close(); diff --git a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx index b1c5e3424c..8dca609361 100644 --- a/src/renderer/components/+workloads-cronjobs/cronjobs.tsx +++ b/src/renderer/components/+workloads-cronjobs/cronjobs.tsx @@ -62,20 +62,20 @@ export class CronJobs extends React.Component { className="CronJobs" store={cronJobStore} dependentStores={[jobStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (cronJob: CronJob) => cronJob.getName(), - [columnId.namespace]: (cronJob: CronJob) => cronJob.getNs(), - [columnId.suspend]: (cronJob: CronJob) => cronJob.getSuspendFlag(), - [columnId.active]: (cronJob: CronJob) => cronJobStore.getActiveJobsNum(cronJob), - [columnId.lastSchedule]: (cronJob: CronJob) => ( + [columnId.name]: cronJob => cronJob.getName(), + [columnId.namespace]: cronJob => cronJob.getNs(), + [columnId.suspend]: cronJob => cronJob.getSuspendFlag(), + [columnId.active]: cronJob => cronJobStore.getActiveJobsNum(cronJob), + [columnId.lastSchedule]: cronJob => ( cronJob.status?.lastScheduleTime ? moment().diff(cronJob.status.lastScheduleTime) : 0 ), - [columnId.age]: (cronJob: CronJob) => cronJob.getTimeDiffFromNow(), + [columnId.age]: cronJob => cronJob.getTimeDiffFromNow(), }} searchFilters={[ - (cronJob: CronJob) => cronJob.getSearchFields(), - (cronJob: CronJob) => cronJob.getSchedule(), + cronJob => cronJob.getSearchFields(), + cronJob => cronJob.getSchedule(), ]} renderHeaderTitle="Cron Jobs" renderTableHeader={[ @@ -88,7 +88,7 @@ export class CronJobs extends React.Component { { title: "Last schedule", className: "last-schedule", sortBy: columnId.lastSchedule, id: columnId.lastSchedule }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(cronJob: CronJob) => [ + renderTableContents={cronJob => [ cronJob.getName(), , cronJob.getNs(), diff --git a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx index e22c310e3d..63f04a8049 100644 --- a/src/renderer/components/+workloads-daemonsets/daemonsets.tsx +++ b/src/renderer/components/+workloads-daemonsets/daemonsets.tsx @@ -65,14 +65,14 @@ export class DaemonSets extends React.Component { className="DaemonSets" store={daemonSetStore} dependentStores={[podsStore, nodesStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (daemonSet: DaemonSet) => daemonSet.getName(), - [columnId.namespace]: (daemonSet: DaemonSet) => daemonSet.getNs(), - [columnId.pods]: (daemonSet: DaemonSet) => this.getPodsLength(daemonSet), - [columnId.age]: (daemonSet: DaemonSet) => daemonSet.getTimeDiffFromNow(), + [columnId.name]: daemonSet => daemonSet.getName(), + [columnId.namespace]: daemonSet => daemonSet.getNs(), + [columnId.pods]: daemonSet => this.getPodsLength(daemonSet), + [columnId.age]: daemonSet => daemonSet.getTimeDiffFromNow(), }} searchFilters={[ - (daemonSet: DaemonSet) => daemonSet.getSearchFields(), - (daemonSet: DaemonSet) => daemonSet.getLabels(), + daemonSet => daemonSet.getSearchFields(), + daemonSet => daemonSet.getLabels(), ]} renderHeaderTitle="Daemon Sets" renderTableHeader={[ @@ -83,7 +83,7 @@ export class DaemonSets extends React.Component { { title: "Node Selector", className: "labels", id: columnId.labels }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(daemonSet: DaemonSet) => [ + renderTableContents={daemonSet => [ daemonSet.getName(), daemonSet.getNs(), this.getPodsLength(daemonSet), diff --git a/src/renderer/components/+workloads-deployments/deployments.tsx b/src/renderer/components/+workloads-deployments/deployments.tsx index c07dc397cc..d43670f6e7 100644 --- a/src/renderer/components/+workloads-deployments/deployments.tsx +++ b/src/renderer/components/+workloads-deployments/deployments.tsx @@ -81,15 +81,15 @@ export class Deployments extends React.Component { className="Deployments" store={deploymentStore} dependentStores={[replicaSetStore, podsStore, nodesStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (deployment: Deployment) => deployment.getName(), - [columnId.namespace]: (deployment: Deployment) => deployment.getNs(), - [columnId.replicas]: (deployment: Deployment) => deployment.getReplicas(), - [columnId.age]: (deployment: Deployment) => deployment.getTimeDiffFromNow(), - [columnId.condition]: (deployment: Deployment) => deployment.getConditionsText(), + [columnId.name]: deployment => deployment.getName(), + [columnId.namespace]: deployment => deployment.getNs(), + [columnId.replicas]: deployment => deployment.getReplicas(), + [columnId.age]: deployment => deployment.getTimeDiffFromNow(), + [columnId.condition]: deployment => deployment.getConditionsText(), }} searchFilters={[ - (deployment: Deployment) => deployment.getSearchFields(), - (deployment: Deployment) => deployment.getConditionsText(), + deployment => deployment.getSearchFields(), + deployment => deployment.getConditionsText(), ]} renderHeaderTitle="Deployments" renderTableHeader={[ @@ -101,7 +101,7 @@ export class Deployments extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Conditions", className: "conditions", sortBy: columnId.condition, id: columnId.condition }, ]} - renderTableContents={(deployment: Deployment) => [ + renderTableContents={deployment => [ deployment.getName(), , deployment.getNs(), @@ -110,9 +110,7 @@ export class Deployments extends React.Component { deployment.getAge(), this.renderConditions(deployment), ]} - renderItemMenu={(item: Deployment) => { - return ; - }} + renderItemMenu={item => } /> ); } diff --git a/src/renderer/components/+workloads-jobs/jobs.tsx b/src/renderer/components/+workloads-jobs/jobs.tsx index 9d0c46764f..e2640db6f6 100644 --- a/src/renderer/components/+workloads-jobs/jobs.tsx +++ b/src/renderer/components/+workloads-jobs/jobs.tsx @@ -27,7 +27,6 @@ import type { RouteComponentProps } from "react-router"; import { podsStore } from "../+workloads-pods/pods.store"; import { jobStore } from "./job.store"; import { eventStore } from "../+events/event.store"; -import type { Job } from "../../api/endpoints/job.api"; import { KubeObjectListLayout } from "../kube-object"; import kebabCase from "lodash/kebabCase"; import { KubeObjectStatusIcon } from "../kube-object-status-icon"; @@ -54,13 +53,13 @@ export class Jobs extends React.Component { className="Jobs" store={jobStore} dependentStores={[podsStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (job: Job) => job.getName(), - [columnId.namespace]: (job: Job) => job.getNs(), - [columnId.conditions]: (job: Job) => job.getCondition() != null ? job.getCondition().type : "", - [columnId.age]: (job: Job) => job.getTimeDiffFromNow(), + [columnId.name]: job => job.getName(), + [columnId.namespace]: job => job.getNs(), + [columnId.conditions]: job => job.getCondition() != null ? job.getCondition().type : "", + [columnId.age]: job => job.getTimeDiffFromNow(), }} searchFilters={[ - (job: Job) => job.getSearchFields(), + job => job.getSearchFields(), ]} renderHeaderTitle="Jobs" renderTableHeader={[ @@ -71,7 +70,7 @@ export class Jobs extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Conditions", className: "conditions", sortBy: columnId.conditions, id: columnId.conditions }, ]} - renderTableContents={(job: Job) => { + renderTableContents={job => { const condition = job.getCondition(); return [ diff --git a/src/renderer/components/+workloads-overview/overview-statuses.tsx b/src/renderer/components/+workloads-overview/overview-statuses.tsx index ae67d96667..ebf62c1d98 100644 --- a/src/renderer/components/+workloads-overview/overview-statuses.tsx +++ b/src/renderer/components/+workloads-overview/overview-statuses.tsx @@ -47,7 +47,12 @@ const resources: KubeResource[] = [ export class OverviewStatuses extends React.Component { @boundMethod renderWorkload(resource: KubeResource): React.ReactElement { - const store = workloadStores[resource]; + const store = workloadStores.get(resource); + + if (!store) { + return null; + } + const items = store.getAllByNs(namespaceStore.contextNamespaces); return ( diff --git a/src/renderer/components/+workloads-pods/pod-tolerations.tsx b/src/renderer/components/+workloads-pods/pod-tolerations.tsx index 9fdaef1c81..28793e0ea5 100644 --- a/src/renderer/components/+workloads-pods/pod-tolerations.tsx +++ b/src/renderer/components/+workloads-pods/pod-tolerations.tsx @@ -38,13 +38,6 @@ enum sortBy { Value = "value", } -const sortingCallbacks = { - [sortBy.Key]: (toleration: IToleration) => toleration.key, - [sortBy.Operator]: (toleration: IToleration) => toleration.operator, - [sortBy.Effect]: (toleration: IToleration) => toleration.effect, - [sortBy.Seconds]: (toleration: IToleration) => toleration.tolerationSeconds, -}; - const getTableRow = (toleration: IToleration) => { const { key, operator, effect, tolerationSeconds, value } = toleration; @@ -68,10 +61,17 @@ export function PodTolerations({ tolerations }: Props) { toleration.key, + [sortBy.Operator]: toleration => toleration.operator, + [sortBy.Effect]: toleration => toleration.effect, + [sortBy.Seconds]: toleration => toleration.tolerationSeconds, + }} sortSyncWithUrl={false} className="PodTolerations" + renderRow={getTableRow} > Key @@ -80,9 +80,6 @@ export function PodTolerations({ tolerations }: Props) { Effect Seconds - { - tolerations.map(getTableRow) - }
); } diff --git a/src/renderer/components/+workloads-pods/pods.tsx b/src/renderer/components/+workloads-pods/pods.tsx index d7ef7175e8..b91a8a98e8 100644 --- a/src/renderer/components/+workloads-pods/pods.tsx +++ b/src/renderer/components/+workloads-pods/pods.tsx @@ -97,21 +97,21 @@ export class Pods extends React.Component { tableId = "workloads_pods" isConfigurable sortingCallbacks={{ - [columnId.name]: (pod: Pod) => pod.getName(), - [columnId.namespace]: (pod: Pod) => pod.getNs(), - [columnId.containers]: (pod: Pod) => pod.getContainers().length, - [columnId.restarts]: (pod: Pod) => pod.getRestartsCount(), - [columnId.owners]: (pod: Pod) => pod.getOwnerRefs().map(ref => ref.kind), - [columnId.qos]: (pod: Pod) => pod.getQosClass(), - [columnId.node]: (pod: Pod) => pod.getNodeName(), - [columnId.age]: (pod: Pod) => pod.getTimeDiffFromNow(), - [columnId.status]: (pod: Pod) => pod.getStatusMessage(), + [columnId.name]: pod => pod.getName(), + [columnId.namespace]: pod => pod.getNs(), + [columnId.containers]: pod => pod.getContainers().length, + [columnId.restarts]: pod => pod.getRestartsCount(), + [columnId.owners]: pod => pod.getOwnerRefs().map(ref => ref.kind), + [columnId.qos]: pod => pod.getQosClass(), + [columnId.node]: pod => pod.getNodeName(), + [columnId.age]: pod => pod.getTimeDiffFromNow(), + [columnId.status]: pod => pod.getStatusMessage(), }} searchFilters={[ - (pod: Pod) => pod.getSearchFields(), - (pod: Pod) => pod.getStatusMessage(), - (pod: Pod) => pod.status.podIP, - (pod: Pod) => pod.getNodeName(), + pod => pod.getSearchFields(), + pod => pod.getStatusMessage(), + pod => pod.status.podIP, + pod => pod.getNodeName(), ]} renderHeaderTitle="Pods" renderTableHeader={[ @@ -126,7 +126,7 @@ export class Pods extends React.Component { { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, ]} - renderTableContents={(pod: Pod) => [ + renderTableContents={pod => [ , , pod.getNs(), diff --git a/src/renderer/components/+workloads-replicasets/replicasets.tsx b/src/renderer/components/+workloads-replicasets/replicasets.tsx index 51d63fec55..447280899f 100644 --- a/src/renderer/components/+workloads-replicasets/replicasets.tsx +++ b/src/renderer/components/+workloads-replicasets/replicasets.tsx @@ -55,15 +55,15 @@ export class ReplicaSets extends React.Component { tableId="workload_replicasets" className="ReplicaSets" store={replicaSetStore} sortingCallbacks={{ - [columnId.name]: (replicaSet: ReplicaSet) => replicaSet.getName(), - [columnId.namespace]: (replicaSet: ReplicaSet) => replicaSet.getNs(), - [columnId.desired]: (replicaSet: ReplicaSet) => replicaSet.getDesired(), - [columnId.current]: (replicaSet: ReplicaSet) => replicaSet.getCurrent(), - [columnId.ready]: (replicaSet: ReplicaSet) => replicaSet.getReady(), - [columnId.age]: (replicaSet: ReplicaSet) => replicaSet.getTimeDiffFromNow(), + [columnId.name]: replicaSet => replicaSet.getName(), + [columnId.namespace]: replicaSet => replicaSet.getNs(), + [columnId.desired]: replicaSet => replicaSet.getDesired(), + [columnId.current]: replicaSet => replicaSet.getCurrent(), + [columnId.ready]: replicaSet => replicaSet.getReady(), + [columnId.age]: replicaSet => replicaSet.getTimeDiffFromNow(), }} searchFilters={[ - (replicaSet: ReplicaSet) => replicaSet.getSearchFields(), + replicaSet => replicaSet.getSearchFields(), ]} renderHeaderTitle="Replica Sets" renderTableHeader={[ @@ -75,7 +75,7 @@ export class ReplicaSets extends React.Component { { title: "Ready", className: "ready", sortBy: columnId.ready, id: columnId.ready }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(replicaSet: ReplicaSet) => [ + renderTableContents={replicaSet => [ replicaSet.getName(), , replicaSet.getNs(), diff --git a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx index 82b1fff151..ff6441f33a 100644 --- a/src/renderer/components/+workloads-statefulsets/statefulsets.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulsets.tsx @@ -64,13 +64,13 @@ export class StatefulSets extends React.Component { className="StatefulSets" store={statefulSetStore} dependentStores={[podsStore, nodesStore, eventStore]} sortingCallbacks={{ - [columnId.name]: (statefulSet: StatefulSet) => statefulSet.getName(), - [columnId.namespace]: (statefulSet: StatefulSet) => statefulSet.getNs(), - [columnId.age]: (statefulSet: StatefulSet) => statefulSet.getTimeDiffFromNow(), - [columnId.replicas]: (statefulSet: StatefulSet) => statefulSet.getReplicas(), + [columnId.name]: statefulSet => statefulSet.getName(), + [columnId.namespace]: statefulSet => statefulSet.getNs(), + [columnId.age]: statefulSet => statefulSet.getTimeDiffFromNow(), + [columnId.replicas]: statefulSet => statefulSet.getReplicas(), }} searchFilters={[ - (statefulSet: StatefulSet) => statefulSet.getSearchFields(), + statefulSet => statefulSet.getSearchFields(), ]} renderHeaderTitle="Stateful Sets" renderTableHeader={[ @@ -81,7 +81,7 @@ export class StatefulSets extends React.Component { { className: "warning", showWithColumn: columnId.replicas }, { title: "Age", className: "age", sortBy: columnId.age, id: columnId.age }, ]} - renderTableContents={(statefulSet: StatefulSet) => [ + renderTableContents={statefulSet => [ statefulSet.getName(), statefulSet.getNs(), this.renderPods(statefulSet), @@ -89,9 +89,7 @@ export class StatefulSets extends React.Component { , statefulSet.getAge(), ]} - renderItemMenu={(item: StatefulSet) => { - return ; - }} + renderItemMenu={item => } /> ); } diff --git a/src/renderer/components/+workloads/workloads.stores.ts b/src/renderer/components/+workloads/workloads.stores.ts index a06c17200d..e53ea7cb03 100644 --- a/src/renderer/components/+workloads/workloads.stores.ts +++ b/src/renderer/components/+workloads/workloads.stores.ts @@ -28,13 +28,14 @@ import { jobStore } from "../+workloads-jobs/job.store"; import { cronJobStore } from "../+workloads-cronjobs/cronjob.store"; import type { KubeResource } from "../../../common/rbac"; import { replicaSetStore } from "../+workloads-replicasets/replicasets.store"; +import type { KubeObject } from "../../api/kube-object"; -export const workloadStores: Partial> = { - "pods": podsStore, - "deployments": deploymentStore, - "daemonsets": daemonSetStore, - "statefulsets": statefulSetStore, - "replicasets": replicaSetStore, - "jobs": jobStore, - "cronjobs": cronJobStore, -}; +export const workloadStores = new Map>([ + ["pods", podsStore], + ["deployments", deploymentStore], + ["daemonsets", daemonSetStore], + ["statefulsets", statefulSetStore], + ["replicasets", replicaSetStore], + ["jobs", jobStore], + ["cronjobs", cronJobStore], +]); diff --git a/src/renderer/components/dock/edit-resource.store.ts b/src/renderer/components/dock/edit-resource.store.ts index 7931d86451..c0467b330b 100644 --- a/src/renderer/components/dock/edit-resource.store.ts +++ b/src/renderer/components/dock/edit-resource.store.ts @@ -84,7 +84,7 @@ export class EditResourceStore extends DockTabStore { return Boolean(tabDataReady && this.getResource(tabId)); // ready to edit resource } - getStore(tabId: TabId): KubeObjectStore | undefined { + getStore(tabId: TabId): KubeObjectStore | undefined { return apiManager.getStore(this.getResourcePath(tabId)); } diff --git a/src/renderer/components/item-object-list/item-list-layout.tsx b/src/renderer/components/item-object-list/item-list-layout.tsx index 72dafbd90e..1287312653 100644 --- a/src/renderer/components/item-object-list/item-list-layout.tsx +++ b/src/renderer/components/item-object-list/item-list-layout.tsx @@ -26,7 +26,7 @@ import React, { ReactNode } from "react"; import { computed, makeObservable } from "mobx"; import { observer } from "mobx-react"; import { ConfirmDialog, ConfirmDialogParams } from "../confirm-dialog"; -import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallback } from "../table"; +import { Table, TableCell, TableCellProps, TableHead, TableProps, TableRow, TableRowProps, TableSortCallbacks } from "../table"; import { boundMethod, createStorage, cssNames, IClassName, isReactNode, noop, ObservableToggleSet, prevDefault, stopPropagation } from "../../utils"; import { AddRemoveButtons, AddRemoveButtonsProps } from "../add-remove-buttons"; import { NoItems } from "../no-items"; @@ -44,8 +44,10 @@ import { namespaceStore } from "../+namespaces/namespace.store"; -export type SearchFilter = (item: T) => string | number | (string | number)[]; -export type ItemsFilter = (items: T[]) => T[]; +export type SearchFilter = (item: I) => string | number | (string | number)[]; +export type SearchFilters = Record>; +export type ItemsFilter = (items: I[]) => I[]; +export type ItemsFilters = Record>; export interface HeaderPlaceholders { title?: ReactNode; @@ -55,23 +57,22 @@ export interface HeaderPlaceholders { } export type HeaderCustomizer = (placeholders: HeaderPlaceholders) => HeaderPlaceholders; - -export interface ItemListLayoutProps { +export interface ItemListLayoutProps { tableId?: string; className: IClassName; - items?: T[]; - store: ItemStore; - dependentStores?: ItemStore[]; + items?: I[]; + store: ItemStore; + dependentStores?: ItemStore[]; preloadStores?: boolean; hideFilters?: boolean; - searchFilters?: SearchFilter[]; + searchFilters?: SearchFilter[]; /** @deprecated */ - filterItems?: ItemsFilter[]; + filterItems?: ItemsFilter[]; // header (title, filtering, searching, etc.) showHeader?: boolean; headerClassName?: IClassName; - renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode); + renderHeaderTitle?: ReactNode | ((parent: ItemListLayout) => ReactNode); customizeHeader?: HeaderCustomizer | HeaderCustomizer[]; // items list configuration @@ -79,26 +80,28 @@ export interface ItemListLayoutProps { isSelectable?: boolean; // show checkbox in rows for selecting items isConfigurable?: boolean; copyClassNameFromHeadCells?: boolean; - sortingCallbacks?: { [sortBy: string]: TableSortCallback }; - tableProps?: Partial; // low-level table configuration + sortingCallbacks?: TableSortCallbacks; + tableProps?: Partial>; // low-level table configuration renderTableHeader: TableCellProps[] | null; - renderTableContents: (item: T) => (ReactNode | TableCellProps)[]; - renderItemMenu?: (item: T, store: ItemStore) => ReactNode; - customizeTableRowProps?: (item: T) => Partial; + renderTableContents: (item: I) => (ReactNode | TableCellProps)[]; + renderItemMenu?: (item: I, store: ItemStore) => ReactNode; + customizeTableRowProps?: (item: I) => Partial; addRemoveButtons?: Partial; virtual?: boolean; // item details view hasDetailsView?: boolean; - detailsItem?: T; - onDetails?: (item: T) => void; + detailsItem?: I; + onDetails?: (item: I) => void; // other - customizeRemoveDialog?: (selectedItems: T[]) => Partial; - renderFooter?: (parent: ItemListLayout) => React.ReactNode; + customizeRemoveDialog?: (selectedItems: I[]) => Partial; + renderFooter?: (parent: ItemListLayout) => React.ReactNode; + + filterCallbacks?: ItemsFilters; } -const defaultProps: Partial = { +const defaultProps: Partial> = { showHeader: true, isSelectable: true, isConfigurable: false, @@ -115,14 +118,14 @@ const defaultProps: Partial = { }; @observer -export class ItemListLayout extends React.Component { +export class ItemListLayout extends React.Component> { static defaultProps = defaultProps as object; private storage = createStorage("item_list_layout", { showFilters: false, // setup defaults }); - constructor(props: ItemListLayoutProps) { + constructor(props: ItemListLayoutProps) { super(props); makeObservable(this); } @@ -158,7 +161,7 @@ export class ItemListLayout extends React.Component { stores.forEach(store => store.loadAll(namespaceStore.contextNamespaces)); } - private filterCallbacks: { [type: string]: ItemsFilter } = { + private filterCallbacks: ItemsFilters = { [FilterType.SEARCH]: items => { const { searchFilters } = this.props; const search = pageFilters.getValues(FilterType.SEARCH)[0] || ""; @@ -199,20 +202,20 @@ export class ItemListLayout extends React.Component { return activeFilters; } - applyFilters(filters: ItemsFilter[], items: T[]): T[] { + applyFilters(filters: ItemsFilter[], items: I[]): I[] { if (!filters || !filters.length) return items; return filters.reduce((items, filter) => filter(items), items); } @computed get items() { - const { filters, filterCallbacks } = this; + const { filters, filterCallbacks, props } = this; const filterGroups = groupBy(filters, ({ type }) => type); - const filterItems: ItemsFilter[] = []; + const filterItems: ItemsFilter[] = []; Object.entries(filterGroups).forEach(([type, filtersGroup]) => { - const filterCallback = filterCallbacks[type]; + const filterCallback = filterCallbacks[type] ?? props.filterCallbacks?.[type]; if (filterCallback && filtersGroup.length > 0) { filterItems.push(filterCallback); diff --git a/src/renderer/components/kube-object/kube-object-details.tsx b/src/renderer/components/kube-object/kube-object-details.tsx index 4075361368..7ef8686130 100644 --- a/src/renderer/components/kube-object/kube-object-details.tsx +++ b/src/renderer/components/kube-object/kube-object-details.tsx @@ -30,9 +30,7 @@ import type { KubeObject } from "../../api/kube-object"; import { Spinner } from "../spinner"; import { apiManager } from "../../api/api-manager"; import { crdStore } from "../+custom-resources/crd.store"; -import { CrdResourceDetails } from "../+custom-resources"; import { KubeObjectMenu } from "./kube-object-menu"; -import type { CustomResourceDefinition } from "../../api/endpoints"; import { KubeObjectDetailRegistry } from "../../api/kube-object-detail-registry"; import logger from "../../../main/logger"; @@ -108,6 +106,8 @@ export class KubeObjectDetails extends React.Component { ?.getByPath(this.path); } catch (error) { logger.error(`[KUBE-OBJECT-DETAILS]: failed to get store or object: ${error}`, { path: this.path }); + + return undefined; } } @@ -142,7 +142,7 @@ export class KubeObjectDetails extends React.Component { }); render() { - const { object, isLoading, loadingError, isCrdInstance } = this; + const { object, isLoading, loadingError } = this; const isOpen = !!(object || isLoading || loadingError); if (!object) { @@ -169,10 +169,6 @@ export class KubeObjectDetails extends React.Component { )); - if (isCrdInstance && details.length === 0) { - details.push(); - } - return ( extends ItemListLayoutProps { + store: KubeObjectStore; + dependentStores?: KubeObjectStore[]; } -const defaultProps: Partial = { +const defaultProps: Partial> = { onDetails: (item: KubeObject) => showDetails(item.selfLink), }; @observer -export class KubeObjectListLayout extends React.Component { +export class KubeObjectListLayout extends React.Component> { static defaultProps = defaultProps as object; - constructor(props: KubeObjectListLayoutProps) { + constructor(props: KubeObjectListLayoutProps) { super(props); makeObservable(this); } @@ -95,7 +95,7 @@ export class KubeObjectListLayout extends React.Component } // safe because we are dealing with KubeObjects here + renderItemMenu={item => } /> ); } diff --git a/src/renderer/components/kube-object/kube-object-meta.tsx b/src/renderer/components/kube-object/kube-object-meta.tsx index 98eb539720..f41b3d0991 100644 --- a/src/renderer/components/kube-object/kube-object-meta.tsx +++ b/src/renderer/components/kube-object/kube-object-meta.tsx @@ -20,7 +20,7 @@ */ import React from "react"; -import type { IKubeMetaField, KubeObject } from "../../api/kube-object"; +import type { KubeMetaField, KubeObject } from "../../api/kube-object"; import { DrawerItem, DrawerItemLabels } from "../drawer"; import { lookupApiLink } from "../../api/kube-api"; import { Link } from "react-router-dom"; @@ -30,15 +30,15 @@ import { getDetailsUrl } from "./kube-object-details"; export interface KubeObjectMetaProps { object: KubeObject; - hideFields?: IKubeMetaField[]; + hideFields?: KubeMetaField[]; } export class KubeObjectMeta extends React.Component { - static defaultHiddenFields: IKubeMetaField[] = [ + static defaultHiddenFields: KubeMetaField[] = [ "uid", "resourceVersion", "selfLink" ]; - isHidden(field: IKubeMetaField): boolean { + isHidden(field: KubeMetaField): boolean { const { hideFields = KubeObjectMeta.defaultHiddenFields } = this.props; return hideFields.includes(field); diff --git a/src/renderer/components/table/table.tsx b/src/renderer/components/table/table.tsx index c1cc7f0cd6..bb5e63f570 100644 --- a/src/renderer/components/table/table.tsx +++ b/src/renderer/components/table/table.tsx @@ -30,19 +30,18 @@ import { TableHead, TableHeadElem, TableHeadProps } from "./table-head"; import type { TableCellElem } from "./table-cell"; import { VirtualList } from "../virtual-list"; import { createPageParam } from "../../navigation"; -import type { ItemObject } from "../../item.store"; import { getSortParams, setSortParams } from "./table.storage"; import { computed, makeObservable } from "mobx"; export type TableSortBy = string; export type TableOrderBy = "asc" | "desc" | string; export type TableSortParams = { sortBy: TableSortBy; orderBy: TableOrderBy }; -export type TableSortCallback = (data: D) => string | number | (string | number)[]; -export type TableSortCallbacks = { [columnId: string]: TableSortCallback }; +export type TableSortCallback = (data: Item) => string | number | (string | number)[]; +export type TableSortCallbacks = Record>; -export interface TableProps extends React.DOMAttributes { +export interface TableProps extends React.DOMAttributes { tableId?: string; - items?: ItemObject[]; // Raw items data + items?: Item[]; // Raw items data className?: string; autoSize?: boolean; // Setup auto-sizing for all columns (flex: 1 0) selectable?: boolean; // Highlight rows on hover @@ -52,7 +51,7 @@ export interface TableProps extends React.DOMAttributes { * Define sortable callbacks for every column in * @sortItem argument in the callback is an object, provided in */ - sortable?: TableSortCallbacks; + sortable?: TableSortCallbacks; sortSyncWithUrl?: boolean; // sorting state is managed globally from url params sortByDefault?: Partial; // default sorting params onSort?: (params: TableSortParams) => void; // callback on sort change, default: global sync with url @@ -61,8 +60,9 @@ export interface TableProps extends React.DOMAttributes { virtual?: boolean; // Use virtual list component to render only visible rows rowPadding?: string; rowLineHeight?: string; - customRowHeights?: (item: object, lineHeight: number, paddings: number) => number; + customRowHeights?: (item: Item, lineHeight: number, paddings: number) => number; getTableRow?: (uid: string) => React.ReactElement; + renderRow?: (item: Item) => React.ReactElement; } export const sortByUrlParam = createPageParam({ @@ -74,8 +74,8 @@ export const orderByUrlParam = createPageParam({ }); @observer -export class Table extends React.Component { - static defaultProps: TableProps = { +export class Table extends React.Component> { + static defaultProps: TableProps = { scrollable: true, autoSize: true, rowPadding: "8px", @@ -83,7 +83,7 @@ export class Table extends React.Component { sortSyncWithUrl: true, }; - constructor(props: TableProps) { + constructor(props: TableProps) { super(props); makeObservable(this); } @@ -171,9 +171,20 @@ export class Table extends React.Component { }); } - renderRows() { - const { sortable, noItems, children, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props; + private getContent() { + const { items, renderRow, children } = this.props; const content = React.Children.toArray(children) as (TableRowElem | TableHeadElem)[]; + + if (renderRow) { + content.push(...items.map(renderRow)); + } + + return content; + } + + renderRows() { + const { sortable, noItems, virtual, customRowHeights, rowLineHeight, rowPadding, items, getTableRow, selectedItemId, className } = this.props; + const content = this.getContent(); let rows: React.ReactElement[] = content.filter(elem => elem.type === TableRow); let sortedItems = rows.length ? rows.map(row => row.props.sortItem) : [...items]; diff --git a/src/renderer/initializers/kube-object-detail-registry.tsx b/src/renderer/initializers/kube-object-detail-registry.tsx index 56e88ac3d9..ddc7617f45 100644 --- a/src/renderer/initializers/kube-object-detail-registry.tsx +++ b/src/renderer/initializers/kube-object-detail-registry.tsx @@ -29,7 +29,7 @@ import { ConfigMapDetails } from "../components/+config-maps"; import { PodDisruptionBudgetDetails } from "../components/+config-pod-disruption-budgets"; import { ResourceQuotaDetails } from "../components/+config-resource-quotas"; import { SecretDetails } from "../components/+config-secrets"; -import { CRDDetails } from "../components/+custom-resources"; +import { CRDDetails, CrdResourceDetails } from "../components/+custom-resources"; import { EventDetails } from "../components/+events"; import { KubeEventDetails } from "../components/+events/kube-event-details"; import { NamespaceDetails } from "../components/+namespaces"; @@ -55,6 +55,7 @@ import { PodDetails } from "../components/+workloads-pods"; import { ReplicaSetDetails } from "../components/+workloads-replicasets"; import { StatefulSetDetails } from "../components/+workloads-statefulsets"; import type { KubeObjectDetailsProps } from "../components/kube-object"; +import type { CustomResourceDefinition } from "../api/endpoints"; export function intiKubeObjectDetailRegistry() { KubeObjectDetailRegistry.getInstance() @@ -444,6 +445,13 @@ export function intiKubeObjectDetailRegistry() { components: { Details: (props: KubeObjectDetailsProps) => , } + }, + { + kind: "CustomResourceDefinition", + apiVersions: ["apiextensions.k8s.io/v1"], + components: { + Details: (props: KubeObjectDetailsProps) => , + } } ]); } diff --git a/src/renderer/item.store.ts b/src/renderer/item.store.ts index 0a72352649..2b5a736abd 100644 --- a/src/renderer/item.store.ts +++ b/src/renderer/item.store.ts @@ -28,15 +28,15 @@ export interface ItemObject { getName(): string; } -export abstract class ItemStore { - abstract loadAll(...args: any[]): Promise; +export abstract class ItemStore { + abstract loadAll(...args: any[]): Promise; - protected defaultSorting = (item: T) => item.getName(); + protected defaultSorting = (item: Item) => item.getName(); @observable failedLoading = false; @observable isLoading = false; @observable isLoaded = false; - @observable items = observable.array([], { deep: false }); + @observable items = observable.array([], { deep: false }); @observable selectedItemsIds = observable.map(); constructor() { @@ -44,11 +44,11 @@ export abstract class ItemStore { autoBind(this); } - @computed get selectedItems(): T[] { + @computed get selectedItems(): Item[] { return this.items.filter(item => this.selectedItemsIds.get(item.getId())); } - public getItems(): T[] { + public getItems(): Item[] { return Array.from(this.items); } @@ -56,8 +56,8 @@ export abstract class ItemStore { return this.items.length; } - getByName(name: string, ...args: any[]): T; - getByName(name: string): T { + getByName(name: string, ...args: any[]): Item; + getByName(name: string): Item { return this.items.find(item => item.getName() === name); } @@ -75,13 +75,13 @@ export abstract class ItemStore { * @param order whether to sort from least to greatest (`"asc"` (default)) or vice-versa (`"desc"`) */ @action - protected sortItems(items: T[] = this.items, sorting: ((item: T) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): T[] { + protected sortItems(items: Item[] = this.items, sorting: ((item: Item) => any)[] = [this.defaultSorting], order?: "asc" | "desc"): Item[] { return orderBy(items, sorting, order); } protected async createItem(...args: any[]): Promise; @action - protected async createItem(request: () => Promise) { + protected async createItem(request: () => Promise) { const newItem = await request(); const item = this.items.find(item => item.getId() === newItem.getId()); @@ -98,7 +98,7 @@ export abstract class ItemStore { protected async loadItems(...args: any[]): Promise; @action - protected async loadItems(request: () => Promise, sortItems = true) { + protected async loadItems(request: () => Promise, sortItems = true) { if (this.isLoading) { await when(() => !this.isLoading); @@ -117,9 +117,9 @@ export abstract class ItemStore { } } - protected async loadItem(...args: any[]): Promise + protected async loadItem(...args: any[]): Promise @action - protected async loadItem(request: () => Promise, sortItems = true) { + protected async loadItem(request: () => Promise, sortItems = true) { const item = await Promise.resolve(request()).catch(() => null); if (item) { @@ -141,7 +141,7 @@ export abstract class ItemStore { } @action - protected async updateItem(item: T, request: () => Promise) { + protected async updateItem(item: Item, request: () => Promise) { const updatedItem = await request(); const index = this.items.findIndex(i => i.getId() === item.getId()); @@ -151,28 +151,28 @@ export abstract class ItemStore { } @action - protected async removeItem(item: T, request: () => Promise) { + protected async removeItem(item: Item, request: () => Promise) { await request(); this.items.remove(item); this.selectedItemsIds.delete(item.getId()); } - isSelected(item: T) { + isSelected(item: Item) { return !!this.selectedItemsIds.get(item.getId()); } @action - select(item: T) { + select(item: Item) { this.selectedItemsIds.set(item.getId(), true); } @action - unselect(item: T) { + unselect(item: Item) { this.selectedItemsIds.delete(item.getId()); } @action - toggleSelection(item: T) { + toggleSelection(item: Item) { if (this.isSelected(item)) { this.unselect(item); } else { @@ -181,7 +181,7 @@ export abstract class ItemStore { } @action - toggleSelectionAll(visibleItems: T[] = this.items) { + toggleSelectionAll(visibleItems: Item[] = this.items) { const allSelected = visibleItems.every(this.isSelected); if (allSelected) { @@ -191,7 +191,7 @@ export abstract class ItemStore { } } - isSelectedAll(visibleItems: T[] = this.items) { + isSelectedAll(visibleItems: Item[] = this.items) { if (!visibleItems.length) return false; return visibleItems.every(this.isSelected); diff --git a/src/renderer/kube-object.store.ts b/src/renderer/kube-object.store.ts index 881a9ae6f3..14dbbc6152 100644 --- a/src/renderer/kube-object.store.ts +++ b/src/renderer/kube-object.store.ts @@ -31,13 +31,13 @@ import { ensureObjectSelfLink, IKubeApiQueryParams, KubeApi, parseKubeApi } from import type { KubeJsonApiData } from "./api/kube-json-api"; import { Notifications } from "./components/notifications"; -export interface KubeObjectStoreLoadingParams { +export interface KubeObjectStoreLoadingParams { namespaces: string[]; - api?: KubeApi; + api?: KubeApi; reqInit?: RequestInit; } -export abstract class KubeObjectStore extends ItemStore { +export abstract class KubeObjectStore extends ItemStore { static defaultContext = observable.box(); // TODO: support multiple cluster contexts abstract api: KubeApi; @@ -137,7 +137,7 @@ export abstract class KubeObjectStore extends ItemSt } } - protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise { + protected async loadItems({ namespaces, api, reqInit }: KubeObjectStoreLoadingParams): Promise { if (this.context?.cluster.isAllowedResource(api.kind)) { if (!api.isNamespaced) { return api.list({ reqInit }, this.query); @@ -279,7 +279,7 @@ export abstract class KubeObjectStore extends ItemSt } async update(item: T, data: Partial): Promise { - const newItem = await item.update(data); + const newItem = await item.update(data); ensureObjectSelfLink(this.api, newItem); @@ -345,7 +345,7 @@ export abstract class KubeObjectStore extends ItemSt const { signal } = abortController; - const callback = (data: IKubeWatchEvent, error: any) => { + const callback = (data: IKubeWatchEvent, error: any) => { if (!this.isLoaded || error instanceof DOMException) return; if (error instanceof Response) { @@ -393,7 +393,7 @@ export abstract class KubeObjectStore extends ItemSt switch (type) { case "ADDED": case "MODIFIED": - const newItem = new api.objectConstructor(object); + const newItem = new api.objectConstructor(object) as T; if (!item) { items.push(newItem); From 0204eef848fc6d510ad532e1eec5e6fdc41266e2 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 22 Jul 2021 14:13:32 -0400 Subject: [PATCH 104/131] Filter out undefined values in hotbar-store migration (#3489) Signed-off-by: Sebastian Malton --- src/migrations/hotbar-store/5.0.0-beta.10.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/migrations/hotbar-store/5.0.0-beta.10.ts b/src/migrations/hotbar-store/5.0.0-beta.10.ts index 0bf780b201..3dd635effc 100644 --- a/src/migrations/hotbar-store/5.0.0-beta.10.ts +++ b/src/migrations/hotbar-store/5.0.0-beta.10.ts @@ -40,7 +40,7 @@ interface Pre500WorkspaceStoreModel { export default { version: "5.0.0-beta.10", run(store) { - const hotbars: Hotbar[] = store.get("hotbars"); + const hotbars = (store.get("hotbars") as Hotbar[] ?? []).filter(Boolean); const userDataPath = app.getPath("userData"); try { From da53aa0d79d7df21aaf4ab52ec83eeea53c79305 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 22 Jul 2021 20:28:01 -0400 Subject: [PATCH 105/131] Treat default weblinks same as any other weblinks (#3385) - Use seenWeblink and check for non-arrays - Spread out link validation Signed-off-by: Sebastian Malton --- package.json | 2 +- src/common/catalog-entities/web-link.ts | 4 +- src/common/weblink-store.ts | 2 +- src/main/catalog-sources/weblinks.ts | 108 ++++++++++++------------ src/migrations/weblinks-store/5.1.4.ts | 43 ++++++++++ src/migrations/weblinks-store/index.ts | 28 ++++++ 6 files changed, 132 insertions(+), 55 deletions(-) create mode 100644 src/migrations/weblinks-store/5.1.4.ts create mode 100644 src/migrations/weblinks-store/index.ts diff --git a/package.json b/package.json index d1f8a44576..4a9eddec0a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.1.3", + "version": "5.1.4", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index e18b3ac931..d54aa8533a 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -24,8 +24,10 @@ import { catalogCategoryRegistry } from "../catalog/catalog-category-registry"; import { productName } from "../vars"; import { WeblinkStore } from "../weblink-store"; +export type WebLinkStatusPhase = "available" | "unavailable"; + export interface WebLinkStatus extends CatalogEntityStatus { - phase: "available" | "unavailable"; + phase: WebLinkStatusPhase; } export type WebLinkSpec = { diff --git a/src/common/weblink-store.ts b/src/common/weblink-store.ts index a91f83afa2..234e17537a 100644 --- a/src/common/weblink-store.ts +++ b/src/common/weblink-store.ts @@ -21,7 +21,7 @@ import { action, comparer, observable, makeObservable } from "mobx"; import { BaseStore } from "./base-store"; -import migrations from "../migrations/hotbar-store"; +import migrations from "../migrations/weblinks-store"; import * as uuid from "uuid"; import { toJS } from "./utils"; diff --git a/src/main/catalog-sources/weblinks.ts b/src/main/catalog-sources/weblinks.ts index 2593fedc7c..c8e983f3be 100644 --- a/src/main/catalog-sources/weblinks.ts +++ b/src/main/catalog-sources/weblinks.ts @@ -19,43 +19,19 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { observable, reaction } from "mobx"; +import { computed, observable, reaction } from "mobx"; import { WeblinkStore } from "../../common/weblink-store"; import { WebLink } from "../../common/catalog-entities"; import { catalogEntityRegistry } from "../catalog"; import got from "got"; -import logger from "../logger"; -import { docsUrl, slackUrl } from "../../common/vars"; - -const defaultLinks = [ - { title: "Lens Website", url: "https://k8slens.dev" }, - { title: "Lens Documentation", url: docsUrl }, - { title: "Lens Community Slack", url: slackUrl }, - { title: "Kubernetes Documentation", url: "https://kubernetes.io/docs/home/" }, - { title: "Lens on Twitter", url: "https://twitter.com/k8slens" }, - { title: "Lens Official Blog", url: "https://medium.com/k8slens" } -].map((link) => ( - new WebLink({ - metadata: { - uid: link.url, - name: link.title, - source: "app", - labels: {} - }, - spec: { - url: link.url - }, - status: { - phase: "available", - active: true - } - }) -)); +import type { Disposer } from "../../common/utils"; +import { random } from "lodash"; async function validateLink(link: WebLink) { try { const response = await got.get(link.spec.url, { - throwHttpErrors: false + throwHttpErrors: false, + timeout: 20_000, }); if (response.statusCode >= 200 && response.statusCode < 500) { @@ -63,7 +39,7 @@ async function validateLink(link: WebLink) { } else { link.status.phase = "unavailable"; } - } catch(error) { + } catch { link.status.phase = "unavailable"; } } @@ -71,32 +47,60 @@ async function validateLink(link: WebLink) { export function syncWeblinks() { const weblinkStore = WeblinkStore.getInstance(); - const weblinks = observable.array(defaultLinks); + const webLinkEntities = observable.map(); + + function periodicallyCheckLink(link: WebLink): Disposer { + validateLink(link); + + let interval: NodeJS.Timeout; + const timeout = setTimeout(() => { + interval = setInterval(() => validateLink(link), 60 * 60 * 1000); // every 60 minutes + }, random(0, 5 * 60 * 1000, false)); // spread out over 5 minutes + + return () => { + clearTimeout(timeout); + clearInterval(interval); + }; + } reaction(() => weblinkStore.weblinks, (links) => { - weblinks.replace(links.map((data) => new WebLink({ - metadata: { - uid: data.id, - name: data.name, - source: "local", - labels: {} - }, - spec: { - url: data.url - }, - status: { - phase: "available", - active: true - } - }))); - weblinks.push(...defaultLinks); + const seenWeblinks = new Set(); - for (const link of weblinks) { - validateLink(link).catch((error) => { - logger.error(`failed to validate link ${link.spec.url}: %s`, error); - }); + for (const weblinkData of links) { + seenWeblinks.add(weblinkData.id); + + if (!webLinkEntities.has(weblinkData.id)) { + const link = new WebLink({ + metadata: { + uid: weblinkData.id, + name: weblinkData.name, + source: "local", + labels: {} + }, + spec: { + url: weblinkData.url + }, + status: { + phase: "available", + active: true + } + }); + + webLinkEntities.set(weblinkData.id, [ + link, + periodicallyCheckLink(link), + ]); + } + } + + // Stop checking and remove weblinks that are no longer in the store + for (const [weblinkId, [, disposer]] of webLinkEntities) { + if (!seenWeblinks.has(weblinkId)) { + disposer(); + webLinkEntities.delete(weblinkId); + } } }, {fireImmediately: true}); - catalogEntityRegistry.addObservableSource("weblinks", weblinks); + catalogEntityRegistry.addComputedSource("weblinks", computed(() => Array.from(webLinkEntities.values(), ([link]) => link))); } diff --git a/src/migrations/weblinks-store/5.1.4.ts b/src/migrations/weblinks-store/5.1.4.ts new file mode 100644 index 0000000000..84aaad5e6d --- /dev/null +++ b/src/migrations/weblinks-store/5.1.4.ts @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { docsUrl, slackUrl } from "../../common/vars"; +import type { WeblinkData } from "../../common/weblink-store"; +import type { MigrationDeclaration } from "../helpers"; + +export default { + version: "5.1.4", + run(store) { + const weblinksRaw: any = store.get("weblinks"); + const weblinks = (Array.isArray(weblinksRaw) ? weblinksRaw : []) as WeblinkData[]; + + weblinks.push( + { id: "https://k8slens.dev", name: "Lens Website", url: "https://k8slens.dev" }, + { id: docsUrl, name: "Lens Documentation", url: docsUrl }, + { id: slackUrl, name: "Lens Community Slack", url: slackUrl }, + { id: "https://kubernetes.io/docs/home/", name: "Kubernetes Documentation", url: "https://kubernetes.io/docs/home/" }, + { id: "https://twitter.com/k8slens", name: "Lens on Twitter", url: "https://twitter.com/k8slens" }, + { id: "https://medium.com/k8slens", name: "Lens Official Blog", url: "https://medium.com/k8slens" } + ); + + store.set("weblinks", weblinks); + } +} as MigrationDeclaration; diff --git a/src/migrations/weblinks-store/index.ts b/src/migrations/weblinks-store/index.ts new file mode 100644 index 0000000000..1646b07810 --- /dev/null +++ b/src/migrations/weblinks-store/index.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { joinMigrations } from "../helpers"; + +import version514 from "./5.1.4"; + +export default joinMigrations( + version514, +); From 823d68014e5b4d834fa147d381d511ab4415e470 Mon Sep 17 00:00:00 2001 From: Jari Kolehmainen Date: Fri, 23 Jul 2021 10:54:30 +0300 Subject: [PATCH 106/131] fix entity settings menu order (#3494) Signed-off-by: Jari Kolehmainen --- .../components/+entity-settings/entity-settings.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/renderer/components/+entity-settings/entity-settings.tsx b/src/renderer/components/+entity-settings/entity-settings.tsx index ad0713e342..f656b6fa9d 100644 --- a/src/renderer/components/+entity-settings/entity-settings.tsx +++ b/src/renderer/components/+entity-settings/entity-settings.tsx @@ -82,6 +82,13 @@ export class EntitySettings extends React.Component { renderNavigation() { const groups = Object.entries(groupBy(this.menuItems, (item) => item.group || "Extensions")); + groups.sort((a, b) => { + if (a[0] === "Settings") return -1; + if (a[0] === "Extensions") return 1; + + return a[0] <= b[0] ? -1 : 1; + }); + return ( <>
From 44fad7f45969d9a77ebc2b20688293e9e79f4d97 Mon Sep 17 00:00:00 2001 From: Alex Andreev Date: Fri, 23 Jul 2021 14:36:48 +0300 Subject: [PATCH 107/131] Fix input default value warning (#3497) * Removing some props from passing to Signed-off-by: Alex Andreev * Fixing ns loadMetrics() this context Signed-off-by: Alex Andreev --- src/renderer/components/+namespaces/namespace-details.tsx | 3 ++- src/renderer/components/input/input.tsx | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/+namespaces/namespace-details.tsx b/src/renderer/components/+namespaces/namespace-details.tsx index f61db41711..ee051977e5 100644 --- a/src/renderer/components/+namespaces/namespace-details.tsx +++ b/src/renderer/components/+namespaces/namespace-details.tsx @@ -25,7 +25,7 @@ import React from "react"; import { computed, makeObservable, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import { DrawerItem } from "../drawer"; -import { cssNames } from "../../utils"; +import { boundMethod, cssNames } from "../../utils"; import { getMetricsForNamespace, IPodMetrics, Namespace } from "../../api/endpoints"; import { getDetailsUrl, KubeObjectDetailsProps } from "../kube-object"; import { Link } from "react-router-dom"; @@ -72,6 +72,7 @@ export class NamespaceDetails extends React.Component { return limitRangeStore.getAllByNs(namespace); } + @boundMethod async loadMetrics() { this.metrics = await getMetricsForNamespace(this.props.object.getName(), ""); } diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index 91b58c24b5..062d61f2c6 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -328,6 +328,8 @@ export class Input extends React.Component { multiLine, showValidationLine, validators, theme, maxRows, children, showErrorsAsTooltip, maxLength, rows, disabled, autoSelectOnFocus, iconLeft, iconRight, contentRight, id, dirty: _dirty, // excluded from passing to input-element + defaultValue, + trim, ...inputProps } = this.props; const { focused, dirty, valid, validating, errors } = this.state; From 239bfec99c1e9313011438818ef776bccdaa1c36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jul 2021 09:02:16 -0400 Subject: [PATCH 108/131] Bump @types/hapi from 18.0.5 to 18.0.6 (#3468) Bumps [@types/hapi](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/hapi) from 18.0.5 to 18.0.6. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/hapi) --- updated-dependencies: - dependency-name: "@types/hapi" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 21 +++++---------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 4a9eddec0a..f8e89a40ba 100644 --- a/package.json +++ b/package.json @@ -266,7 +266,7 @@ "@types/electron-devtools-installer": "^2.2.0", "@types/electron-window-state": "^2.0.34", "@types/fs-extra": "^9.0.1", - "@types/hapi": "^18.0.5", + "@types/hapi": "^18.0.6", "@types/hoist-non-react-statics": "^3.3.1", "@types/html-webpack-plugin": "^3.2.3", "@types/http-proxy": "^1.17.5", diff --git a/yarn.lock b/yarn.lock index 2bc6562fda..1eee071f2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1436,10 +1436,10 @@ dependencies: "@types/node" "*" -"@types/hapi@^18.0.5": - version "18.0.5" - resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-18.0.5.tgz#7573fc83ec1d8ecf127b93bd326266da533fe30b" - integrity sha512-OnBslvAL//tsTZemW8wSGrVUj7BgtS16kBicgKCtEpHle9A3pOd7X4ViykKv/X3rh7g636mDzoqQ27+Ols5GQw== +"@types/hapi@^18.0.6": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-18.0.6.tgz#092ef4afbb7f5e32fcad429c4d3341cb696fdf15" + integrity sha512-0UvpE7afOsWbDuSWv1W2kLAacEvhDGNnGwsiNe0IIw6o4XumZxH9Z850ZBLc4DywzwwJ8FDPneCpy2hJ1OzmSg== dependencies: "@types/boom" "*" "@types/catbox" "*" @@ -8678,18 +8678,7 @@ jest@26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" -joi@^17.3.0: - version "17.3.0" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.3.0.tgz#f1be4a6ce29bc1716665819ac361dfa139fff5d2" - integrity sha512-Qh5gdU6niuYbUIUV5ejbsMiiFmBdw8Kcp8Buj2JntszCkCfxJ9Cz76OtHxOZMPXrt5810iDIXs+n1nNVoquHgg== - dependencies: - "@hapi/hoek" "^9.0.0" - "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.0" - "@sideway/formula" "^3.0.0" - "@sideway/pinpoint" "^2.0.0" - -joi@^17.4.0: +joi@^17.3.0, joi@^17.4.0: version "17.4.0" resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.0.tgz#b5c2277c8519e016316e49ababd41a1908d9ef20" integrity sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg== From 07532b15527a92dec302cd20401b52a9641e5b13 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 23 Jul 2021 09:26:23 -0400 Subject: [PATCH 109/131] Display hint to user on how to add items in an EditableList (#3475) Signed-off-by: Sebastian Malton --- .../editable-list/editable-list.tsx | 5 +-- src/renderer/components/input/input.tsx | 42 +++++++++++++++---- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/renderer/components/editable-list/editable-list.tsx b/src/renderer/components/editable-list/editable-list.tsx index 61bf70bb70..3a16263ff4 100644 --- a/src/renderer/components/editable-list/editable-list.tsx +++ b/src/renderer/components/editable-list/editable-list.tsx @@ -53,11 +53,9 @@ export class EditableList extends React.Component> { @boundMethod onSubmit(val: string, evt: React.KeyboardEvent) { - const { add } = this.props; - if (val) { evt.preventDefault(); - add(val); + this.props.add(val); } } @@ -72,6 +70,7 @@ export class EditableList extends React.Component> { onSubmit={this.onSubmit} validators={validators} placeholder={placeholder} + iconRight={({ isDirty }) => isDirty ? : null} />
diff --git a/src/renderer/components/input/input.tsx b/src/renderer/components/input/input.tsx index 062d61f2c6..e1d39d9835 100644 --- a/src/renderer/components/input/input.tsx +++ b/src/renderer/components/input/input.tsx @@ -27,7 +27,6 @@ import { Icon } from "../icon"; import { Tooltip, TooltipProps } from "../tooltip"; import * as Validators from "./input_validators"; import type { InputValidator } from "./input_validators"; -import isString from "lodash/isString"; import isFunction from "lodash/isFunction"; import isBoolean from "lodash/isBoolean"; import uniqueId from "lodash/uniqueId"; @@ -41,6 +40,18 @@ export type { InputValidator }; type InputElement = HTMLInputElement | HTMLTextAreaElement; type InputElementProps = InputHTMLAttributes & TextareaHTMLAttributes & DOMAttributes; +export interface IconDataFnArg { + isDirty: boolean; +} + +/** + * One of the folloing: + * - A material icon name + * - A react node + * - Or a function that produces a react node + */ +export type IconData = string | React.ReactNode | ((opt: IconDataFnArg) => React.ReactNode); + export type InputProps = Omit & { theme?: "round-black" | "round"; className?: string; @@ -53,8 +64,8 @@ export type InputProps = Omit & { dirty?: boolean; // show validation errors even if the field wasn't touched yet showValidationLine?: boolean; // show animated validation line for async validators showErrorsAsTooltip?: boolean | Omit; // show validation errors as a tooltip :hover (instead of block below) - iconLeft?: string | React.ReactNode; // material-icon name in case of string-type - iconRight?: string | React.ReactNode; + iconLeft?: IconData; + iconRight?: IconData; contentRight?: string | React.ReactNode; // Any component of string goes after iconRight validators?: InputValidator | InputValidator[]; onChange?(value: string, evt: React.ChangeEvent): void; @@ -75,7 +86,6 @@ const defaultProps: Partial = { maxRows: 10000, showValidationLine: true, validators: [], - defaultValue: "", }; export class Input extends React.Component { @@ -105,7 +115,7 @@ export class Input extends React.Component { } getValue(): string { - const { trim, value, defaultValue } = this.props; + const { trim, value, defaultValue = "" } = this.props; const rawValue = value ?? this.input?.value ?? defaultValue; return trim ? rawValue.trim() : rawValue; @@ -262,6 +272,10 @@ export class Input extends React.Component { this.props.onSubmit?.(this.getValue(), evt); this.setDirtyOnChange.cancel(); this.setState({ submitted: true }); + + if (this.input && typeof this.props.value !== "string") { + this.input.value = ""; + } } else { this.setDirty(); } @@ -323,6 +337,20 @@ export class Input extends React.Component { this.input = elem; } + private renderIcon(iconData: IconData) { + if (typeof iconData === "string") { + return ; + } + + if (typeof iconData === "function") { + return iconData({ + isDirty: Boolean(this.getValue()), + }); + } + + return iconData; + } + render() { const { multiLine, showValidationLine, validators, theme, maxRows, children, showErrorsAsTooltip, @@ -383,9 +411,9 @@ export class Input extends React.Component {
{tooltipError}