From 159d4c4189209e340efce8f55f19386f5c3f154f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 21 Oct 2021 10:04:23 -0400 Subject: [PATCH] Add several more util functions and tests - array.filled() - tuple.filled() - tuple.zip() (formarlly array.zipStrict()) - string.repeated() Signed-off-by: Sebastian Malton --- src/common/hotbar-types.ts | 4 +- src/common/utils/__tests__/string.test.ts | 38 ++++++++++++ src/common/utils/__tests__/tuple.test.ts | 59 +++++++++++++++++++ src/common/utils/array.ts | 25 ++------ src/common/utils/index.ts | 9 ++- src/common/utils/string.ts | 45 ++++++++++++++ src/common/utils/tuple.ts | 54 +++++++++++++++++ src/main/routes/metrics-route.ts | 4 +- .../+service-accounts/secret.tsx | 4 +- src/renderer/components/dock/log-list.tsx | 4 +- src/renderer/components/table/sorting.ts | 4 +- 11 files changed, 218 insertions(+), 32 deletions(-) create mode 100644 src/common/utils/__tests__/string.test.ts create mode 100644 src/common/utils/__tests__/tuple.test.ts create mode 100644 src/common/utils/string.ts create mode 100644 src/common/utils/tuple.ts diff --git a/src/common/hotbar-types.ts b/src/common/hotbar-types.ts index f8fc5a225b..ee65071e0e 100644 --- a/src/common/hotbar-types.ts +++ b/src/common/hotbar-types.ts @@ -20,7 +20,7 @@ */ import * as uuid from "uuid"; -import type { Tuple } from "./utils"; +import { tuple, Tuple } from "./utils"; export interface HotbarItem { entity: { @@ -46,7 +46,7 @@ export const defaultHotbarCells = 12; // Number is chosen to easy hit any item w export function getEmptyHotbar(name: string, id: string = uuid.v4()): Hotbar { return { id, - items: Array(defaultHotbarCells).fill(null) as Tuple, + items: tuple.filled(defaultHotbarCells, null), name, }; } diff --git a/src/common/utils/__tests__/string.test.ts b/src/common/utils/__tests__/string.test.ts new file mode 100644 index 0000000000..d87730d57f --- /dev/null +++ b/src/common/utils/__tests__/string.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 { string } from "../../utils"; + +describe("string tests", () => { + describe("repeated()", () => { + const table = []; + let acc = ""; + + for (let i = 0; i <= 20; i += 1) { + table.push([acc, i] as const); + acc += "l"; + } + + it.each(table)("should return %p string if times = %i", (output, times) => { + expect(string.repeated("l", times)).toBe(output); + }); + }); +}); diff --git a/src/common/utils/__tests__/tuple.test.ts b/src/common/utils/__tests__/tuple.test.ts new file mode 100644 index 0000000000..5de0b64a87 --- /dev/null +++ b/src/common/utils/__tests__/tuple.test.ts @@ -0,0 +1,59 @@ +/** + * 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 { tuple } from "../../utils"; + +describe("tuple tests", () => { + describe("zip()", () => { + it("should yield 0 times and return 1 tuple of empty arrays when given empty array", () => { + expect(tuple.zip([]).next()).toEqual({ + done: true, + value: [[]], + }); + }); + + it("should yield 1 times and return 2 tuple of empty arrays when given one element array tuples", () => { + const i = tuple.zip([1], [2]); + + expect(i.next()).toEqual({ + done: false, + value: [1, 2] + }); + expect(i.next()).toEqual({ + done: true, + value: [[], []], + }); + }); + + it("should yield 1 times and return 2 tuple of partial arrays when given one element array tuples", () => { + const i = tuple.zip([1], [2, 3]); + + expect(i.next()).toEqual({ + done: false, + value: [1, 2] + }); + expect(i.next()).toEqual({ + done: true, + value: [[], [3]], + }); + }); + }); +}); diff --git a/src/common/utils/array.ts b/src/common/utils/array.ts index b0561e3eaf..54e122b9ba 100644 --- a/src/common/utils/array.ts +++ b/src/common/utils/array.ts @@ -19,26 +19,11 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export type Tuple = N extends N ? number extends N ? T[] : _TupleOf : never; -type _TupleOf = R["length"] extends N ? R : _TupleOf; - /** - * - * @param sources The source arrays - * @yields A tuple of the next element from each of the sources - * @returns The tuple of all the sources as soon as at least one of the sources is exausted + * A inference typed version of `Array(length).fill(value)` + * @param length The number of entries + * @param value The value of each of the indices */ -export function* zipStrict(...sources: Tuple): Iterator, Tuple> { - const maxSafeLength = sources.reduce((prev, cur) => Math.min(prev, cur.length), Number.POSITIVE_INFINITY); - - if (!isFinite(maxSafeLength)) { - // There are no sources, thus just return - return [] as Tuple; - } - - for (let i = 0; i < maxSafeLength; i += 1) { - yield sources.map(source => source[i]) as Tuple; - } - - return sources.map(source => source.slice(maxSafeLength)) as Tuple; +export function filled(length: number, value: T): T[] { + return Array(length).fill(value); } diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index fdf91d39c1..4ad0d32f72 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -61,5 +61,12 @@ export * from "./convertCpu"; import * as iter from "./iter"; import * as array from "./array"; +import * as tuple from "./tuple"; +import * as string from "./string"; -export { iter, array }; +export { + iter, + array, + string, + tuple, +}; diff --git a/src/common/utils/string.ts b/src/common/utils/string.ts new file mode 100644 index 0000000000..8365f7a11b --- /dev/null +++ b/src/common/utils/string.ts @@ -0,0 +1,45 @@ +/** + * 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. + */ + +/** + * Create a new string that is `times` repetitions of `part` + * @param part The string segment to repeat + * @param times A non-negative count of repetitions. + */ +export function repeated(part: string, times: number): string { + function _repeated(part: string, times: number): string { + switch (times) { + case 0: + return ""; + case 1: + return part; + case 2: + return part + part; + default: + const major = Math.pow(2, Math.floor(Math.log2(times))); + const majorString = _repeated(part, major / 2); + + return majorString + majorString + _repeated(part, times - major); + } + } + + return _repeated(part, Math.min(Number.MAX_SAFE_INTEGER, Math.max(0, Math.round(times)))); +} diff --git a/src/common/utils/tuple.ts b/src/common/utils/tuple.ts new file mode 100644 index 0000000000..5a252cf89c --- /dev/null +++ b/src/common/utils/tuple.ts @@ -0,0 +1,54 @@ +/** + * 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 { array } from "../utils"; + +/** + * A strict N-tuple of type T + */ +export type Tuple = N extends N ? number extends N ? T[] : _TupleOf : never; +type _TupleOf = R["length"] extends N ? R : _TupleOf; + +/** + * Iterates over `sources` yielding full tuples until one of the tuple arrays + * is empty. Then it returns a tuple with the rest of each of tuples + * @param sources The source arrays + * @yields A tuple of the next element from each of the sources + * @returns The tuple of all the sources as soon as at least one of the sources is exausted + */ +export function* zip(...sources: Tuple): Iterator, Tuple> { + const maxSafeLength = Math.min(...sources.map(source => source.length)); + + for (let i = 0; i < maxSafeLength; i += 1) { + yield sources.map(source => source[i]) as Tuple; + } + + return sources.map(source => source.slice(maxSafeLength)) as Tuple; +} + +/** + * Returns a `length` tuple filled with copies of `value` + * @param length The size of the tuple + * @param value The value for each of the tuple entries + */ +export function filled(length: L, value: T): Tuple { + return array.filled(length, value) as Tuple; +} diff --git a/src/main/routes/metrics-route.ts b/src/main/routes/metrics-route.ts index 2b8aa0f921..eef0c19800 100644 --- a/src/main/routes/metrics-route.ts +++ b/src/main/routes/metrics-route.ts @@ -19,7 +19,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import _ from "lodash"; import type { LensApiRequest } from "../router"; import { respondJson } from "../utils/http-responses"; import type { Cluster } from "../cluster"; @@ -33,8 +32,7 @@ export type IMetricsQuery = string | string[] | { }; // This is used for backoff retry tracking. -const MAX_ATTEMPTS = 5; -const ATTEMPTS = [...(_.fill(Array(MAX_ATTEMPTS - 1), false)), true]; +const ATTEMPTS = [false, false, false, false, true]; // prometheus metrics loader async function loadMetrics(promQueries: string[], cluster: Cluster, prometheusPath: string, queryParams: Record): Promise { diff --git a/src/renderer/components/+user-management/+service-accounts/secret.tsx b/src/renderer/components/+user-management/+service-accounts/secret.tsx index 3acd53f0dc..0c44908a2b 100644 --- a/src/renderer/components/+user-management/+service-accounts/secret.tsx +++ b/src/renderer/components/+user-management/+service-accounts/secret.tsx @@ -25,7 +25,7 @@ import moment from "moment"; import React from "react"; import type { Secret } from "../../../../common/k8s-api/endpoints/secret.api"; -import { prevDefault } from "../../../utils"; +import { prevDefault, string } from "../../../utils"; import { Icon } from "../../icon"; interface Props { @@ -49,7 +49,7 @@ export class ServiceAccountsSecret extends React.Component { <> {!showToken && ( <> - {Array(16).fill("•").join("")} + {string.repeated("•", 16)} { render() { const { isLoading } = this.props; const isInitLoading = isLoading && !this.logs.length; - const rowHeights = new Array(this.logs.length).fill(this.lineHeight); + const rowHeights = array.filled(this.logs.length, this.lineHeight); if (isInitLoading) { return ( diff --git a/src/renderer/components/table/sorting.ts b/src/renderer/components/table/sorting.ts index ca9ea959fc..9f907b0ae5 100644 --- a/src/renderer/components/table/sorting.ts +++ b/src/renderer/components/table/sorting.ts @@ -20,7 +20,7 @@ */ import type { TableSortCallback } from "./table"; -import { array, Ordering, rectifyOrdering, sortCompare } from "../../utils"; +import { Ordering, rectifyOrdering, sortCompare, tuple } from "../../utils"; export function getSorted(rawItems: T[], sortingCallback: TableSortCallback | undefined, orderByRaw: string): T[] { if (typeof sortingCallback !== "function") { @@ -40,7 +40,7 @@ export function getSorted(rawItems: T[], sortingCallback: TableSortCallback