mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Convert runMany and runManySync to use injectManyWithMeta + move to seperate package (#7244)
* Convert runMany and runManySync to use injectManyWithMeta Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup type errors due to new Runnable requirements Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add documentation for verifyRunnablesAreDAG Signed-off-by: Sebastian Malton <sebastian@malton.name> * Simplify convertToWithIdWith Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move all utility functions to separate package Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move testing utilities to separate package Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move run-many and run-many-sync to separate package Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace all internal uses of utilities with new packages Signed-off-by: Sebastian Malton <sebastian@malton.name> * Use new @k8slens/run-many package in core Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add dep to open-lens Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup uses of @k8slens/test-utils Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup getGlobalOverride Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move tests to new package too Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup uses of AsyncResult and autoBind Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup remaining import issues Signed-off-by: Sebastian Malton <sebastian@malton.name> * Finial fixups to fix build Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * Revert moving "testUsingFakeTime" to separate package - This fixes tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix integration tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix unit test failing due to spelling fix Signed-off-by: Sebastian Malton <sebastian@malton.name> --------- Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
ad9bafe2a5
commit
2789bcebcb
754 files changed
+2715
-2752
No files matched your search
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getConvertedParts } from "../name-parts";
|
||||
|
||||
describe("getConvertedParts", () => {
|
||||
it.each([
|
||||
["hello", ["hello"]],
|
||||
["hello.goodbye", ["hello", "goodbye"]],
|
||||
["hello.1", ["hello", 1]],
|
||||
["3-hello.1", [3, "hello", 1]],
|
||||
["3_hello.1", [3, "hello", 1]],
|
||||
["3_hello.1/foobar", [3, "hello", 1, "foobar"]],
|
||||
["3_hello.1/foobar\\new", [3, "hello", 1, "foobar", "new"]],
|
||||
])("Splits '%s' as into %j", (input, output) => {
|
||||
expect(getConvertedParts(input)).toEqual(output);
|
||||
});
|
||||
});
|
||||
@@ -1,178 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { convertKubectlJsonPathToNodeJsonPath, safeJSONPathValue } from "../jsonPath";
|
||||
|
||||
describe("convertKubectlJsonPathToNodeJsonPath", () => {
|
||||
it("should convert \\. to use indexed notation", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels.kubesphere\\.io/alias-name");
|
||||
|
||||
expect(res).toBe("$.metadata.labels['kubesphere.io/alias-name']");
|
||||
});
|
||||
|
||||
it("should convert keys with escaped characters to use indexed notation", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels.kubesphere\\\"io/alias-name");
|
||||
|
||||
expect(res).toBe("$.metadata.labels['kubesphere\"io/alias-name']");
|
||||
});
|
||||
|
||||
it("should convert '-' to use indexed notation", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels.alias-name");
|
||||
|
||||
expect(res).toBe("$.metadata.labels['alias-name']");
|
||||
});
|
||||
|
||||
it("should drop leading dot if first group is converted to index notation", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata\\.labels.alias-name");
|
||||
|
||||
expect(res).toBe("$['metadata.labels']['alias-name']");
|
||||
});
|
||||
|
||||
it("should handle scenario when both \\. and indexed notation are present", () => {
|
||||
const rest = convertKubectlJsonPathToNodeJsonPath(".metadata.labels\\.serving['some.other.item']");
|
||||
|
||||
expect(rest).toBe("$.metadata['labels.serving']['some.other.item']");
|
||||
});
|
||||
|
||||
it("should not touch given jsonPath if no invalid characters present", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".status.conditions[?(@.type=='Ready')].status");
|
||||
|
||||
expect(res).toBe("$.status.conditions[?(@.type=='Ready')].status");
|
||||
});
|
||||
|
||||
it("strips '\\' away from the result", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels['serving\\.knative\\.dev/configuration']");
|
||||
|
||||
expect(res).toBe("$.metadata.labels['serving.knative.dev/configuration']");
|
||||
});
|
||||
|
||||
it("converts all [] to [0]", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels[].foo[]");
|
||||
|
||||
expect(res).toBe("$.metadata.labels[0].foo[0]");
|
||||
});
|
||||
|
||||
it("removes trailing ..", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels[]..");
|
||||
|
||||
expect(res).toBe("$.metadata.labels[0]");
|
||||
});
|
||||
|
||||
it("converts ending ...name to ..name", () => {
|
||||
const res = convertKubectlJsonPathToNodeJsonPath(".metadata.labels[]...name");
|
||||
|
||||
expect(res).toBe("$.metadata.labels[0]..name");
|
||||
});
|
||||
});
|
||||
|
||||
describe("safeJSONPathValue", () => {
|
||||
let oldWarn: typeof console["warn"];
|
||||
|
||||
beforeEach(() => {
|
||||
oldWarn = console.warn;
|
||||
console.warn = jest.fn();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.warn = oldWarn;
|
||||
});
|
||||
|
||||
it("should convert boolean values to strings", () => {
|
||||
const res = safeJSONPathValue({ bar: false }, ".bar");
|
||||
|
||||
expect(res).toBe(false);
|
||||
});
|
||||
|
||||
it("should convert number values to strings", () => {
|
||||
const res = safeJSONPathValue({ bar: 0 }, ".bar");
|
||||
|
||||
expect(res).toBe(0);
|
||||
});
|
||||
|
||||
it("should join sliced entries with commas only", () => {
|
||||
const res = safeJSONPathValue({
|
||||
bar: [
|
||||
{
|
||||
foo: 1,
|
||||
},
|
||||
{
|
||||
foo: "hello",
|
||||
},
|
||||
],
|
||||
}, ".bar[].foo");
|
||||
|
||||
expect(res).toBe(1);
|
||||
});
|
||||
|
||||
it("should join an array of values using JSON.stringify", () => {
|
||||
const res = safeJSONPathValue({
|
||||
bar: [
|
||||
"world",
|
||||
"hello",
|
||||
],
|
||||
}, ".bar");
|
||||
|
||||
expect(res).toEqual(["world", "hello"]);
|
||||
});
|
||||
|
||||
it("should stringify an object value", () => {
|
||||
const res = safeJSONPathValue({
|
||||
foo: { bar: "bat" },
|
||||
}, ".foo");
|
||||
|
||||
expect(res).toEqual({ "bar":"bat" });
|
||||
});
|
||||
|
||||
it("should use convertKubectlJsonPathToNodeJsonPath", () => {
|
||||
const res = safeJSONPathValue({
|
||||
foo: { "hello.world": "bat" },
|
||||
}, ".foo.hello\\.world");
|
||||
|
||||
expect(res).toBe("bat");
|
||||
});
|
||||
|
||||
it("should not throw when given '.spec.metrics[*].external.highWatermark..'", () => {
|
||||
const obj = {
|
||||
spec: {
|
||||
metrics: [
|
||||
{
|
||||
external: {
|
||||
metricName: "cpu",
|
||||
highWatermark: "100",
|
||||
},
|
||||
},
|
||||
{
|
||||
external: {
|
||||
metricName: "memory",
|
||||
highWatermark: "100",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const res = safeJSONPathValue(obj, ".spec.metrics[*].external.highWatermark..");
|
||||
|
||||
expect(res).toEqual(["100", "100"]);
|
||||
});
|
||||
|
||||
it("should not throw if path is invalid jsonpath", () => {
|
||||
const res = safeJSONPathValue({
|
||||
foo: { "hello.world": "bat" },
|
||||
}, "asd[");
|
||||
|
||||
expect(res).toBe(undefined);
|
||||
});
|
||||
|
||||
it("should retrive value with '/' in jsonpath", () => {
|
||||
const res = safeJSONPathValue({
|
||||
foo: {
|
||||
"hello/world": "bat",
|
||||
},
|
||||
}, ".foo.hello/world");
|
||||
|
||||
expect(res).toBe("bat");
|
||||
});
|
||||
});
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { metricUnitsToNumber } from "../metricUnitsToNumber";
|
||||
|
||||
describe("metricUnitsToNumber tests", () => {
|
||||
test("plain number", () => {
|
||||
expect(metricUnitsToNumber("124")).toStrictEqual(124);
|
||||
});
|
||||
|
||||
test("with k suffix", () => {
|
||||
expect(metricUnitsToNumber("124k")).toStrictEqual(124000);
|
||||
});
|
||||
|
||||
test("with m suffix", () => {
|
||||
expect(metricUnitsToNumber("124m")).toStrictEqual(124000000);
|
||||
});
|
||||
});
|
||||
-1
@@ -10,7 +10,6 @@ const startListeningOfChannelsInjectable = getInjectable({
|
||||
id: "start-listening-of-channels-renderer",
|
||||
|
||||
instantiate: (di) => ({
|
||||
id: "start-listening-of-channels-renderer",
|
||||
run: () => {
|
||||
const listeningOfChannels = di.inject(listeningOnMessageChannelsInjectable);
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
import type { IpcRenderer } from "electron";
|
||||
import ipcRendererInjectable from "./ipc-renderer.injectable";
|
||||
import { getGlobalOverride } from "../../../common/test-utils/get-global-override";
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
|
||||
export default getGlobalOverride(ipcRendererInjectable, () => ({
|
||||
invoke: () => {},
|
||||
|
||||
@@ -20,7 +20,6 @@ import storageSaveDelayInjectable from "./storage-save-delay.injectable";
|
||||
const initializeStateInjectable = getInjectable({
|
||||
id: "initialize-lens-local-storage-state",
|
||||
instantiate: (di) => ({
|
||||
id: "initialize-lens-local-storage-state",
|
||||
run: async () => {
|
||||
const joinPaths = di.inject(joinPathsInjectable);
|
||||
const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable);
|
||||
@@ -67,7 +66,7 @@ const initializeStateInjectable = getInjectable({
|
||||
}
|
||||
}
|
||||
},
|
||||
runAfter: di.inject(setupAppPathsInjectable),
|
||||
runAfter: setupAppPathsInjectable,
|
||||
}),
|
||||
injectionToken: beforeFrameStartsFirstInjectionToken,
|
||||
});
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getGlobalOverride } from "../../../common/test-utils/get-global-override";
|
||||
import { getGlobalOverride } from "@k8slens/test-utils";
|
||||
import storageSaveDelayInjectable from "./storage-save-delay.injectable";
|
||||
|
||||
export default getGlobalOverride(storageSaveDelayInjectable, () => 0);
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { iter } from "../../common/utils";
|
||||
|
||||
export type IgnoredClassNames = number | symbol | Function;
|
||||
export type IClassName = string | string[] | Record<string, any> | undefined | null | false | IgnoredClassNames;
|
||||
|
||||
export function cssNames(...classNames: IClassName[]): string {
|
||||
const classNamesEnabled = new Map<string, boolean>();
|
||||
|
||||
for (const className of classNames) {
|
||||
if (typeof className === "string") {
|
||||
classNamesEnabled.set(className, true);
|
||||
} else if (Array.isArray(className)) {
|
||||
for (const name of className) {
|
||||
classNamesEnabled.set(name, true);
|
||||
}
|
||||
} else if (className && typeof className === "object") {
|
||||
for (const [name, value] of Object.entries(className)) {
|
||||
classNamesEnabled.set(name, Boolean(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return iter.chain(classNamesEnabled.entries())
|
||||
.filter(([, isActive]) => !!isActive)
|
||||
.filterMap(([className]) => className.trim())
|
||||
.join(" ");
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Helper for getting/setting css-variables
|
||||
|
||||
export function cssVar(elem: HTMLElement) {
|
||||
return {
|
||||
get(name: string) {
|
||||
const value = window.getComputedStyle(elem).getPropertyValue(name).trim();
|
||||
|
||||
return {
|
||||
toString: () => value,
|
||||
valueOf: () => parseFloat(value),
|
||||
};
|
||||
},
|
||||
set(name: string, value: number | string) {
|
||||
if (typeof value === "number") value = `${value}px`;
|
||||
elem.style.setProperty(name, value);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Format `mode` in octal notation
|
||||
*/
|
||||
export function displayMode(mode: number): string {
|
||||
return `0o${mode.toString(8)}`;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Common usage utils & helpers
|
||||
export * from "../../common/utils";
|
||||
export * from "../../common/event-emitter";
|
||||
|
||||
export * from "./cssNames";
|
||||
export * from "./cssVar";
|
||||
export * from "./display-mode";
|
||||
export * from "./interval";
|
||||
export * from "./isMiddleClick";
|
||||
export * from "./isReactNode";
|
||||
export * from "./metricUnitsToNumber";
|
||||
export * from "./name-parts";
|
||||
export * from "./prevDefault";
|
||||
export * from "./saveFile";
|
||||
export * from "./storage-helper";
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Helper for working with time updates / data-polling callbacks
|
||||
|
||||
export interface IntervalFn {
|
||||
start(runImmediately?: boolean): void;
|
||||
stop(): void;
|
||||
restart(runImmediately?: boolean): void;
|
||||
readonly isRunning: boolean;
|
||||
}
|
||||
|
||||
export function interval(timeSec = 1, callback: (count: number) => void, autoRun = false): IntervalFn {
|
||||
let count = 0;
|
||||
let timer = -1;
|
||||
let isRunning = false;
|
||||
const intervalManager = {
|
||||
start(runImmediately = false) {
|
||||
if (isRunning) return;
|
||||
const tick = () => callback(++count);
|
||||
|
||||
isRunning = true;
|
||||
timer = window.setInterval(tick, 1000 * timeSec);
|
||||
if (runImmediately) tick();
|
||||
},
|
||||
stop() {
|
||||
count = 0;
|
||||
isRunning = false;
|
||||
clearInterval(timer);
|
||||
},
|
||||
restart(runImmediately = false) {
|
||||
this.stop();
|
||||
this.start(runImmediately);
|
||||
},
|
||||
get isRunning() {
|
||||
return isRunning;
|
||||
},
|
||||
};
|
||||
|
||||
if (autoRun) intervalManager.start();
|
||||
|
||||
return intervalManager;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type React from "react";
|
||||
|
||||
/**
|
||||
* Returns `true` if `node` is a falsy value
|
||||
*/
|
||||
export function isNodeFalsy(node: React.ReactNode): boolean {
|
||||
return !isNodeRenderable(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `true` if React would render this
|
||||
*/
|
||||
export function isNodeRenderable(node: React.ReactNode): boolean {
|
||||
return Boolean(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first react node provided that is would be rendered by react
|
||||
*/
|
||||
export function foldNodes(...nodes: React.ReactNode[]): React.ReactNode {
|
||||
return nodes.find(isNodeRenderable);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type React from "react";
|
||||
|
||||
// Helper for inlining middleClick checks
|
||||
// <form onAuxClick={isMiddleClick(() => console.log('do some action'))}>
|
||||
// <input name="text"/>
|
||||
// <button type="submit">Action</button>
|
||||
// </form>
|
||||
|
||||
export function isMiddleClick<E extends React.MouseEvent>(callback: (evt: E) => any) {
|
||||
return function (evt: E) {
|
||||
if(evt.button === 1) {
|
||||
return callback(evt);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
// Type guard for checking valid react node to use in render
|
||||
import type { ReactNode } from "react";
|
||||
import React from "react";
|
||||
import { isObject } from "../../common/utils";
|
||||
|
||||
export function isReactNode(node: unknown): node is ReactNode {
|
||||
return (isObject(node) && React.isValidElement(node))
|
||||
|| Array.isArray(node) && node.every(isReactNode)
|
||||
|| node == null
|
||||
|| typeof node !== "object";
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { JSONPath } from "@astronautlabs/jsonpath";
|
||||
import { TypedRegEx } from "typed-regex";
|
||||
|
||||
const slashDashSearch = /[/\\-]/g;
|
||||
const pathByBareDots = /(?<=\w)\./;
|
||||
const textBeforeFirstSquare = /^.*(?=\[)/g;
|
||||
const backSlash = /\\/g;
|
||||
const kubectlOptionPrefix = TypedRegEx("^\\$?\\.?(?<pathExpression>.*)");
|
||||
const sliceVersion = /\[]/g;
|
||||
const tripleDotName = /\.\.\.(?<trailing>.)/g;
|
||||
const trailingDotDot = /\.\.$/;
|
||||
|
||||
/**
|
||||
* The GO package that kubectl and kubernetes uses for its JSONpath implementation has some
|
||||
* shorthand conveniences that are not part of the official spec. This function tries to convert
|
||||
* those shorthands to the official spec.
|
||||
*
|
||||
* Known shorthands:
|
||||
* - Leading `$` is optional (but implied)
|
||||
* - The string `/` can be used without a leading `\` escapement
|
||||
* - The string `\.` is used to denote the "value of '.'" and not "next key"
|
||||
* - The string `-` can be used while not in quotes
|
||||
* - `[]` as shorthand for `[0]`
|
||||
* - Remove `..` at the end of a path, we will just format it slightly differently
|
||||
* - Allow `...foo` as well as `..foo`
|
||||
*/
|
||||
export function convertKubectlJsonPathToNodeJsonPath(jsonPath: string) {
|
||||
const captures = kubectlOptionPrefix.captures(jsonPath);
|
||||
let start = "$";
|
||||
|
||||
if (!captures) {
|
||||
return start;
|
||||
}
|
||||
|
||||
let { pathExpression } = captures;
|
||||
|
||||
if (pathExpression.match(slashDashSearch)) {
|
||||
const [first, ...rest] = pathExpression.split(pathByBareDots);
|
||||
|
||||
pathExpression = `${convertToIndexNotation(first, true)}${rest.map(value => convertToIndexNotation(value)).join("")}`;
|
||||
}
|
||||
|
||||
pathExpression = pathExpression.replace(trailingDotDot, "");
|
||||
pathExpression = pathExpression.replace(tripleDotName, "..$<trailing>");
|
||||
|
||||
if (!pathExpression.startsWith("[")) {
|
||||
start += ".";
|
||||
}
|
||||
|
||||
// strip '\' characters from the result
|
||||
return `${start}${pathExpression.replace(backSlash, "").replace(sliceVersion, "[0]")}`;
|
||||
}
|
||||
|
||||
function convertToIndexNotation(key: string, firstItem = false) {
|
||||
if (key.match(slashDashSearch)) {
|
||||
if (key.includes("[")) { // handle cases where key contains [...]
|
||||
const keyToConvert = key.match(textBeforeFirstSquare); // get the text from the key before '['
|
||||
|
||||
if (keyToConvert && keyToConvert[0].match(slashDashSearch)) { // check if that part contains illegal characters
|
||||
return key.replace(keyToConvert[0], `['${keyToConvert[0]}']`); // surround key with '[' and ']'
|
||||
} else {
|
||||
return `.${key}`; // otherwise return as is with leading '.'
|
||||
}
|
||||
}
|
||||
|
||||
return `['${key}']`;
|
||||
} else { // no illegal characters found, do not touch
|
||||
const prefix = firstItem ? "" : ".";
|
||||
|
||||
return `${prefix}${key}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function formatJSONValue(value: unknown): string {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(formatJSONValue).join(", ");
|
||||
}
|
||||
|
||||
if (typeof value === "object") {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
return String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a safer version of `JSONPath.value(obj, path)` with untrusted jsonpath strings
|
||||
*
|
||||
* This function will also stringify the value retreived from the object
|
||||
*/
|
||||
export function safeJSONPathValue(obj: object, path: string): unknown {
|
||||
try {
|
||||
const parsedPath = JSONPath.parse(convertKubectlJsonPathToNodeJsonPath(path));
|
||||
const isSlice = parsedPath.some((exp: any) => exp.expression.type === "slice" || exp.expression.type === "wildcard");
|
||||
const value = JSONPath.query(obj, JSONPath.stringify(parsedPath), isSlice ? Infinity : 1);
|
||||
|
||||
if (isSlice) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return value[0];
|
||||
} catch (error) {
|
||||
// something failed
|
||||
console.warn("[JSON-PATH]: failed to parse jsonpath", error);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
const base = 1000;
|
||||
const suffixes = ["k", "m", "g", "t", "q"];
|
||||
|
||||
export function metricUnitsToNumber(value: string): number {
|
||||
const suffix = value.toLowerCase().slice(-1);
|
||||
const index = suffixes.indexOf(suffix);
|
||||
|
||||
return parseInt(
|
||||
(parseFloat(value) * Math.pow(base, index + 1)).toFixed(1),
|
||||
);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Split `name` into the parts seperated by one or more of (-, _, or .) and
|
||||
* the sections can be converted to numbers will be converted
|
||||
* @param name A kube object name
|
||||
* @returns The converted parts of the name
|
||||
*/
|
||||
export function getConvertedParts(name: string): (string | number)[] {
|
||||
return name
|
||||
.split(/[-_./\\]+/)
|
||||
.map(part => {
|
||||
const converted = +part;
|
||||
|
||||
return isNaN(converted) ? part : converted;
|
||||
});
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { WindowEventListener } from "../window/event-listener.injectable";
|
||||
|
||||
function parseKeyDownDescriptor(descriptor: string): (event: KeyboardEvent) => boolean {
|
||||
const parts = new Set((
|
||||
descriptor
|
||||
.split("+")
|
||||
.filter(Boolean)
|
||||
.map(part => part.toLowerCase())
|
||||
));
|
||||
|
||||
if (parts.size === 0) {
|
||||
return () => true;
|
||||
}
|
||||
|
||||
const hasShift = parts.delete("shift");
|
||||
const hasAlt = parts.delete("alt");
|
||||
|
||||
const rawHasCtrl = parts.delete("ctrl");
|
||||
const rawHasControl = parts.delete("control");
|
||||
const hasCtrl = rawHasCtrl || rawHasControl;
|
||||
|
||||
const rawHasMeta = parts.delete("meta");
|
||||
const rawHasCmd = parts.delete("cmd");
|
||||
const hasMeta = rawHasCmd || rawHasMeta; // This means either matches
|
||||
|
||||
const [key, ...rest] = [...parts];
|
||||
|
||||
if (rest.length !== 0) {
|
||||
throw new Error("only single key combinations are currently supported");
|
||||
}
|
||||
|
||||
return (event) => {
|
||||
return event.altKey === hasAlt
|
||||
&& event.shiftKey === hasShift
|
||||
&& event.ctrlKey === hasCtrl
|
||||
&& event.metaKey === hasMeta
|
||||
&& event.key.toLowerCase() === key.toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
export function onKeyboardShortcut(descriptor: string, action: () => void): WindowEventListener<"keydown"> {
|
||||
const isMatchingEvent = parseKeyDownDescriptor(descriptor);
|
||||
|
||||
return (event) => {
|
||||
if (isMatchingEvent(event)) {
|
||||
action();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type React from "react";
|
||||
|
||||
// Helper for preventing default event action and performing custom callback
|
||||
// 1)
|
||||
// <form onSubmit={prevDefault(() => console.log('do some action'))}>
|
||||
// <input name="text"/>
|
||||
// <button type="submit">Action</button>
|
||||
// </form>
|
||||
// 2)
|
||||
// <a href="/some-page-url">
|
||||
// <span>Link text</span>
|
||||
// <Icon onClick={prevDefault(() => console.log('stay on the page and open dialog'))}/>
|
||||
// </a>
|
||||
|
||||
export function prevDefault<E extends React.SyntheticEvent | Event, R>(callback: (evt: E) => R): (event: E) => R {
|
||||
return function (evt: E) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
|
||||
return callback(evt);
|
||||
};
|
||||
}
|
||||
|
||||
export function stopPropagation(evt: Event | React.SyntheticEvent) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import type { KubeResource } from "../../common/rbac";
|
||||
import { apiResourceRecord } from "../../common/rbac";
|
||||
import { object } from "../../common/utils";
|
||||
import { object } from "@k8slens/utilities";
|
||||
|
||||
export const ResourceNames: Record<KubeResource, string> = {
|
||||
"namespaces": "Namespaces",
|
||||
|
||||
-1
@@ -15,7 +15,6 @@ const provideInitialValuesForSyncBoxesInjectable = getInjectable({
|
||||
id: "provide-initial-values-for-sync-boxes",
|
||||
|
||||
instantiate: (di) => ({
|
||||
id: "provide-initial-values-for-sync-boxes",
|
||||
run: async () => {
|
||||
const requestFromChannel = di.inject(requestFromChannelInjectionToken);
|
||||
const syncBoxes = di.injectMany(syncBoxInjectionToken);
|
||||
|
||||
Reference in new issue
Block a user