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

replacing toJS({}) to utils/cloneJson where might be necessary

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-04-23 16:10:29 +03:00
parent f620c09095
commit 4fbefb648a
19 changed files with 110 additions and 110 deletions

View File

@ -49,9 +49,9 @@ export class ExamplePreferencesStore extends Store.ExtensionStore<ExamplePrefere
}
toJSON(): ExamplePreferencesModel {
return toJS({
enabled: this.enabled
});
return {
enabled: toJS(this.enabled),
};
}
}
```
@ -71,7 +71,6 @@ The `enabled` field of the `ExamplePreferencesStore` is set to the value from th
The `toJSON()` method is complementary to `fromStore()`.
It is called when the store is being saved.
`toJSON()` must provide a JSON serializable object, facilitating its storage in JSON format.
The `toJS()` function from [`mobx`](https://mobx.js.org/README.html) is convenient for this purpose, and is used here.
Finally, `ExamplePreferencesStore` is created by calling `ExamplePreferencesStore.getInstanceOrCreate()`, and exported for use by other parts of the extension.
Note that `ExamplePreferencesStore` is a singleton.

View File

@ -25,6 +25,7 @@ export class KubernetesCluster implements CatalogEntity {
constructor(data: CatalogEntityData) {
makeObservable(this);
this.metadata = data.metadata;
this.status = data.status as KubernetesClusterStatus;
this.spec = data.spec as KubernetesClusterSpec;

View File

@ -1,4 +1,4 @@
import { action, computed, observable, IObservableArray, makeObservable } from "mobx";
import { action, computed, IObservableArray, makeObservable, observable, toJS } from "mobx";
import { CatalogEntity } from "./catalog-entity";
export class CatalogEntityRegistry {
@ -17,7 +17,7 @@ export class CatalogEntityRegistry {
}
@computed get items(): CatalogEntity[] {
return Array.from(this.sources.values()).flat();
return Array.from(toJS(this.sources).values()).flat();
}
getItemsForApiKind<T extends CatalogEntity>(apiVersion: string, kind: string): T[] {

View File

@ -1,7 +1,7 @@
import path from "path";
import { app, ipcRenderer, remote, webFrame } from "electron";
import { unlink } from "fs-extra";
import { action, comparer, computed, observable, reaction, toJS, makeObservable } from "mobx";
import { action, comparer, computed, makeObservable, observable, reaction, toJS } from "mobx";
import { BaseStore } from "./base-store";
import { Cluster, ClusterState } from "../main/cluster";
import migrations from "../migrations/cluster-store";
@ -12,6 +12,7 @@ import { saveToAppFiles } from "./utils/saveToAppFiles";
import { KubeConfig } from "@kubernetes/client-node";
import { handleRequest, requestMain, subscribeToBroadcast, unsubscribeAllFromBroadcast } from "./ipc";
import { ResourceType } from "../renderer/components/cluster-settings/components/cluster-metrics-setting";
import { cloneJson } from "./utils";
export interface ClusterIconUpload {
clusterId: string;
@ -47,7 +48,7 @@ export interface ClusterModel {
* Workspace id
*
* @deprecated
*/
*/
workspace?: string;
/** User context in kubeconfig */
@ -330,7 +331,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
}
toJSON(): ClusterStoreModel {
return toJS({
return cloneJson({
activeCluster: this.activeCluster,
clusters: this.clustersList.map(cluster => cluster.toJSON()),
});

View File

@ -1,7 +1,8 @@
import { action, comparer, observable, toJS, makeObservable } from "mobx";
import { action, comparer, makeObservable, observable } from "mobx";
import { BaseStore } from "./base-store";
import migrations from "../migrations/hotbar-store";
import * as uuid from "uuid";
import { cloneJson } from "./utils";
export interface HotbarItem {
entity: {
@ -59,7 +60,8 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
return this.hotbars.findIndex((hotbar) => hotbar.id === this.activeHotbarId);
}
@action protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
@action
protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
if (data.hotbars?.length === 0) {
this.hotbars = [{
id: uuid.v4(),
@ -139,11 +141,9 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
}
toJSON(): HotbarStoreModel {
const model: HotbarStoreModel = {
return cloneJson({
hotbars: this.hotbars,
activeHotbarId: this.activeHotbarId
};
return toJS(model);
});
}
}

View File

@ -2,10 +2,10 @@
// https://www.electronjs.org/docs/api/ipc-main
// https://www.electronjs.org/docs/api/ipc-renderer
import { ipcMain, ipcRenderer, webContents, remote } from "electron";
import { ipcMain, ipcRenderer, remote, webContents } from "electron";
import { toJS } from "mobx";
import logger from "../../main/logger";
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
import { ClusterFrameInfo, clusterFrameMap } from "../cluster-frames";
const subFramesChannel = "ipc:get-sub-frames";
@ -18,7 +18,7 @@ export async function requestMain(channel: string, ...args: any[]) {
}
function getSubFrames(): ClusterFrameInfo[] {
return toJS(Array.from(clusterFrameMap.values()));
return Array.from(toJS(clusterFrameMap).values());
}
export async function broadcastMessage(channel: string, ...args: any[]) {

View File

@ -1,15 +1,19 @@
// Global configuration setup for external packages.
// Should be imported at the top of app's entry points.
import { configure } from "mobx";
import { enableMapSet, setAutoFreeze } from "immer";
import * as Mobx from "mobx";
import * as Immer from "immer";
// Mobx
// Docs: https://mobx.js.org/configuration.html
configure({
isolateGlobalState: true,
Mobx.configure({
enforceActions: "never",
isolateGlobalState: true,
// TODO: enable later (read more: https://mobx.js.org/migrating-from-4-or-5.html)
// computedRequiresReaction: true,
// reactionRequiresObservable: true,
// observableRequiresReaction: true,
});
// Immer
setAutoFreeze(false); // allow to merge mobx observables, docs: https://immerjs.github.io/immer/freezing
enableMapSet(); // allow to merge maps and sets, docs: https://immerjs.github.io/immer/map-set
// Docs: https://immerjs.github.io/immer/
Immer.setAutoFreeze(false); // allow to merge mobx observables
Immer.enableMapSet(); // allow to merge maps and sets

View File

@ -2,16 +2,15 @@ import type { ThemeId } from "../renderer/theme.store";
import { app, remote } from "electron";
import semver from "semver";
import { readFile } from "fs-extra";
import { action, computed, observable, reaction, toJS, makeObservable } from "mobx";
import { action, computed, makeObservable, observable, reaction } from "mobx";
import moment from "moment-timezone";
import { BaseStore } from "./base-store";
import migrations from "../migrations/user-store";
import { getAppVersion } from "./utils/app-version";
import migrations, { fileNameMigration } from "../migrations/user-store";
import { cloneJson, getAppVersion } from "./utils";
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
import { appEventBus } from "./event-bus";
import logger from "../main/logger";
import path from "path";
import { fileNameMigration } from "../migrations/user-store";
export interface UserStoreModel {
kubeConfigPath: string;
@ -181,7 +180,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
}
toJSON(): UserStoreModel {
return toJS({
return cloneJson({
kubeConfigPath: this.kubeConfigPath,
lastSeenAppVersion: this.lastSeenAppVersion,
seenContexts: Array.from(this.seenContexts),

View File

@ -1,5 +1,5 @@
// Clone json-serializable object
export function cloneJsonObject<T = object>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
export function cloneJson<T>(data: T): T {
return JSON.parse(JSON.stringify(data));
}

View File

@ -2,30 +2,29 @@ import { watch } from "chokidar";
import { ipcRenderer } from "electron";
import { EventEmitter } from "events";
import fs from "fs-extra";
import { observable, reaction, toJS, when, makeObservable } from "mobx";
import { makeObservable, observable, reaction, when } from "mobx";
import os from "os";
import path from "path";
import { broadcastMessage, handleRequest, requestMain, subscribeToBroadcast } from "../common/ipc";
import { Singleton } from "../common/utils";
import { cloneJson, Singleton } from "../common/utils";
import logger from "../main/logger";
import { extensionInstaller, PackageJson } from "./extension-installer";
import { ExtensionsStore } from "./extensions-store";
import type { LensExtensionId, LensExtensionManifest } from "./lens-extension";
export interface InstalledExtension {
id: LensExtensionId;
id: LensExtensionId;
readonly manifest: LensExtensionManifest;
readonly manifest: LensExtensionManifest;
// Absolute path to the non-symlinked source folder,
// e.g. "/Users/user/.k8slens/extensions/helloworld"
readonly absolutePath: string;
// Absolute path to the non-symlinked source folder,
// e.g. "/Users/user/.k8slens/extensions/helloworld"
readonly absolutePath: string;
// Absolute to the symlinked package.json file
readonly manifestPath: string;
readonly isBundled: boolean; // defined in project root's package.json
isEnabled: boolean;
}
// Absolute to the symlinked package.json file
readonly manifestPath: string;
readonly isBundled: boolean; // defined in project root's package.json
isEnabled: boolean;
}
const logModule = "[EXTENSION-DISCOVERY]";
@ -151,7 +150,7 @@ export class ExtensionDiscovery extends Singleton {
.on("unlinkDir", this.handleWatchUnlinkDir);
}
handleWatchFileAdd = async (manifestPath: string) => {
handleWatchFileAdd = async (manifestPath: string) => {
// e.g. "foo/package.json"
const relativePath = path.relative(this.localFolderPath, manifestPath);
@ -262,7 +261,6 @@ export class ExtensionDiscovery extends Singleton {
// fs.remove won't throw if path is missing
await fs.remove(path.join(extensionInstaller.extensionPackagesRoot, "package-lock.json"));
try {
// Verify write access to static/extensions, which is needed for symlinking
await fs.access(this.inTreeFolderPath, fs.constants.W_OK);
@ -447,7 +445,7 @@ export class ExtensionDiscovery extends Singleton {
}
toJSON(): ExtensionDiscoveryChannelMessage {
return toJS({
return cloneJson({
isLoaded: this.isLoaded
});
}

View File

@ -1,6 +1,7 @@
import type { LensExtensionId } from "./lens-extension";
import { BaseStore } from "../common/base-store";
import { action, computed, observable, toJS, makeObservable } from "mobx";
import { action, computed, makeObservable, observable } from "mobx";
import { cloneJson } from "../common/utils";
export interface LensExtensionsStoreModel {
extensions: Record<LensExtensionId, LensExtensionState>;
@ -22,8 +23,8 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
@computed
get enabledExtensions() {
return Array.from(this.state.values())
.filter(({enabled}) => enabled)
.map(({name}) => name);
.filter(({ enabled }) => enabled)
.map(({ name }) => name);
}
protected state = observable.map<LensExtensionId, LensExtensionState>();
@ -47,7 +48,7 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
}
toJSON(): LensExtensionsStoreModel {
return toJS({
return cloneJson({
extensions: Object.fromEntries(this.state),
});
}

View File

@ -1,13 +1,13 @@
import "../common/cluster-ipc";
import type http from "http";
import { ipcMain } from "electron";
import { action, autorun, observable, reaction, toJS, makeObservable } from "mobx";
import { action, autorun, makeObservable, observable, reaction, toJS } from "mobx";
import { ClusterStore, getClusterIdFromHost } from "../common/cluster-store";
import { Cluster } from "./cluster";
import logger from "./logger";
import { apiKubePrefix } from "../common/vars";
import { Singleton } from "../common/utils";
import { CatalogEntity } from "../common/catalog-entity";
import { cloneJson, Singleton } from "../common/utils";
import { CatalogEntity, CatalogEntityData } from "../common/catalog-entity";
import { KubernetesCluster } from "../common/catalog-entities/kubernetes-cluster";
import { catalogEntityRegistry } from "../common/catalog-entity-registry";
@ -41,7 +41,6 @@ export class ClusterManager extends Singleton {
this.syncClustersFromCatalog(entities);
});
// auto-stop removed clusters
autorun(() => {
const removedClusters = Array.from(ClusterStore.getInstance().removedClusters.values());
@ -57,11 +56,16 @@ export class ClusterManager extends Singleton {
delay: 250
});
ipcMain.on("network:offline", () => { this.onNetworkOffline(); });
ipcMain.on("network:online", () => { this.onNetworkOnline(); });
ipcMain.on("network:offline", () => {
this.onNetworkOffline();
});
ipcMain.on("network:online", () => {
this.onNetworkOnline();
});
}
@action protected updateCatalogSource(clusters: Cluster[]) {
@action
protected updateCatalogSource(clusters: Cluster[]) {
this.catalogSource.forEach((entity, index) => {
const clusterIndex = clusters.findIndex((cluster) => entity.metadata.uid === cluster.id);
@ -121,7 +125,7 @@ export class ClusterManager extends Singleton {
}
protected catalogEntityFromCluster(cluster: Cluster) {
return new KubernetesCluster(toJS({
const data: CatalogEntityData = cloneJson({
apiVersion: "entity.k8slens.dev/v1alpha1",
kind: "KubernetesCluster",
metadata: {
@ -142,7 +146,9 @@ export class ClusterManager extends Singleton {
message: "",
active: !cluster.disconnected
}
}));
});
return new KubernetesCluster(data);
}
protected onNetworkOffline() {

View File

@ -1,7 +1,7 @@
import { ipcMain } from "electron";
import type { ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences } from "../common/cluster-store";
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
import { action, comparer, computed, makeObservable, observable, reaction, toJS, when } from "mobx";
import { action, comparer, computed, makeObservable, observable, reaction, when } from "mobx";
import { apiKubePrefix } from "../common/vars";
import { broadcastMessage, ClusterListNamespaceForbiddenChannel, InvalidKubeconfigChannel } from "../common/ipc";
import { ContextHandler } from "./context-handler";
@ -15,6 +15,7 @@ import logger from "./logger";
import { VersionDetector } from "./cluster-detectors/version-detector";
import { detectorRegistry } from "./cluster-detectors/detector-registry";
import plimit from "p-limit";
import { cloneJson } from "../common/utils";
export enum ClusterStatus {
AccessGranted = 2,
@ -240,7 +241,7 @@ export class Cluster implements ClusterModel, ClusterState {
@computed get prometheusPreferences(): ClusterPrometheusPreferences {
const { prometheus, prometheusProvider } = this.preferences;
return toJS({ prometheus, prometheusProvider });
return cloneJson({ prometheus, prometheusProvider });
}
/**
@ -604,7 +605,7 @@ export class Cluster implements ClusterModel, ClusterState {
}
toJSON(): ClusterModel {
const model: ClusterModel = {
return cloneJson({
id: this.id,
contextName: this.contextName,
kubeConfigPath: this.kubeConfigPath,
@ -613,16 +614,14 @@ export class Cluster implements ClusterModel, ClusterState {
metadata: this.metadata,
ownerRef: this.ownerRef,
accessibleNamespaces: this.accessibleNamespaces,
};
return toJS(model);
});
}
/**
* Serializable cluster-state used for sync btw main <-> renderer
*/
getState(): ClusterState {
const state: ClusterState = {
return cloneJson({
initialized: this.initialized,
enabled: this.enabled,
apiUrl: this.apiUrl,
@ -635,9 +634,7 @@ export class Cluster implements ClusterModel, ClusterState {
allowedNamespaces: this.allowedNamespaces,
allowedResources: this.allowedResources,
isGlobalWatchEnabled: this.isGlobalWatchEnabled,
};
return toJS(state);
});
}
/**
@ -680,7 +677,7 @@ export class Cluster implements ClusterModel, ClusterState {
const api = (await this.getProxyKubeconfig()).makeApiClient(CoreV1Api);
try {
const { body: { items }} = await api.listNamespace();
const { body: { items } } = await api.listNamespace();
const namespaces = items.map(ns => ns.metadata.name);
this.getAllowedNamespacesErrorCount = 0; // reset on success

View File

@ -2,17 +2,18 @@ import { randomBytes } from "crypto";
import { SHA256 } from "crypto-js";
import { app, remote } from "electron";
import fse from "fs-extra";
import { action, observable, toJS, makeObservable } from "mobx";
import { action, makeObservable, observable } from "mobx";
import path from "path";
import { BaseStore } from "../common/base-store";
import { LensExtensionId } from "../extensions/lens-extension";
import { cloneJson } from "../common/utils";
interface FSProvisionModel {
extensions: Record<string, string>; // extension names to paths
}
export class FilesystemProvisionerStore extends BaseStore<FSProvisionModel> {
@observable registeredExtensions = observable.map<LensExtensionId, string>();
registeredExtensions = observable.map<LensExtensionId, string>();
constructor() {
super({
@ -50,8 +51,8 @@ export class FilesystemProvisionerStore extends BaseStore<FSProvisionModel> {
}
toJSON(): FSProvisionModel {
return toJS({
extensions: Object.fromEntries(this.registeredExtensions.toJSON()),
return cloneJson({
extensions: Object.fromEntries(this.registeredExtensions),
});
}
}

View File

@ -7,7 +7,7 @@ import path from "path";
import * as tempy from "tempy";
import logger from "./logger";
import { appEventBus } from "../common/event-bus";
import { cloneJsonObject } from "../common/utils";
import { cloneJson } from "../common/utils";
export class ResourceApplier {
constructor(protected cluster: Cluster) {
@ -83,7 +83,7 @@ export class ResourceApplier {
}
protected sanitizeObject(resource: KubernetesObject | any) {
resource = cloneJsonObject(resource);
resource = cloneJson(resource);
delete resource.status;
delete resource.metadata?.resourceVersion;
const annotations = resource.metadata?.annotations;

View File

@ -1,6 +1,6 @@
import { Select } from "../select";
import { computed, observable, toJS, makeObservable } from "mobx";
import { computed, observable, makeObservable } from "mobx";
import { observer } from "mobx-react";
import React from "react";
import { commandRegistry } from "../../../extensions/registries/command-registry";
@ -52,13 +52,11 @@ export class CommandDialog extends React.Component {
return;
}
const action = toJS(command.action);
try {
CommandOverlay.close();
if (command.scope === "global") {
action({
command.action({
entity: commandRegistry.activeEntity
});
} else if(commandRegistry.activeEntity) {

View File

@ -1,13 +1,12 @@
import debounce from "lodash/debounce";
import { reaction, toJS } from "mobx";
import { reaction } from "mobx";
import { Terminal as XTerm } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import { dockStore, TabId } from "./dock.store";
import { TerminalApi } from "../../api/terminal-api";
import { ThemeStore } from "../../theme.store";
import { autobind } from "../../utils";
import { autobind, cloneJson } from "../../utils";
import { isMac } from "../../../common/vars";
import { camelCase } from "lodash";
import { camelCase, debounce } from "lodash";
export class Terminal {
static spawningPool: HTMLElement;
@ -104,7 +103,7 @@ export class Terminal {
window.addEventListener("resize", this.onResize);
this.disposers.push(
reaction(() => toJS(ThemeStore.getInstance().activeTheme.colors), this.setTheme, {
reaction(() => cloneJson(ThemeStore.getInstance().activeTheme.colors), this.setTheme, {
fireImmediately: true
}),
dockStore.onResize(this.onResize),
@ -132,7 +131,7 @@ export class Terminal {
const { cols, rows } = this.xterm;
this.api.sendTerminalSize(cols, rows);
} catch(error) {
} catch (error) {
console.error(error);
return; // see https://github.com/lensapp/lens/issues/1891
@ -183,12 +182,12 @@ export class Terminal {
// Handle custom hotkey bindings
if (ctrlKey) {
switch (code) {
// Ctrl+C: prevent terminal exit on windows / linux (?)
// Ctrl+C: prevent terminal exit on windows / linux (?)
case "KeyC":
if (this.xterm.hasSelection()) return false;
break;
// Ctrl+W: prevent unexpected terminal tab closing, e.g. editing file in vim
// Ctrl+W: prevent unexpected terminal tab closing, e.g. editing file in vim
case "KeyW":
evt.preventDefault();
break;

View File

@ -175,7 +175,7 @@ describe("renderer/utils/StorageHelper", () => {
it("storage.get() is observable", () => {
expect(storageHelper.get()).toEqual(defaultValue);
reaction(() => toJS(storageHelper), change => {
reaction(() => toJS(storageHelper.toJS()), change => {
observedChanges.push(change);
});

View File

@ -1,6 +1,6 @@
// Helper for working with storages (e.g. window.localStorage, NodeJS/file-system, etc.)
import { action, comparer, CreateObservableOptions, IObservableValue, makeObservable, observable, reaction, toJS, when } from "mobx";
import { action, comparer, CreateObservableOptions, IObservableValue, IReactionDisposer, makeObservable, observable, reaction, toJS, when } from "mobx";
import produce, { Draft } from "immer";
import { isEqual, isFunction, isPlainObject, merge } from "lodash";
import logger from "../../main/logger";
@ -30,19 +30,24 @@ export class StorageHelper<T> {
};
private data: IObservableValue<T>;
@observable initialized = false;
whenReady = when(() => this.initialized);
protected unwatchChanges: IReactionDisposer;
public readonly storage: StorageAdapter<T>;
public readonly defaultValue: T;
@observable initialized = false;
whenReady = when(() => this.initialized);
constructor(readonly key: string, private options: StorageHelperOptions<T>) {
makeObservable(this);
this.options = merge({}, StorageHelper.defaultOptions, options);
this.storage = options.storage;
this.defaultValue = options.defaultValue;
this.observeData();
this.data = observable.box(this.defaultValue, this.options.observable);
this.unwatchChanges = reaction(() => toJS(this.data.get()), (newValue, oldValue) => {
this.onChange(newValue, oldValue);
}, this.options.observable);
if (this.options.autoInit) {
this.init();
@ -93,19 +98,6 @@ export class StorageHelper<T> {
return isEqual(value, this.defaultValue);
}
private observeData(value = this.options.defaultValue) {
const observableOptions: CreateObservableOptions = {
...StorageHelper.defaultOptions.observable, // inherit default observability options
...this.options.observable,
};
this.data = observable.box<T>(value, observableOptions);
return reaction(() => toJS(this.data.get()), (newValue, oldValue) => {
this.onChange(newValue, oldValue);
}, observableOptions);
}
protected onChange(value: T, oldValue?: T) {
if (!this.initialized) return;
@ -151,6 +143,10 @@ export class StorageHelper<T> {
this.set(nextValue as T);
}
destroy() {
this.unwatchChanges();
}
toJS() {
return toJS(this.get());
}