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

Remove ExtenedMap, ToggleSet, and observable versions (#3310)

- Switch to helper functions instead. They don't impact the prototype
  tree and it means that we don't need to introduce versions that are
  both observable and "normal"

- Tidy <ItemListLayout> and hiddenTableColumns to not need
  pre-initialization

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-02-16 18:20:22 -05:00 committed by GitHub
parent ed91ef2d03
commit 8088d3b5c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 58 additions and 105 deletions

View File

@ -4,15 +4,16 @@
*/
import { action, computed, observable, makeObservable } from "mobx";
import { Disposer, ExtendedMap, iter } from "../utils";
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
import type { Disposer } from "../utils";
import { strictSet, iter, getOrInsertMap } from "../utils";
import { once } from "lodash";
import { CatalogCategory, CatalogEntityData, CatalogEntityKindData } from "./catalog-entity";
export type CategoryFilter = (category: CatalogCategory) => any;
export class CatalogCategoryRegistry {
protected categories = observable.set<CatalogCategory>();
protected groupKinds = new ExtendedMap<string, ExtendedMap<string, CatalogCategory>>();
protected groupKinds = new Map<string, Map<string, CatalogCategory>>();
protected filters = observable.set<CategoryFilter>([], {
deep: false,
});
@ -22,14 +23,14 @@ export class CatalogCategoryRegistry {
}
@action add(category: CatalogCategory): Disposer {
const byGroup = getOrInsertMap(this.groupKinds, category.spec.group);
this.categories.add(category);
this.groupKinds
.getOrInsert(category.spec.group, ExtendedMap.new)
.strictSet(category.spec.names.kind, category);
strictSet(byGroup, category.spec.names.kind, category);
return () => {
this.categories.delete(category);
this.groupKinds.get(category.spec.group).delete(category.spec.names.kind);
byGroup.delete(category.spec.names.kind);
};
}

View File

@ -21,8 +21,8 @@ export function getOrInsert<K, V>(map: Map<K, V>, key: K, value: V): V {
}
/**
* Like `getOrInsert` but specifically for when `V` is `Map<any, any>` so that
* the typings are inferred.
* Like `getOrInsert` but specifically for when `V` is `Map<MK, MV>` so that
* the typings are inferred correctly.
*/
export function getOrInsertMap<K, MK, MV>(map: Map<K, Map<MK, MV>>, key: K): Map<MK, MV> {
return getOrInsert(map, key, new Map<MK, MV>());
@ -37,11 +37,39 @@ export function getOrInsertSet<K, SK>(map: Map<K, Set<SK>>, key: K): Set<SK> {
}
/**
* Like `getOrInsert` but with delayed creation of the item
* Like `getOrInsert` but with delayed creation of the item. Which is useful
* if it is very expensive to create the initial value.
*/
export function getOrInsertWith<K, V>(map: Map<K, V>, key: K, value: () => V): V {
export function getOrInsertWith<K, V>(map: Map<K, V>, key: K, builder: () => V): V {
if (!map.has(key)) {
map.set(key, value());
map.set(key, builder());
}
return map.get(key);
}
/**
* Set the value associated with `key` iff there was not a previous value
* @param map The map to interact with
* @throws if `key` already in map
* @returns `this` so that `strictSet` can be chained
*/
export function strictSet<K, V>(map: Map<K, V>, key: K, val: V): typeof map {
if (map.has(key)) {
throw new TypeError("Duplicate key in map");
}
return map.set(key, val);
}
/**
* Get the value associated with `key`
* @param map The map to interact with
* @throws if `key` did not a value associated with it
*/
export function strictGet<K, V>(map: Map<K, V>, key: K): V {
if (!map.has(key)) {
throw new TypeError("key not in map");
}
return map.get(key);

View File

@ -1,70 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { action, ObservableMap, runInAction } from "mobx";
export function multiSet<T, V>(map: Map<T, V>, newEntries: [T, V][]): void {
runInAction(() => {
for (const [key, val] of newEntries) {
map.set(key, val);
}
});
}
export class ExtendedMap<K, V> extends Map<K, V> {
static new<K, V>(entries?: readonly (readonly [K, V])[] | null): ExtendedMap<K, V> {
return new ExtendedMap<K, V>(entries);
}
/**
* 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
*/
getOrInsert(key: K, getVal: () => V): V {
if (this.has(key)) {
return this.get(key);
}
return this.set(key, getVal()).get(key);
}
/**
* Set the value associated with `key` iff there was not a previous value
* @throws if `key` already in map
* @returns `this` so that `strictSet` can be chained
*/
strictSet(key: K, val: V): this {
if (this.has(key)) {
throw new TypeError("Duplicate key in map");
}
return this.set(key, val);
}
/**
* Get the value associated with `key`
* @throws if `key` did not a value associated with it
*/
strictGet(key: K): V {
if (!this.has(key)) {
throw new TypeError("key not in map");
}
return this.get(key);
}
}
export class ExtendedObservableMap<K, V> extends ObservableMap<K, V> {
@action
getOrInsert(key: K, getVal: () => V): V {
if (this.has(key)) {
return this.get(key);
}
return this.set(key, getVal()).get(key);
}
}

View File

@ -25,7 +25,6 @@ export * from "./delay";
export * from "./disposer";
export * from "./downloadFile";
export * from "./escapeRegExp";
export * from "./extended-map";
export * from "./formatDuration";
export * from "./getRandId";
export * from "./hash-set";

View File

@ -10,7 +10,7 @@ import { FSWatcher, watch } from "chokidar";
import fs from "fs";
import path from "path";
import type stream from "stream";
import { bytesToUnits, Disposer, ExtendedObservableMap, iter, noop } from "../../../common/utils";
import { bytesToUnits, Disposer, getOrInsertWith, iter, noop } from "../../../common/utils";
import logger from "../../logger";
import type { KubeConfig } from "@kubernetes/client-node";
import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers";
@ -294,7 +294,7 @@ const diffChangedConfigFor = (dependencies: Dependencies) => ({ filePath, source
};
const watchFileChanges = (filePath: string, dependencies: Dependencies): [IComputedValue<CatalogEntity[]>, Disposer] => {
const rootSource = new ExtendedObservableMap<string, ObservableMap<string, RootSourceValue>>();
const rootSource = observable.map<string, ObservableMap<string, RootSourceValue>>();
const derivedSource = computed(() => Array.from(iter.flatMap(rootSource.values(), from => iter.map(from.values(), child => child[1]))));
let watcher: FSWatcher;
@ -335,7 +335,7 @@ const watchFileChanges = (filePath: string, dependencies: Dependencies): [ICompu
cleanup();
cleanupFns.set(childFilePath, diffChangedConfig({
filePath: childFilePath,
source: rootSource.getOrInsert(childFilePath, observable.map),
source: getOrInsertWith(rootSource, childFilePath, observable.map),
stats,
maxAllowedFileReadSize,
}));
@ -353,7 +353,7 @@ const watchFileChanges = (filePath: string, dependencies: Dependencies): [ICompu
cleanupFns.set(childFilePath, diffChangedConfig({
filePath: childFilePath,
source: rootSource.getOrInsert(childFilePath, observable.map),
source: getOrInsertWith(rootSource, childFilePath, observable.map),
stats,
maxAllowedFileReadSize,
}));

View File

@ -2,7 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { ExtendedMap } from "../../../../common/utils";
import { getOrInsertMap } from "../../../../common/utils";
import type { ClusterId } from "../../../../common/cluster-types";
import { ipcMainHandle } from "../../../../common/ipc";
import crypto from "crypto";
@ -11,15 +11,14 @@ import { promisify } from "util";
const randomBytes = promisify(crypto.randomBytes);
export class ShellRequestAuthenticator {
private tokens = new ExtendedMap<ClusterId, Map<string, Uint8Array>>();
private tokens = new Map<ClusterId, Map<string, Uint8Array>>();
init() {
ipcMainHandle("cluster:shell-api", async (event, clusterId, tabId) => {
const authToken = Uint8Array.from(await randomBytes(128));
const forCluster = getOrInsertMap(this.tokens, clusterId);
this.tokens
.getOrInsert(clusterId, () => new Map())
.set(tabId, authToken);
forCluster.set(tabId, authToken);
return authToken;
});

View File

@ -5,7 +5,7 @@
import type Conf from "conf";
import type { Migrations } from "conf/dist/source/types";
import { ExtendedMap, iter } from "../common/utils";
import { getOrInsert, iter } from "../common/utils";
import { isTestEnv } from "../common/vars";
export function migrationLog(...args: any[]) {
@ -20,10 +20,10 @@ export interface MigrationDeclaration {
}
export function joinMigrations(...declarations: MigrationDeclaration[]): Migrations<any> {
const migrations = new ExtendedMap<string, ((store: Conf<any>) => void)[]>();
const migrations = new Map<string, ((store: Conf<any>) => void)[]>();
for (const decl of declarations) {
migrations.getOrInsert(decl.version, () => []).push(decl.run);
getOrInsert(migrations, decl.version, []).push(decl.run);
}
return Object.fromEntries(

View File

@ -27,7 +27,7 @@ interface Props extends KubeObjectDetailsProps<Secret> {
export class SecretDetails extends React.Component<Props> {
@observable isSaving = false;
@observable data: { [name: string]: string } = {};
revealSecret = new Set<string>();
@observable revealSecret = observable.set<string>();
constructor(props: Props) {
super(props);

View File

@ -11,7 +11,7 @@ import { Notice } from "../+extensions/notice";
import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
import { isWindows } from "../../../common/vars";
import logger from "../../../main/logger";
import { iter, multiSet } from "../../utils";
import { iter } from "../../utils";
import { SubTitle } from "../layout/sub-title";
import { PathPicker } from "../path-picker/path-picker";
import { Spinner } from "../spinner";
@ -93,7 +93,9 @@ export class KubeconfigSyncs extends React.Component {
return Array.from(this.syncs.entries(), ([filePath, value]) => ({ filePath, ...value }));
}
onPick = async (filePaths: string[]) => multiSet(this.syncs, await getAllEntries(filePaths));
onPick = async (filePaths: string[]) => {
this.syncs.merge(await getAllEntries(filePaths));
};
getIconName(entry: Entry) {
switch (entry.info.type) {

View File

@ -6,21 +6,15 @@
import React from "react";
import { kubernetesClusterCategory } from "../../common/catalog-entities";
import { addClusterURL, kubernetesURL } from "../../common/routes";
import { multiSet } from "../utils";
import { UserStore } from "../../common/user-store";
import { getAllEntries } from "../components/+preferences/kubeconfig-syncs";
import { runInAction } from "mobx";
import { isLinux, isWindows } from "../../common/vars";
import { PathPicker } from "../components/path-picker";
import { Notifications } from "../components/notifications";
import { Link } from "react-router-dom";
async function addSyncEntries(filePaths: string[]) {
const entries = await getAllEntries(filePaths);
runInAction(() => {
multiSet(UserStore.getInstance().syncKubeconfigEntries, entries);
});
UserStore.getInstance().syncKubeconfigEntries.merge(await getAllEntries(filePaths));
Notifications.ok(
<div>