mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fixing Singleton typing to correctly return child class (#1914)
- Add distinction between `getInstance` and `getInstanceOrCreate` since it is not always possible to create an instance (since you might not know the correct arguments) - Remove all the `export const *Store = *Store.getInstance<*Store>();` calls as it defeats the purpose of `Singleton`. Plus with the typing changes the appropriate `*Store.getInstance()` is "short enough". - Special case the two extension export facades to not need to use `getInstanceOrCreate`. Plus since they are just facades it is always possible to create them. - Move some other types to be also `Singleton`'s: ExtensionLoader, ExtensionDiscovery, ThemeStore, LocalizationStore, ... - Fixed dev-run always using the same port with electron inspect - Update Store documentation with new recommendations about creating instances of singletons - Fix all unit tests to create their dependent singletons Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
b2a570ce28
commit
9563ead2e6
114 files changed
+924
-736
No files matched your search
@@ -4,8 +4,9 @@ import yaml from "js-yaml";
|
||||
import { Cluster } from "../../main/cluster";
|
||||
import { ClusterStore, getClusterIdFromHost } from "../cluster-store";
|
||||
import { Console } from "console";
|
||||
import { stdout, stderr } from "process";
|
||||
|
||||
console = new Console(process.stdout, process.stderr); // fix mockFS
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
const testDataIcon = fs.readFileSync("test-data/cluster-store-migration-icon.png");
|
||||
const kubeconfig = `
|
||||
@@ -47,10 +48,8 @@ jest.mock("electron", () => {
|
||||
};
|
||||
});
|
||||
|
||||
let clusterStore: ClusterStore;
|
||||
|
||||
describe("empty config", () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
ClusterStore.resetInstance();
|
||||
const mockOpts = {
|
||||
"tmp": {
|
||||
@@ -59,9 +58,8 @@ describe("empty config", () => {
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
await ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -70,7 +68,7 @@ describe("empty config", () => {
|
||||
|
||||
describe("with foo cluster added", () => {
|
||||
beforeEach(() => {
|
||||
clusterStore.addCluster(
|
||||
ClusterStore.getInstance().addCluster(
|
||||
new Cluster({
|
||||
id: "foo",
|
||||
contextName: "foo",
|
||||
@@ -85,7 +83,7 @@ describe("empty config", () => {
|
||||
});
|
||||
|
||||
it("adds new cluster to store", async () => {
|
||||
const storedCluster = clusterStore.getById("foo");
|
||||
const storedCluster = ClusterStore.getInstance().getById("foo");
|
||||
|
||||
expect(storedCluster.id).toBe("foo");
|
||||
expect(storedCluster.preferences.terminalCWD).toBe("/tmp");
|
||||
@@ -94,19 +92,19 @@ describe("empty config", () => {
|
||||
});
|
||||
|
||||
it("removes cluster from store", async () => {
|
||||
await clusterStore.removeById("foo");
|
||||
expect(clusterStore.getById("foo")).toBeNull();
|
||||
await ClusterStore.getInstance().removeById("foo");
|
||||
expect(ClusterStore.getInstance().getById("foo")).toBeNull();
|
||||
});
|
||||
|
||||
it("sets active cluster", () => {
|
||||
clusterStore.setActive("foo");
|
||||
expect(clusterStore.active.id).toBe("foo");
|
||||
ClusterStore.getInstance().setActive("foo");
|
||||
expect(ClusterStore.getInstance().active.id).toBe("foo");
|
||||
});
|
||||
});
|
||||
|
||||
describe("with prod and dev clusters added", () => {
|
||||
beforeEach(() => {
|
||||
clusterStore.addClusters(
|
||||
ClusterStore.getInstance().addClusters(
|
||||
new Cluster({
|
||||
id: "prod",
|
||||
contextName: "foo",
|
||||
@@ -127,8 +125,8 @@ describe("empty config", () => {
|
||||
});
|
||||
|
||||
it("check if store can contain multiple clusters", () => {
|
||||
expect(clusterStore.hasClusters()).toBeTruthy();
|
||||
expect(clusterStore.clusters.size).toBe(2);
|
||||
expect(ClusterStore.getInstance().hasClusters()).toBeTruthy();
|
||||
expect(ClusterStore.getInstance().clusters.size).toBe(2);
|
||||
});
|
||||
|
||||
it("check if cluster's kubeconfig file saved", () => {
|
||||
@@ -178,9 +176,8 @@ describe("config with existing clusters", () => {
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -188,24 +185,24 @@ describe("config with existing clusters", () => {
|
||||
});
|
||||
|
||||
it("allows to retrieve a cluster", () => {
|
||||
const storedCluster = clusterStore.getById("cluster1");
|
||||
const storedCluster = ClusterStore.getInstance().getById("cluster1");
|
||||
|
||||
expect(storedCluster.id).toBe("cluster1");
|
||||
expect(storedCluster.preferences.terminalCWD).toBe("/foo");
|
||||
});
|
||||
|
||||
it("allows to delete a cluster", () => {
|
||||
clusterStore.removeById("cluster2");
|
||||
const storedCluster = clusterStore.getById("cluster1");
|
||||
ClusterStore.getInstance().removeById("cluster2");
|
||||
const storedCluster = ClusterStore.getInstance().getById("cluster1");
|
||||
|
||||
expect(storedCluster).toBeTruthy();
|
||||
const storedCluster2 = clusterStore.getById("cluster2");
|
||||
const storedCluster2 = ClusterStore.getInstance().getById("cluster2");
|
||||
|
||||
expect(storedCluster2).toBeNull();
|
||||
});
|
||||
|
||||
it("allows getting all of the clusters", async () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
const storedClusters = ClusterStore.getInstance().clustersList;
|
||||
|
||||
expect(storedClusters.length).toBe(3);
|
||||
expect(storedClusters[0].id).toBe("cluster1");
|
||||
@@ -216,7 +213,7 @@ describe("config with existing clusters", () => {
|
||||
});
|
||||
|
||||
it("marks owned cluster disabled by default", () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
const storedClusters = ClusterStore.getInstance().clustersList;
|
||||
|
||||
expect(storedClusters[0].enabled).toBe(true);
|
||||
expect(storedClusters[2].enabled).toBe(false);
|
||||
@@ -276,9 +273,8 @@ users:
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -286,7 +282,7 @@ users:
|
||||
});
|
||||
|
||||
it("does not enable clusters with invalid kubeconfig", () => {
|
||||
const storedClusters = clusterStore.clustersList;
|
||||
const storedClusters = ClusterStore.getInstance().clustersList;
|
||||
|
||||
expect(storedClusters.length).toBe(2);
|
||||
expect(storedClusters[0].enabled).toBeFalsy;
|
||||
@@ -319,9 +315,8 @@ describe("pre 2.0 config with an existing cluster", () => {
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -329,7 +324,7 @@ describe("pre 2.0 config with an existing cluster", () => {
|
||||
});
|
||||
|
||||
it("migrates to modern format with kubeconfig in a file", async () => {
|
||||
const config = clusterStore.clustersList[0].kubeConfigPath;
|
||||
const config = ClusterStore.getInstance().clustersList[0].kubeConfigPath;
|
||||
|
||||
expect(fs.readFileSync(config, "utf8")).toContain(`"contexts":[]`);
|
||||
});
|
||||
@@ -347,16 +342,51 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
|
||||
}
|
||||
},
|
||||
cluster1: {
|
||||
kubeConfig: "apiVersion: v1\nclusters:\n- cluster:\n server: https://10.211.55.6:8443\n name: minikube\ncontexts:\n- context:\n cluster: minikube\n user: minikube\n name: minikube\ncurrent-context: minikube\nkind: Config\npreferences: {}\nusers:\n- name: minikube\n user:\n client-certificate: /Users/kimmo/.minikube/client.crt\n client-key: /Users/kimmo/.minikube/client.key\n auth-provider:\n config:\n access-token:\n - should be string\n expiry:\n - should be string\n"
|
||||
kubeConfig: JSON.stringify({
|
||||
apiVersion: "v1",
|
||||
clusters: [{
|
||||
cluster: {
|
||||
server: "https://10.211.55.6:8443",
|
||||
},
|
||||
name: "minikube",
|
||||
}],
|
||||
contexts: [{
|
||||
context: {
|
||||
cluster: "minikube",
|
||||
user: "minikube",
|
||||
name: "minikube",
|
||||
},
|
||||
name: "minikube",
|
||||
}],
|
||||
"current-context": "minikube",
|
||||
kind: "Config",
|
||||
preferences: {},
|
||||
users: [{
|
||||
name: "minikube",
|
||||
user: {
|
||||
"client-certificate": "/Users/foo/.minikube/client.crt",
|
||||
"client-key": "/Users/foo/.minikube/client.key",
|
||||
"auth-provider": {
|
||||
config: {
|
||||
"access-token": [
|
||||
"should be string"
|
||||
],
|
||||
expiry: [
|
||||
"should be string"
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
}]
|
||||
}),
|
||||
},
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -364,10 +394,12 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
|
||||
});
|
||||
|
||||
it("replaces array format access token and expiry into string", async () => {
|
||||
const file = clusterStore.clustersList[0].kubeConfigPath;
|
||||
const file = ClusterStore.getInstance().clustersList[0].kubeConfigPath;
|
||||
const config = fs.readFileSync(file, "utf8");
|
||||
const kc = yaml.safeLoad(config);
|
||||
|
||||
console.log(kc);
|
||||
|
||||
expect(kc.users[0].user["auth-provider"].config["access-token"]).toBe("should be string");
|
||||
expect(kc.users[0].user["auth-provider"].config["expiry"]).toBe("should be string");
|
||||
});
|
||||
@@ -397,9 +429,8 @@ describe("pre 2.6.0 config with a cluster icon", () => {
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -407,7 +438,7 @@ describe("pre 2.6.0 config with a cluster icon", () => {
|
||||
});
|
||||
|
||||
it("moves the icon into preferences", async () => {
|
||||
const storedClusterData = clusterStore.clustersList[0];
|
||||
const storedClusterData = ClusterStore.getInstance().clustersList[0];
|
||||
|
||||
expect(storedClusterData.hasOwnProperty("icon")).toBe(false);
|
||||
expect(storedClusterData.preferences.hasOwnProperty("icon")).toBe(true);
|
||||
@@ -437,9 +468,8 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -474,9 +504,8 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
|
||||
};
|
||||
|
||||
mockFs(mockOpts);
|
||||
clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
return clusterStore.load();
|
||||
return ClusterStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -484,13 +513,13 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
|
||||
});
|
||||
|
||||
it("migrates to modern format with kubeconfig in a file", async () => {
|
||||
const config = clusterStore.clustersList[0].kubeConfigPath;
|
||||
const config = ClusterStore.getInstance().clustersList[0].kubeConfigPath;
|
||||
|
||||
expect(fs.readFileSync(config, "utf8")).toBe(minimalValidKubeConfig);
|
||||
});
|
||||
|
||||
it("migrates to modern format with icon not in file", async () => {
|
||||
const { icon } = clusterStore.clustersList[0].preferences;
|
||||
const { icon } = ClusterStore.getInstance().clustersList[0].preferences;
|
||||
|
||||
expect(icon.startsWith("data:;base64,")).toBe(true);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { appEventBus, AppEvent } from "../event-bus";
|
||||
import { Console } from "console";
|
||||
import { stdout, stderr } from "process";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
describe("event bus tests", () => {
|
||||
describe("emit", () => {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import mockFs from "mock-fs";
|
||||
import { HotbarStore, hotbarStore } from "../hotbar-store";
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import { HotbarStore } from "../hotbar-store";
|
||||
|
||||
describe("HotbarStore", () => {
|
||||
beforeEach(() => {
|
||||
ClusterStore.resetInstance();
|
||||
ClusterStore.getInstanceOrCreate();
|
||||
|
||||
HotbarStore.resetInstance();
|
||||
mockFs({ tmp: { "lens-hotbar-store.json": "{}" } });
|
||||
});
|
||||
@@ -13,8 +17,8 @@ describe("HotbarStore", () => {
|
||||
|
||||
describe("load", () => {
|
||||
it("loads one hotbar by default", () => {
|
||||
hotbarStore.load();
|
||||
expect(hotbarStore.hotbars.length).toEqual(1);
|
||||
HotbarStore.getInstanceOrCreate().load();
|
||||
expect(HotbarStore.getInstance().hotbars.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,8 @@
|
||||
import { SearchStore } from "../search-store";
|
||||
import { Console } from "console";
|
||||
import { stdout, stderr } from "process";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
let searchStore: SearchStore = null;
|
||||
const logs = [
|
||||
|
||||
@@ -10,7 +10,7 @@ jest.mock("electron", () => {
|
||||
getVersion: () => "99.99.99",
|
||||
getPath: () => "tmp",
|
||||
getLocale: () => "en",
|
||||
setLoginItemSettings: jest.fn(),
|
||||
setLoginItemSettings: (): void => void 0,
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -18,12 +18,19 @@ jest.mock("electron", () => {
|
||||
import { UserStore } from "../user-store";
|
||||
import { SemVer } from "semver";
|
||||
import electron from "electron";
|
||||
import { stdout, stderr } from "process";
|
||||
|
||||
console = new Console(stdout, stderr);
|
||||
|
||||
describe("user store tests", () => {
|
||||
describe("for an empty config", () => {
|
||||
beforeEach(() => {
|
||||
UserStore.resetInstance();
|
||||
mockFs({ tmp: { "config.json": "{}" } });
|
||||
mockFs({ tmp: { "config.json": "{}", "kube_config": "{}" } });
|
||||
|
||||
(UserStore.getInstanceOrCreate() as any).refreshNewContexts = jest.fn(() => Promise.resolve());
|
||||
|
||||
return UserStore.getInstance().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -31,14 +38,14 @@ describe("user store tests", () => {
|
||||
});
|
||||
|
||||
it("allows setting and retrieving lastSeenAppVersion", () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
const us = UserStore.getInstance();
|
||||
|
||||
us.lastSeenAppVersion = "1.2.3";
|
||||
expect(us.lastSeenAppVersion).toBe("1.2.3");
|
||||
});
|
||||
|
||||
it("allows adding and listing seen contexts", () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
const us = UserStore.getInstance();
|
||||
|
||||
us.seenContexts.add("foo");
|
||||
expect(us.seenContexts.size).toBe(1);
|
||||
@@ -51,7 +58,7 @@ describe("user store tests", () => {
|
||||
});
|
||||
|
||||
it("allows setting and getting preferences", () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
const us = UserStore.getInstance();
|
||||
|
||||
us.preferences.httpsProxy = "abcd://defg";
|
||||
|
||||
@@ -63,7 +70,7 @@ describe("user store tests", () => {
|
||||
});
|
||||
|
||||
it("correctly resets theme to default value", async () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
const us = UserStore.getInstance();
|
||||
|
||||
us.isLoaded = true;
|
||||
|
||||
@@ -73,7 +80,7 @@ describe("user store tests", () => {
|
||||
});
|
||||
|
||||
it("correctly calculates if the last seen version is an old release", () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
const us = UserStore.getInstance();
|
||||
|
||||
expect(us.isNewVersion).toBe(true);
|
||||
|
||||
@@ -94,6 +101,8 @@ describe("user store tests", () => {
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
return UserStore.getInstanceOrCreate().load();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -101,7 +110,7 @@ describe("user store tests", () => {
|
||||
});
|
||||
|
||||
it("sets last seen app version to 0.0.0", () => {
|
||||
const us = UserStore.getInstance<UserStore>();
|
||||
const us = UserStore.getInstance();
|
||||
|
||||
expect(us.lastSeenAppVersion).toBe("0.0.0");
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { observable } from "mobx";
|
||||
import { catalogCategoryRegistry } from "../catalog-category-registry";
|
||||
import { CatalogCategory, CatalogEntity, CatalogEntityActionContext, CatalogEntityAddMenuContext, CatalogEntityContextMenuContext, CatalogEntityData, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog-entity";
|
||||
import { clusterDisconnectHandler } from "../cluster-ipc";
|
||||
import { clusterStore } from "../cluster-store";
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import { requestMain } from "../ipc";
|
||||
|
||||
export type KubernetesClusterSpec = {
|
||||
@@ -56,7 +56,7 @@ export class KubernetesCluster implements CatalogEntity {
|
||||
icon: "delete",
|
||||
title: "Delete",
|
||||
onlyVisibleForSource: "local",
|
||||
onClick: async () => clusterStore.removeById(this.metadata.uid),
|
||||
onClick: async () => ClusterStore.getInstance().removeById(this.metadata.uid),
|
||||
confirm: {
|
||||
message: `Remove Kubernetes Cluster "${this.metadata.name} from Lens?`
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export class KubernetesCluster implements CatalogEntity {
|
||||
icon: "link_off",
|
||||
title: "Disconnect",
|
||||
onClick: async () => {
|
||||
clusterStore.deactivate(this.metadata.uid);
|
||||
ClusterStore.getInstance().deactivate(this.metadata.uid);
|
||||
requestMain(clusterDisconnectHandler, this.metadata.uid);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { handleRequest } from "./ipc";
|
||||
import { ClusterId, clusterStore } from "./cluster-store";
|
||||
import { ClusterId, ClusterStore } from "./cluster-store";
|
||||
import { appEventBus } from "./event-bus";
|
||||
import { ResourceApplier } from "../main/resource-applier";
|
||||
import { ipcMain, IpcMainInvokeEvent } from "electron";
|
||||
@@ -11,10 +11,9 @@ export const clusterRefreshHandler = "cluster:refresh";
|
||||
export const clusterDisconnectHandler = "cluster:disconnect";
|
||||
export const clusterKubectlApplyAllHandler = "cluster:kubectl-apply-all";
|
||||
|
||||
|
||||
if (ipcMain) {
|
||||
handleRequest(clusterActivateHandler, (event, clusterId: ClusterId, force = false) => {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
return cluster.activate(force);
|
||||
@@ -22,7 +21,7 @@ if (ipcMain) {
|
||||
});
|
||||
|
||||
handleRequest(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
clusterFrameMap.set(cluster.id, { frameId: event.frameId, processId: event.processId });
|
||||
@@ -32,14 +31,14 @@ if (ipcMain) {
|
||||
});
|
||||
|
||||
handleRequest(clusterRefreshHandler, (event, clusterId: ClusterId) => {
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) return cluster.refresh({ refreshMetadata: true });
|
||||
});
|
||||
|
||||
handleRequest(clusterDisconnectHandler, (event, clusterId: ClusterId) => {
|
||||
appEventBus.emit({name: "cluster", action: "stop"});
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
cluster.disconnect();
|
||||
@@ -49,7 +48,7 @@ if (ipcMain) {
|
||||
|
||||
handleRequest(clusterKubectlApplyAllHandler, (event, clusterId: ClusterId, resources: string[]) => {
|
||||
appEventBus.emit({name: "cluster", action: "kubectl-apply-all"});
|
||||
const cluster = clusterStore.getById(clusterId);
|
||||
const cluster = ClusterStore.getInstance().getById(clusterId);
|
||||
|
||||
if (cluster) {
|
||||
const applier = new ResourceApplier(cluster);
|
||||
|
||||
@@ -112,7 +112,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
|
||||
private static stateRequestChannel = "cluster:states";
|
||||
|
||||
private constructor() {
|
||||
constructor() {
|
||||
super({
|
||||
configName: "lens-cluster-store",
|
||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||
@@ -337,8 +337,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||
}
|
||||
}
|
||||
|
||||
export const clusterStore = ClusterStore.getInstance<ClusterStore>();
|
||||
|
||||
export function getClusterIdFromHost(host: string): ClusterId | undefined {
|
||||
// e.g host == "%clusterId.localhost:45345"
|
||||
const subDomains = host.split(":")[0].split(".");
|
||||
@@ -355,5 +353,5 @@ export function getHostedClusterId() {
|
||||
}
|
||||
|
||||
export function getHostedCluster(): Cluster {
|
||||
return clusterStore.getById(getHostedClusterId());
|
||||
return ClusterStore.getInstance().getById(getHostedClusterId());
|
||||
}
|
||||
@@ -23,7 +23,7 @@ export interface HotbarStoreModel {
|
||||
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
@observable hotbars: Hotbar[] = [];
|
||||
|
||||
private constructor() {
|
||||
constructor() {
|
||||
super({
|
||||
configName: "lens-hotbar-store",
|
||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||
@@ -67,5 +67,3 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const hotbarStore = HotbarStore.getInstance<HotbarStore>();
|
||||
@@ -5,8 +5,8 @@ import { pathToRegexp } from "path-to-regexp";
|
||||
import logger from "../../main/logger";
|
||||
import Url from "url-parse";
|
||||
import { RoutingError, RoutingErrorType } from "./error";
|
||||
import { extensionsStore } from "../../extensions/extensions-store";
|
||||
import { extensionLoader } from "../../extensions/extension-loader";
|
||||
import { ExtensionsStore } from "../../extensions/extensions-store";
|
||||
import { ExtensionLoader } from "../../extensions/extension-loader";
|
||||
import { LensExtension } from "../../extensions/lens-extension";
|
||||
import { RouteHandler, RouteParams } from "../../extensions/registries/protocol-handler-registry";
|
||||
|
||||
@@ -72,7 +72,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
|
||||
/**
|
||||
* find the most specific matching handler and call it
|
||||
* @param routes the array of (path schemas, handler) paris to match against
|
||||
* @param routes the array of (path schemas, handler) pairs to match against
|
||||
* @param url the url (in its current state)
|
||||
*/
|
||||
protected _route(routes: [string, RouteHandler][], url: Url, extensionName?: string): void {
|
||||
@@ -124,7 +124,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
const { [EXTENSION_PUBLISHER_MATCH]: publisher, [EXTENSION_NAME_MATCH]: partialName } = match.params;
|
||||
const name = [publisher, partialName].filter(Boolean).join("/");
|
||||
|
||||
const extension = extensionLoader.userExtensionsByName.get(name);
|
||||
const extension = ExtensionLoader.getInstance().userExtensionsByName.get(name);
|
||||
|
||||
if (!extension) {
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not installed`);
|
||||
@@ -132,7 +132,7 @@ export abstract class LensProtocolRouter extends Singleton {
|
||||
return name;
|
||||
}
|
||||
|
||||
if (!extensionsStore.isEnabled(extension.id)) {
|
||||
if (!ExtensionsStore.getInstance().isEnabled(extension.id)) {
|
||||
logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`);
|
||||
|
||||
return name;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import request from "request";
|
||||
import requestPromise from "request-promise-native";
|
||||
import { userStore } from "./user-store";
|
||||
import { UserStore } from "./user-store";
|
||||
|
||||
// todo: get rid of "request" (deprecated)
|
||||
// https://github.com/lensapp/lens/issues/459
|
||||
|
||||
function getDefaultRequestOpts(): Partial<request.Options> {
|
||||
const { httpsProxy, allowUntrustedCAs } = userStore.preferences;
|
||||
const { httpsProxy, allowUntrustedCAs } = UserStore.getInstance().preferences;
|
||||
|
||||
return {
|
||||
proxy: httpsProxy || undefined,
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface UserPreferences {
|
||||
export class UserStore extends BaseStore<UserStoreModel> {
|
||||
static readonly defaultTheme: ThemeId = "lens-dark";
|
||||
|
||||
private constructor() {
|
||||
constructor() {
|
||||
super({
|
||||
configName: "lens-user-store",
|
||||
migrations,
|
||||
@@ -163,14 +163,6 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
this.newContexts.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getting default directory to download kubectl binaries
|
||||
* @returns string
|
||||
*/
|
||||
getDefaultKubectlPath(): string {
|
||||
return path.join((app || remote.app).getPath("userData"), "binaries");
|
||||
}
|
||||
|
||||
@action
|
||||
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
||||
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data;
|
||||
@@ -200,4 +192,10 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
}
|
||||
}
|
||||
|
||||
export const userStore = UserStore.getInstance<UserStore>();
|
||||
/**
|
||||
* Getting default directory to download kubectl binaries
|
||||
* @returns string
|
||||
*/
|
||||
export function getDefaultKubectlPath(): string {
|
||||
return path.join((app || remote.app).getPath("userData"), "binaries");
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
export function debouncePromise<T, F extends any[]>(func: (...args: F) => T | Promise<T>, timeout = 0): (...args: F) => Promise<T> {
|
||||
let timer: NodeJS.Timeout;
|
||||
|
||||
return (...params: any[]) => new Promise(resolve => {
|
||||
return (...params: F) => new Promise(resolve => {
|
||||
clearTimeout(timer);
|
||||
timer = global.setTimeout(() => resolve(func.apply(this, params)), timeout);
|
||||
timer = global.setTimeout(() => resolve(func(...params)), timeout);
|
||||
});
|
||||
}
|
||||
@@ -5,25 +5,39 @@
|
||||
* @example
|
||||
* const usersStore: UsersStore = UsersStore.getInstance();
|
||||
*/
|
||||
type StaticThis<T, R extends any[]> = { new(...args: R): T };
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
class Singleton {
|
||||
export class Singleton {
|
||||
private static instances = new WeakMap<object, Singleton>();
|
||||
private static creating = "";
|
||||
|
||||
// todo: improve types inferring
|
||||
static getInstance<T>(...args: ConstructorParameters<Constructor<T>>): T {
|
||||
constructor() {
|
||||
if (Singleton.creating.length === 0) {
|
||||
throw new TypeError("A singleton class must be created by getInstanceOrCreate()");
|
||||
}
|
||||
}
|
||||
|
||||
static getInstanceOrCreate<T, R extends any[]>(this: StaticThis<T, R>, ...args: R): T {
|
||||
if (!Singleton.instances.has(this)) {
|
||||
Singleton.instances.set(this, Reflect.construct(this, args));
|
||||
Singleton.creating = this.name;
|
||||
Singleton.instances.set(this, new this(...args));
|
||||
Singleton.creating = "";
|
||||
}
|
||||
|
||||
return Singleton.instances.get(this) as T;
|
||||
}
|
||||
|
||||
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict = true): T | undefined {
|
||||
if (!Singleton.instances.has(this) && strict) {
|
||||
throw new TypeError(`instance of ${this.name} is not created`);
|
||||
}
|
||||
|
||||
return Singleton.instances.get(this) as (T | undefined);
|
||||
}
|
||||
|
||||
static resetInstance() {
|
||||
Singleton.instances.delete(this);
|
||||
}
|
||||
}
|
||||
|
||||
export { Singleton };
|
||||
export default Singleton;
|
||||
Reference in new issue
Block a user