mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Fix hotbar migration of workspaces (#3178)
This commit is contained in:
parent
b9dfd20a93
commit
51655884c7
@ -37,27 +37,27 @@ interface DoneCallback {
|
|||||||
* This is necessary because Jest doesn't do this correctly.
|
* This is necessary because Jest doesn't do this correctly.
|
||||||
* @param fn The function to call
|
* @param fn The function to call
|
||||||
*/
|
*/
|
||||||
export function wrapJestLifecycle(fn: () => Promise<void>): (done: DoneCallback) => void {
|
export function wrapJestLifecycle(fn: () => Promise<void> | void): (done: DoneCallback) => void {
|
||||||
return function (done: DoneCallback) {
|
return function (done: DoneCallback) {
|
||||||
fn()
|
(async () => fn())()
|
||||||
.then(() => done())
|
.then(() => done())
|
||||||
.catch(error => done.fail(error));
|
.catch(error => done.fail(error));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function beforeAllWrapped(fn: () => Promise<void>): void {
|
export function beforeAllWrapped(fn: () => Promise<void> | void): void {
|
||||||
beforeAll(wrapJestLifecycle(fn));
|
beforeAll(wrapJestLifecycle(fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function beforeEachWrapped(fn: () => Promise<void>): void {
|
export function beforeEachWrapped(fn: () => Promise<void> | void): void {
|
||||||
beforeEach(wrapJestLifecycle(fn));
|
beforeEach(wrapJestLifecycle(fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function afterAllWrapped(fn: () => Promise<void>): void {
|
export function afterAllWrapped(fn: () => Promise<void> | void): void {
|
||||||
afterAll(wrapJestLifecycle(fn));
|
afterAll(wrapJestLifecycle(fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function afterEachWrapped(fn: () => Promise<void>): void {
|
export function afterEachWrapped(fn: () => Promise<void> | void): void {
|
||||||
afterEach(wrapJestLifecycle(fn));
|
afterEach(wrapJestLifecycle(fn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,7 @@ describe("empty config", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
await ClusterStore.createInstance().load();
|
ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -203,7 +203,7 @@ describe("config with existing clusters", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -285,7 +285,7 @@ users:
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -344,7 +344,7 @@ describe("pre 2.0 config with an existing cluster", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -414,7 +414,7 @@ describe("pre 2.6.0 config with a cluster that has arrays in auth config", () =>
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -456,7 +456,7 @@ describe("pre 2.6.0 config with a cluster icon", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -495,7 +495,7 @@ describe("for a pre 2.7.0-beta.0 config without a workspace", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -531,7 +531,7 @@ describe("pre 3.6.0-beta.1 config with an existing cluster", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return ClusterStore.createInstance().load();
|
return ClusterStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import mockFs from "mock-fs";
|
|||||||
import { ClusterStore } from "../cluster-store";
|
import { ClusterStore } from "../cluster-store";
|
||||||
import { HotbarStore } from "../hotbar-store";
|
import { HotbarStore } from "../hotbar-store";
|
||||||
|
|
||||||
jest.mock("../../renderer/api/catalog-entity-registry", () => ({
|
jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
||||||
catalogEntityRegistry: {
|
catalogEntityRegistry: {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -39,7 +39,14 @@ jest.mock("../../renderer/api/catalog-entity-registry", () => ({
|
|||||||
name: "my_shiny_cluster",
|
name: "my_shiny_cluster",
|
||||||
source: "remote"
|
source: "remote"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
metadata: {
|
||||||
|
uid: "catalog-entity",
|
||||||
|
name: "Catalog",
|
||||||
|
source: "app"
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@ -120,29 +127,31 @@ jest.mock("electron", () => {
|
|||||||
|
|
||||||
describe("HotbarStore", () => {
|
describe("HotbarStore", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
ClusterStore.resetInstance();
|
mockFs({
|
||||||
|
"tmp": {
|
||||||
|
"lens-hotbar-store.json": JSON.stringify({})
|
||||||
|
}
|
||||||
|
});
|
||||||
ClusterStore.createInstance();
|
ClusterStore.createInstance();
|
||||||
|
HotbarStore.createInstance();
|
||||||
HotbarStore.resetInstance();
|
|
||||||
mockFs({ tmp: { "lens-hotbar-store.json": "{}" } });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
ClusterStore.resetInstance();
|
||||||
|
HotbarStore.resetInstance();
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("load", () => {
|
describe("load", () => {
|
||||||
it("loads one hotbar by default", () => {
|
it("loads one hotbar by default", () => {
|
||||||
HotbarStore.createInstance().load();
|
|
||||||
expect(HotbarStore.getInstance().hotbars.length).toEqual(1);
|
expect(HotbarStore.getInstance().hotbars.length).toEqual(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("add", () => {
|
describe("add", () => {
|
||||||
it("adds a hotbar", () => {
|
it("adds a hotbar", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.add({ name: "hottest" });
|
hotbarStore.add({ name: "hottest" });
|
||||||
expect(hotbarStore.hotbars.length).toEqual(2);
|
expect(hotbarStore.hotbars.length).toEqual(2);
|
||||||
});
|
});
|
||||||
@ -150,23 +159,20 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
describe("hotbar items", () => {
|
describe("hotbar items", () => {
|
||||||
it("initially creates 12 empty cells", () => {
|
it("initially creates 12 empty cells", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
expect(hotbarStore.getActive().items.length).toEqual(12);
|
expect(hotbarStore.getActive().items.length).toEqual(12);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("initially adds catalog entity as first item", () => {
|
it("initially adds catalog entity as first item", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
expect(hotbarStore.getActive().items[0].entity.name).toEqual("Catalog");
|
expect(hotbarStore.getActive().items[0].entity.name).toEqual("Catalog");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("adds items", () => {
|
it("adds items", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||||
|
|
||||||
@ -174,9 +180,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("removes items", () => {
|
it("removes items", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.removeFromHotbar("test");
|
hotbarStore.removeFromHotbar("test");
|
||||||
hotbarStore.removeFromHotbar("catalog-entity");
|
hotbarStore.removeFromHotbar("catalog-entity");
|
||||||
@ -186,9 +191,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does nothing if removing with invalid uid", () => {
|
it("does nothing if removing with invalid uid", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.removeFromHotbar("invalid uid");
|
hotbarStore.removeFromHotbar("invalid uid");
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||||
@ -197,9 +201,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("moves item to empty cell", () => {
|
it("moves item to empty cell", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
@ -213,9 +216,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("moves items down", () => {
|
it("moves items down", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
@ -229,9 +231,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("moves items up", () => {
|
it("moves items up", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
@ -245,9 +246,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("does nothing when item moved to same cell", () => {
|
it("does nothing when item moved to same cell", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.restackItems(1, 1);
|
hotbarStore.restackItems(1, 1);
|
||||||
|
|
||||||
@ -255,9 +255,8 @@ describe("HotbarStore", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("new items takes first empty cell", () => {
|
it("new items takes first empty cell", () => {
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
hotbarStore.restackItems(0, 3);
|
hotbarStore.restackItems(0, 3);
|
||||||
@ -268,13 +267,13 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
it("throws if invalid arguments provided", () => {
|
it("throws if invalid arguments provided", () => {
|
||||||
// Prevent writing to stderr during this render.
|
// Prevent writing to stderr during this render.
|
||||||
const err = console.error;
|
const { error, warn } = console;
|
||||||
|
|
||||||
console.error = jest.fn();
|
console.error = jest.fn();
|
||||||
|
console.warn = jest.fn();
|
||||||
|
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
|
||||||
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
||||||
@ -283,7 +282,8 @@ describe("HotbarStore", () => {
|
|||||||
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
||||||
|
|
||||||
// Restore writing to stderr.
|
// Restore writing to stderr.
|
||||||
console.error = err;
|
console.error = error;
|
||||||
|
console.warn = warn;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -354,7 +354,7 @@ describe("HotbarStore", () => {
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
return HotbarStore.createInstance().load();
|
HotbarStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@ -52,7 +52,7 @@ describe("user store tests", () => {
|
|||||||
|
|
||||||
(UserStore.createInstance() as any).refreshNewContexts = jest.fn(() => Promise.resolve());
|
(UserStore.createInstance() as any).refreshNewContexts = jest.fn(() => Promise.resolve());
|
||||||
|
|
||||||
return UserStore.getInstance().load();
|
UserStore.getInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@ -81,10 +81,8 @@ describe("user store tests", () => {
|
|||||||
it("correctly resets theme to default value", async () => {
|
it("correctly resets theme to default value", async () => {
|
||||||
const us = UserStore.getInstance();
|
const us = UserStore.getInstance();
|
||||||
|
|
||||||
us.isLoaded = true;
|
|
||||||
|
|
||||||
us.colorTheme = "some other theme";
|
us.colorTheme = "some other theme";
|
||||||
await us.resetTheme();
|
us.resetTheme();
|
||||||
expect(us.colorTheme).toBe(UserStore.defaultTheme);
|
expect(us.colorTheme).toBe(UserStore.defaultTheme);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,7 +109,7 @@ describe("user store tests", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return UserStore.createInstance().load();
|
UserStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@ -23,41 +23,42 @@ import path from "path";
|
|||||||
import Config from "conf";
|
import Config from "conf";
|
||||||
import type { Options as ConfOptions } from "conf/dist/source/types";
|
import type { Options as ConfOptions } from "conf/dist/source/types";
|
||||||
import { app, ipcMain, ipcRenderer, remote } from "electron";
|
import { app, ipcMain, ipcRenderer, remote } from "electron";
|
||||||
import { IReactionOptions, makeObservable, observable, reaction, runInAction, when } from "mobx";
|
import { IReactionOptions, makeObservable, reaction, runInAction } from "mobx";
|
||||||
import { getAppVersion, Singleton, toJS, Disposer } from "./utils";
|
import { getAppVersion, Singleton, toJS, Disposer } from "./utils";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
import { broadcastMessage, ipcMainOn, ipcRendererOn } from "./ipc";
|
import { broadcastMessage, ipcMainOn, ipcRendererOn } from "./ipc";
|
||||||
import isEqual from "lodash/isEqual";
|
import isEqual from "lodash/isEqual";
|
||||||
|
|
||||||
export interface BaseStoreParams<T = any> extends ConfOptions<T> {
|
export interface BaseStoreParams<T> extends ConfOptions<T> {
|
||||||
autoLoad?: boolean;
|
|
||||||
syncEnabled?: boolean;
|
|
||||||
syncOptions?: IReactionOptions;
|
syncOptions?: IReactionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: T should only contain base JSON serializable types.
|
* Note: T should only contain base JSON serializable types.
|
||||||
*/
|
*/
|
||||||
export abstract class BaseStore<T = any> extends Singleton {
|
export abstract class BaseStore<T> extends Singleton {
|
||||||
protected storeConfig?: Config<T>;
|
protected storeConfig?: Config<T>;
|
||||||
protected syncDisposers: Disposer[] = [];
|
protected syncDisposers: Disposer[] = [];
|
||||||
|
|
||||||
@observable isLoaded = false;
|
protected constructor(protected params: BaseStoreParams<T>) {
|
||||||
|
|
||||||
get whenLoaded() {
|
|
||||||
return when(() => this.isLoaded);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected constructor(protected params: BaseStoreParams) {
|
|
||||||
super();
|
super();
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
}
|
||||||
|
|
||||||
this.params = {
|
/**
|
||||||
autoLoad: false,
|
* This must be called after the last child's constructor is finished (or just before it finishes)
|
||||||
syncEnabled: true,
|
*/
|
||||||
...params,
|
load() {
|
||||||
};
|
this.storeConfig = new Config({
|
||||||
this.init();
|
...this.params,
|
||||||
|
projectName: "lens",
|
||||||
|
projectVersion: getAppVersion(),
|
||||||
|
cwd: this.cwd(),
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`[STORE]: LOADED from ${this.path}`);
|
||||||
|
this.fromStore(this.storeConfig.store);
|
||||||
|
this.enableSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
@ -76,31 +77,6 @@ export abstract class BaseStore<T = any> extends Singleton {
|
|||||||
return this.storeConfig?.path || "";
|
return this.storeConfig?.path || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async init() {
|
|
||||||
if (this.params.autoLoad) {
|
|
||||||
await this.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.params.syncEnabled) {
|
|
||||||
await this.whenLoaded;
|
|
||||||
this.enableSync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async load() {
|
|
||||||
const { autoLoad, syncEnabled, ...confOptions } = this.params;
|
|
||||||
|
|
||||||
this.storeConfig = new Config({
|
|
||||||
...confOptions,
|
|
||||||
projectName: "lens",
|
|
||||||
projectVersion: getAppVersion(),
|
|
||||||
cwd: this.cwd(),
|
|
||||||
});
|
|
||||||
logger.info(`[STORE]: LOADED from ${this.path}`);
|
|
||||||
this.fromStore(this.storeConfig.store);
|
|
||||||
this.isLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected cwd() {
|
protected cwd() {
|
||||||
return (app || remote.app).getPath("userData");
|
return (app || remote.app).getPath("userData");
|
||||||
}
|
}
|
||||||
@ -159,11 +135,8 @@ export abstract class BaseStore<T = any> extends Singleton {
|
|||||||
protected applyWithoutSync(callback: () => void) {
|
protected applyWithoutSync(callback: () => void) {
|
||||||
this.disableSync();
|
this.disableSync();
|
||||||
runInAction(callback);
|
runInAction(callback);
|
||||||
|
|
||||||
if (this.params.syncEnabled) {
|
|
||||||
this.enableSync();
|
this.enableSync();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected onSync(model: T) {
|
protected onSync(model: T) {
|
||||||
// todo: use "resourceVersion" if merge required (to avoid equality checks => better performance)
|
// todo: use "resourceVersion" if merge required (to avoid equality checks => better performance)
|
||||||
|
|||||||
@ -110,6 +110,8 @@ export interface ClusterPrometheusPreferences {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialStates = "cluster:states";
|
||||||
|
|
||||||
export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
||||||
private static StateChannel = "cluster:state";
|
private static StateChannel = "cluster:state";
|
||||||
|
|
||||||
@ -121,8 +123,8 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
return path.resolve(ClusterStore.storedKubeConfigFolder, clusterId);
|
return path.resolve(ClusterStore.storedKubeConfigFolder, clusterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable clusters = observable.map<ClusterId, Cluster>();
|
clusters = observable.map<ClusterId, Cluster>();
|
||||||
@observable removedClusters = observable.map<ClusterId, Cluster>();
|
removedClusters = observable.map<ClusterId, Cluster>();
|
||||||
|
|
||||||
protected disposer = disposer();
|
protected disposer = disposer();
|
||||||
|
|
||||||
@ -137,22 +139,19 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
this.load();
|
||||||
this.pushStateToViewsAutomatically();
|
this.pushStateToViewsAutomatically();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async loadInitialOnRenderer() {
|
||||||
const initialStates = "cluster:states";
|
|
||||||
|
|
||||||
await super.load();
|
|
||||||
|
|
||||||
if (ipcRenderer) {
|
|
||||||
logger.info("[CLUSTER-STORE] requesting initial state sync");
|
logger.info("[CLUSTER-STORE] requesting initial state sync");
|
||||||
|
|
||||||
for (const { id, state } of await requestMain(initialStates)) {
|
for (const { id, state } of await requestMain(initialStates)) {
|
||||||
this.getById(id)?.setState(state);
|
this.getById(id)?.setState(state);
|
||||||
}
|
}
|
||||||
} else if (ipcMain) {
|
}
|
||||||
|
|
||||||
|
provideInitialFromMain() {
|
||||||
ipcMainHandle(initialStates, () => {
|
ipcMainHandle(initialStates, () => {
|
||||||
return this.clustersList.map(cluster => ({
|
return this.clustersList.map(cluster => ({
|
||||||
id: cluster.id,
|
id: cluster.id,
|
||||||
@ -160,7 +159,6 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected pushStateToViewsAutomatically() {
|
protected pushStateToViewsAutomatically() {
|
||||||
if (ipcMain) {
|
if (ipcMain) {
|
||||||
|
|||||||
@ -26,7 +26,6 @@ import * as uuid from "uuid";
|
|||||||
import isNull from "lodash/isNull";
|
import isNull from "lodash/isNull";
|
||||||
import { toJS } from "./utils";
|
import { toJS } from "./utils";
|
||||||
import { CatalogEntity } from "./catalog";
|
import { CatalogEntity } from "./catalog";
|
||||||
import { catalogEntity } from "../main/catalog-sources/general";
|
|
||||||
|
|
||||||
export interface HotbarItem {
|
export interface HotbarItem {
|
||||||
entity: {
|
entity: {
|
||||||
@ -39,16 +38,12 @@ export interface HotbarItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Hotbar {
|
export type Hotbar = Required<HotbarCreateOptions>;
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
items: HotbarItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HotbarCreateOptions {
|
export interface HotbarCreateOptions {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
items?: HotbarItem[];
|
items?: (HotbarItem | null)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HotbarStoreModel {
|
export interface HotbarStoreModel {
|
||||||
@ -72,6 +67,7 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
migrations,
|
migrations,
|
||||||
});
|
});
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeHotbarId() {
|
get activeHotbarId() {
|
||||||
@ -92,30 +88,13 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
return this.hotbarIndex(this.activeHotbarId);
|
return this.hotbarIndex(this.activeHotbarId);
|
||||||
}
|
}
|
||||||
|
|
||||||
get defaultHotbarInitialItems() {
|
static getInitialItems() {
|
||||||
const { metadata: { uid, name, source } } = catalogEntity;
|
|
||||||
const initialItem = { entity: { uid, name, source }};
|
|
||||||
|
|
||||||
return [
|
|
||||||
initialItem,
|
|
||||||
...Array.from(Array(defaultHotbarCells - 1).fill(null))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
get initialItems() {
|
|
||||||
return [...Array.from(Array(defaultHotbarCells).fill(null))];
|
return [...Array.from(Array(defaultHotbarCells).fill(null))];
|
||||||
}
|
}
|
||||||
|
|
||||||
@action protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
|
@action
|
||||||
if (data.hotbars?.length === 0) {
|
protected async fromStore(data: Partial<HotbarStoreModel> = {}) {
|
||||||
this.hotbars = [{
|
|
||||||
id: uuid.v4(),
|
|
||||||
name: "Default",
|
|
||||||
items: this.defaultHotbarInitialItems,
|
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
this.hotbars = data.hotbars;
|
this.hotbars = data.hotbars;
|
||||||
}
|
|
||||||
|
|
||||||
if (data.activeHotbarId) {
|
if (data.activeHotbarId) {
|
||||||
if (this.getById(data.activeHotbarId)) {
|
if (this.getById(data.activeHotbarId)) {
|
||||||
@ -140,18 +119,19 @@ export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|||||||
return this.hotbars.find((hotbar) => hotbar.id === id);
|
return this.hotbars.find((hotbar) => hotbar.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(data: HotbarCreateOptions) {
|
@action
|
||||||
|
add(data: HotbarCreateOptions, { setActive = false } = {}) {
|
||||||
const {
|
const {
|
||||||
id = uuid.v4(),
|
id = uuid.v4(),
|
||||||
items = this.initialItems,
|
items = HotbarStore.getInitialItems(),
|
||||||
name,
|
name,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
const hotbar = { id, name, items };
|
this.hotbars.push({ id, name, items });
|
||||||
|
|
||||||
this.hotbars.push(hotbar as Hotbar);
|
if (setActive) {
|
||||||
|
this._activeHotbarId = id;
|
||||||
return hotbar as Hotbar;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@ -68,7 +68,10 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
configName: "lens-user-store",
|
configName: "lens-user-store",
|
||||||
migrations,
|
migrations,
|
||||||
});
|
});
|
||||||
|
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
fileNameMigration();
|
||||||
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@observable lastSeenAppVersion = "0.0.0";
|
@observable lastSeenAppVersion = "0.0.0";
|
||||||
@ -101,15 +104,15 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
[path.join(os.homedir(), ".kube"), {}]
|
[path.join(os.homedir(), ".kube"), {}]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
async load(): Promise<void> {
|
@computed get isNewVersion() {
|
||||||
/**
|
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
||||||
* This has to be here before the call to `new Config` in `super.load()`
|
}
|
||||||
* as we have to make sure that file is in the expected place for that call
|
|
||||||
*/
|
|
||||||
await fileNameMigration();
|
|
||||||
await super.load();
|
|
||||||
|
|
||||||
if (app) {
|
@computed get resolvedShell(): string | undefined {
|
||||||
|
return this.shell || process.env.SHELL || process.env.PTYSHELL;
|
||||||
|
}
|
||||||
|
|
||||||
|
startMainReactions() {
|
||||||
// track telemetry availability
|
// track telemetry availability
|
||||||
reaction(() => this.allowTelemetry, allowed => {
|
reaction(() => this.allowTelemetry, allowed => {
|
||||||
appEventBus.emit({ name: "telemetry", action: allowed ? "enabled" : "disabled" });
|
appEventBus.emit({ name: "telemetry", action: allowed ? "enabled" : "disabled" });
|
||||||
@ -126,15 +129,6 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
fireImmediately: true,
|
fireImmediately: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@computed get isNewVersion() {
|
|
||||||
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get resolvedShell(): string | undefined {
|
|
||||||
return this.shell || process.env.SHELL || process.env.PTYSHELL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a column (by ID) for a table (by ID) is configured to be hidden
|
* Checks if a column (by ID) for a table (by ID) is configured to be hidden
|
||||||
@ -165,8 +159,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async resetTheme() {
|
resetTheme() {
|
||||||
await this.whenLoaded;
|
|
||||||
this.colorTheme = UserStore.defaultTheme;
|
this.colorTheme = UserStore.defaultTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,6 +55,7 @@ export class WeblinkStore extends BaseStore<WeblinkStoreModel> {
|
|||||||
migrations,
|
migrations,
|
||||||
});
|
});
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@action protected async fromStore(data: Partial<WeblinkStoreModel> = {}) {
|
@action protected async fromStore(data: Partial<WeblinkStoreModel> = {}) {
|
||||||
|
|||||||
@ -39,6 +39,12 @@ jest.mock("../extension-installer", () => ({
|
|||||||
installPackage: jest.fn()
|
installPackage: jest.fn()
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
jest.mock("electron", () => ({
|
||||||
|
app: {
|
||||||
|
getPath: () => "tmp",
|
||||||
|
setLoginItemSettings: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
console = new Console(process.stdout, process.stderr); // fix mockFS
|
console = new Console(process.stdout, process.stderr); // fix mockFS
|
||||||
const mockedWatch = watch as jest.MockedFunction<typeof watch>;
|
const mockedWatch = watch as jest.MockedFunction<typeof watch>;
|
||||||
|
|||||||
@ -124,7 +124,7 @@ export class ExtensionLoader extends Singleton {
|
|||||||
await this.initMain();
|
await this.initMain();
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all([this.whenLoaded, ExtensionsStore.getInstance().whenLoaded]);
|
await Promise.all([this.whenLoaded]);
|
||||||
|
|
||||||
// broadcasting extensions between main/renderer processes
|
// broadcasting extensions between main/renderer processes
|
||||||
reaction(() => this.toJSON(), () => this.broadcastExtensions(), {
|
reaction(() => this.toJSON(), () => this.broadcastExtensions(), {
|
||||||
|
|||||||
@ -26,13 +26,13 @@ import type { LensExtension } from "./lens-extension";
|
|||||||
export abstract class ExtensionStore<T> extends BaseStore<T> {
|
export abstract class ExtensionStore<T> extends BaseStore<T> {
|
||||||
protected extension: LensExtension;
|
protected extension: LensExtension;
|
||||||
|
|
||||||
async loadExtension(extension: LensExtension) {
|
loadExtension(extension: LensExtension) {
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
|
|
||||||
return super.load();
|
return super.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
load() {
|
||||||
if (!this.extension) { return; }
|
if (!this.extension) { return; }
|
||||||
|
|
||||||
return super.load();
|
return super.load();
|
||||||
|
|||||||
@ -39,6 +39,7 @@ export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
|
|||||||
configName: "lens-extensions",
|
configName: "lens-extensions",
|
||||||
});
|
});
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
|
|||||||
@ -22,6 +22,14 @@
|
|||||||
import { UserStore } from "../../common/user-store";
|
import { UserStore } from "../../common/user-store";
|
||||||
import { ContextHandler } from "../context-handler";
|
import { ContextHandler } from "../context-handler";
|
||||||
import { PrometheusProvider, PrometheusProviderRegistry, PrometheusService } from "../prometheus";
|
import { PrometheusProvider, PrometheusProviderRegistry, PrometheusService } from "../prometheus";
|
||||||
|
import mockFs from "mock-fs";
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({
|
||||||
|
app: {
|
||||||
|
getPath: () => "tmp",
|
||||||
|
setLoginItemSettings: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
enum ServiceResult {
|
enum ServiceResult {
|
||||||
Success,
|
Success,
|
||||||
@ -70,6 +78,10 @@ function getHandler() {
|
|||||||
|
|
||||||
describe("ContextHandler", () => {
|
describe("ContextHandler", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mockFs({
|
||||||
|
"tmp": {}
|
||||||
|
});
|
||||||
|
|
||||||
PrometheusProviderRegistry.createInstance();
|
PrometheusProviderRegistry.createInstance();
|
||||||
UserStore.createInstance();
|
UserStore.createInstance();
|
||||||
});
|
});
|
||||||
@ -77,6 +89,7 @@ describe("ContextHandler", () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
PrometheusProviderRegistry.resetInstance();
|
PrometheusProviderRegistry.resetInstance();
|
||||||
UserStore.resetInstance();
|
UserStore.resetInstance();
|
||||||
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getPrometheusService", () => {
|
describe("getPrometheusService", () => {
|
||||||
|
|||||||
@ -46,7 +46,8 @@ jest.mock("winston", () => ({
|
|||||||
|
|
||||||
jest.mock("electron", () => ({
|
jest.mock("electron", () => ({
|
||||||
app: {
|
app: {
|
||||||
getPath: () => "/foo",
|
getPath: () => "tmp",
|
||||||
|
setLoginItemSettings: jest.fn(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -77,8 +78,6 @@ const mockWaitUntilUsed = waitUntilUsed as jest.MockedFunction<typeof waitUntilU
|
|||||||
describe("kube auth proxy tests", () => {
|
describe("kube auth proxy tests", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
UserStore.resetInstance();
|
|
||||||
UserStore.createInstance();
|
|
||||||
|
|
||||||
const mockMinikubeConfig = {
|
const mockMinikubeConfig = {
|
||||||
"minikube-config.yml": JSON.stringify({
|
"minikube-config.yml": JSON.stringify({
|
||||||
@ -102,13 +101,16 @@ describe("kube auth proxy tests", () => {
|
|||||||
}],
|
}],
|
||||||
kind: "Config",
|
kind: "Config",
|
||||||
preferences: {},
|
preferences: {},
|
||||||
})
|
}),
|
||||||
|
"tmp": {},
|
||||||
};
|
};
|
||||||
|
|
||||||
mockFs(mockMinikubeConfig);
|
mockFs(mockMinikubeConfig);
|
||||||
|
UserStore.createInstance();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
UserStore.resetInstance();
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -67,6 +67,6 @@ const generalEntities = observable([
|
|||||||
preferencesEntity
|
preferencesEntity
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function initializeGeneralEntities() {
|
export function syncGeneralEntities() {
|
||||||
catalogEntityRegistry.addObservableSource("lens:general", generalEntities);
|
catalogEntityRegistry.addObservableSource("lens:general", generalEntities);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,6 @@
|
|||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export { initializeWeblinks } from "./weblinks";
|
export { syncWeblinks } from "./weblinks";
|
||||||
export { KubeconfigSyncManager } from "./kubeconfig-sync";
|
export { KubeconfigSyncManager } from "./kubeconfig-sync";
|
||||||
export { initializeGeneralEntities } from "./general";
|
export { syncGeneralEntities } from "./general";
|
||||||
|
|||||||
@ -69,7 +69,7 @@ async function validateLink(link: WebLink) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function initializeWeblinks() {
|
export function syncWeblinks() {
|
||||||
const weblinkStore = WeblinkStore.getInstance();
|
const weblinkStore = WeblinkStore.getInstance();
|
||||||
const weblinks = observable.array(defaultLinks);
|
const weblinks = observable.array(defaultLinks);
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,7 @@ export class FilesystemProvisionerStore extends BaseStore<FSProvisionModel> {
|
|||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||||
});
|
});
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
|
this.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -48,11 +48,17 @@ import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
|
|||||||
import { pushCatalogToRenderer } from "./catalog-pusher";
|
import { pushCatalogToRenderer } from "./catalog-pusher";
|
||||||
import { catalogEntityRegistry } from "./catalog";
|
import { catalogEntityRegistry } from "./catalog";
|
||||||
import { HelmRepoManager } from "./helm/helm-repo-manager";
|
import { HelmRepoManager } from "./helm/helm-repo-manager";
|
||||||
import { KubeconfigSyncManager } from "./catalog-sources";
|
import { syncGeneralEntities, syncWeblinks, KubeconfigSyncManager } from "./catalog-sources";
|
||||||
import { handleWsUpgrade } from "./proxy/ws-upgrade";
|
import { handleWsUpgrade } from "./proxy/ws-upgrade";
|
||||||
import configurePackages from "../common/configure-packages";
|
import configurePackages from "../common/configure-packages";
|
||||||
import { PrometheusProviderRegistry } from "./prometheus";
|
import { PrometheusProviderRegistry } from "./prometheus";
|
||||||
import * as initializers from "./initializers";
|
import * as initializers from "./initializers";
|
||||||
|
import { ClusterStore } from "../common/cluster-store";
|
||||||
|
import { HotbarStore } from "../common/hotbar-store";
|
||||||
|
import { UserStore } from "../common/user-store";
|
||||||
|
import { WeblinkStore } from "../common/weblink-store";
|
||||||
|
import { ExtensionsStore } from "../extensions/extensions-store";
|
||||||
|
import { FilesystemProvisionerStore } from "./extension-filesystem";
|
||||||
|
|
||||||
const workingDir = path.join(app.getPath("appData"), appName);
|
const workingDir = path.join(app.getPath("appData"), appName);
|
||||||
const cleanup = disposer();
|
const cleanup = disposer();
|
||||||
@ -123,8 +129,23 @@ app.on("ready", async () => {
|
|||||||
PrometheusProviderRegistry.createInstance();
|
PrometheusProviderRegistry.createInstance();
|
||||||
initializers.initPrometheusProviderRegistry();
|
initializers.initPrometheusProviderRegistry();
|
||||||
|
|
||||||
await initializers.initializeStores();
|
/**
|
||||||
initializers.initializeWeblinks();
|
* The following sync MUST be done before HotbarStore creation, because that
|
||||||
|
* store has migrations that will remove items that previous migrations add
|
||||||
|
* if this is not presant
|
||||||
|
*/
|
||||||
|
syncGeneralEntities();
|
||||||
|
|
||||||
|
logger.info("💾 Loading stores");
|
||||||
|
|
||||||
|
UserStore.createInstance().startMainReactions();
|
||||||
|
ClusterStore.createInstance().provideInitialFromMain();
|
||||||
|
HotbarStore.createInstance();
|
||||||
|
ExtensionsStore.createInstance();
|
||||||
|
FilesystemProvisionerStore.createInstance();
|
||||||
|
WeblinkStore.createInstance();
|
||||||
|
|
||||||
|
syncWeblinks();
|
||||||
|
|
||||||
HelmRepoManager.createInstance(); // create the instance
|
HelmRepoManager.createInstance(); // create the instance
|
||||||
|
|
||||||
@ -182,7 +203,6 @@ app.on("ready", async () => {
|
|||||||
ipcMainOn(IpcRendererNavigationEvents.LOADED, () => {
|
ipcMainOn(IpcRendererNavigationEvents.LOADED, () => {
|
||||||
cleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
|
cleanup.push(pushCatalogToRenderer(catalogEntityRegistry));
|
||||||
KubeconfigSyncManager.getInstance().startSync();
|
KubeconfigSyncManager.getInstance().startSync();
|
||||||
initializers.initializeGeneralEntities();
|
|
||||||
startUpdateChecking();
|
startUpdateChecking();
|
||||||
LensProtocolRouterMain.getInstance().rendererLoaded = true;
|
LensProtocolRouterMain.getInstance().rendererLoaded = true;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { initializeGeneralEntities } from "../catalog-sources";
|
|
||||||
@ -22,6 +22,3 @@
|
|||||||
export * from "./registries";
|
export * from "./registries";
|
||||||
export * from "./metrics-providers";
|
export * from "./metrics-providers";
|
||||||
export * from "./ipc";
|
export * from "./ipc";
|
||||||
export * from "./weblinks";
|
|
||||||
export * from "./stores";
|
|
||||||
export * from "./general-entities";
|
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 { HotbarStore } from "../../common/hotbar-store";
|
|
||||||
import { ClusterStore } from "../../common/cluster-store";
|
|
||||||
import { UserStore } from "../../common/user-store";
|
|
||||||
import { ExtensionsStore } from "../../extensions/extensions-store";
|
|
||||||
import { FilesystemProvisionerStore } from "../extension-filesystem";
|
|
||||||
import { WeblinkStore } from "../../common/weblink-store";
|
|
||||||
import logger from "../logger";
|
|
||||||
|
|
||||||
export async function initializeStores() {
|
|
||||||
const userStore = UserStore.createInstance();
|
|
||||||
const clusterStore = ClusterStore.createInstance();
|
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
|
||||||
const extensionsStore = ExtensionsStore.createInstance();
|
|
||||||
const filesystemStore = FilesystemProvisionerStore.createInstance();
|
|
||||||
const weblinkStore = WeblinkStore.createInstance();
|
|
||||||
|
|
||||||
logger.info("💾 Loading stores");
|
|
||||||
// preload
|
|
||||||
await Promise.all([
|
|
||||||
userStore.load(),
|
|
||||||
clusterStore.load(),
|
|
||||||
hotbarStore.load(),
|
|
||||||
extensionsStore.load(),
|
|
||||||
filesystemStore.load(),
|
|
||||||
weblinkStore.load()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export { initializeWeblinks } from "../catalog-sources";
|
|
||||||
@ -28,9 +28,17 @@ import { LensExtension } from "../../../extensions/main-api";
|
|||||||
import { ExtensionLoader } from "../../../extensions/extension-loader";
|
import { ExtensionLoader } from "../../../extensions/extension-loader";
|
||||||
import { ExtensionsStore } from "../../../extensions/extensions-store";
|
import { ExtensionsStore } from "../../../extensions/extensions-store";
|
||||||
import { LensProtocolRouterMain } from "../router";
|
import { LensProtocolRouterMain } from "../router";
|
||||||
|
import mockFs from "mock-fs";
|
||||||
|
|
||||||
jest.mock("../../../common/ipc");
|
jest.mock("../../../common/ipc");
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({
|
||||||
|
app: {
|
||||||
|
getPath: () => "tmp",
|
||||||
|
setLoginItemSettings: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
function throwIfDefined(val: any): void {
|
function throwIfDefined(val: any): void {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
throw val;
|
throw val;
|
||||||
@ -39,6 +47,9 @@ function throwIfDefined(val: any): void {
|
|||||||
|
|
||||||
describe("protocol router tests", () => {
|
describe("protocol router tests", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mockFs({
|
||||||
|
"tmp": {}
|
||||||
|
});
|
||||||
ExtensionsStore.createInstance();
|
ExtensionsStore.createInstance();
|
||||||
ExtensionLoader.createInstance();
|
ExtensionLoader.createInstance();
|
||||||
|
|
||||||
@ -53,6 +64,7 @@ describe("protocol router tests", () => {
|
|||||||
ExtensionsStore.resetInstance();
|
ExtensionsStore.resetInstance();
|
||||||
ExtensionLoader.resetInstance();
|
ExtensionLoader.resetInstance();
|
||||||
LensProtocolRouterMain.resetInstance();
|
LensProtocolRouterMain.resetInstance();
|
||||||
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should throw on non-lens URLS", () => {
|
it("should throw on non-lens URLS", () => {
|
||||||
|
|||||||
@ -23,7 +23,7 @@ import path from "path";
|
|||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
import type { ClusterModel } from "../../common/cluster-store";
|
import type { ClusterModel } from "../../common/cluster-store";
|
||||||
import { MigrationDeclaration, migrationLog } from "../helpers";
|
import type { MigrationDeclaration } from "../helpers";
|
||||||
|
|
||||||
interface Pre500WorkspaceStoreModel {
|
interface Pre500WorkspaceStoreModel {
|
||||||
workspaces: {
|
workspaces: {
|
||||||
@ -45,8 +45,6 @@ export default {
|
|||||||
workspaces.set(id, name);
|
workspaces.set(id, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
migrationLog("workspaces", JSON.stringify([...workspaces.entries()]));
|
|
||||||
|
|
||||||
const clusters: ClusterModel[] = store.get("clusters");
|
const clusters: ClusterModel[] = store.get("clusters");
|
||||||
|
|
||||||
for (const cluster of clusters) {
|
for (const cluster of clusters) {
|
||||||
@ -58,8 +56,6 @@ export default {
|
|||||||
|
|
||||||
store.set("clusters", clusters);
|
store.set("clusters", clusters);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
migrationLog("error", error.path);
|
|
||||||
|
|
||||||
if (!(error.code === "ENOENT" && error.path.endsWith("lens-workspace-store.json"))) {
|
if (!(error.code === "ENOENT" && error.path.endsWith("lens-workspace-store.json"))) {
|
||||||
// ignore lens-workspace-store.json being missing
|
// ignore lens-workspace-store.json being missing
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@ -20,34 +20,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Cleans up a store that had the state related data stored
|
// Cleans up a store that had the state related data stored
|
||||||
import type { Hotbar } from "../../common/hotbar-store";
|
import { Hotbar, HotbarStore } from "../../common/hotbar-store";
|
||||||
import { ClusterStore } from "../../common/cluster-store";
|
|
||||||
import * as uuid from "uuid";
|
import * as uuid from "uuid";
|
||||||
import type { MigrationDeclaration } from "../helpers";
|
import type { MigrationDeclaration } from "../helpers";
|
||||||
|
import { catalogEntity } from "../../main/catalog-sources/general";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
version: "5.0.0-alpha.0",
|
version: "5.0.0-alpha.0",
|
||||||
run(store) {
|
run(store) {
|
||||||
const hotbars: Hotbar[] = [];
|
const hotbar: Hotbar = {
|
||||||
|
id: uuid.v4(),
|
||||||
|
name: "default",
|
||||||
|
items: HotbarStore.getInitialItems(),
|
||||||
|
};
|
||||||
|
|
||||||
ClusterStore.getInstance().clustersList.forEach((cluster: any) => {
|
const { metadata: { uid, name, source } } = catalogEntity;
|
||||||
const name = cluster.workspace;
|
|
||||||
|
|
||||||
if (!name) return;
|
hotbar.items[0] = { entity: { uid, name, source } };
|
||||||
|
|
||||||
let hotbar = hotbars.find((h) => h.name === name);
|
store.set("hotbars", [hotbar]);
|
||||||
|
|
||||||
if (!hotbar) {
|
|
||||||
hotbar = { id: uuid.v4(), name, items: [] };
|
|
||||||
hotbars.push(hotbar);
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbar.items.push({
|
|
||||||
entity: { uid: cluster.id },
|
|
||||||
params: {}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
store.set("hotbars", hotbars);
|
|
||||||
}
|
}
|
||||||
} as MigrationDeclaration;
|
} as MigrationDeclaration;
|
||||||
|
|||||||
@ -19,12 +19,15 @@
|
|||||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { createHash } from "crypto";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import fse from "fs-extra";
|
import fse from "fs-extra";
|
||||||
|
import { isNull } from "lodash";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import * as uuid from "uuid";
|
import * as uuid from "uuid";
|
||||||
import type { ClusterStoreModel } from "../../common/cluster-store";
|
import type { ClusterStoreModel } from "../../common/cluster-store";
|
||||||
import { defaultHotbarCells, Hotbar } from "../../common/hotbar-store";
|
import { defaultHotbarCells, Hotbar, HotbarStore } from "../../common/hotbar-store";
|
||||||
|
import { catalogEntity } from "../../main/catalog-sources/general";
|
||||||
import type { MigrationDeclaration } from "../helpers";
|
import type { MigrationDeclaration } from "../helpers";
|
||||||
|
|
||||||
interface Pre500WorkspaceStoreModel {
|
interface Pre500WorkspaceStoreModel {
|
||||||
@ -49,7 +52,19 @@ export default {
|
|||||||
workspaceHotbars.set(id, {
|
workspaceHotbars.set(id, {
|
||||||
id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs
|
id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs
|
||||||
items: [],
|
items: [],
|
||||||
|
name: `Workspace: ${name}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// grab the default named hotbar or the first.
|
||||||
|
const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default"));
|
||||||
|
const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1);
|
||||||
|
|
||||||
|
workspaceHotbars.set("default", {
|
||||||
name,
|
name,
|
||||||
|
id,
|
||||||
|
items: items.filter(Boolean),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +74,7 @@ export default {
|
|||||||
if (workspaceHotbar?.items.length < defaultHotbarCells) {
|
if (workspaceHotbar?.items.length < defaultHotbarCells) {
|
||||||
workspaceHotbar.items.push({
|
workspaceHotbar.items.push({
|
||||||
entity: {
|
entity: {
|
||||||
uid: cluster.id,
|
uid: createHash("md5").update(`${cluster.kubeConfigPath}:${cluster.contextName}`).digest("hex"),
|
||||||
name: cluster.preferences.clusterName || cluster.contextName,
|
name: cluster.preferences.clusterName || cluster.contextName,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -74,6 +89,46 @@ export default {
|
|||||||
hotbars.push(hotbar);
|
hotbars.push(hotbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finally, make sure that the catalog entity hotbar item is in place.
|
||||||
|
* Just in case something else removed it.
|
||||||
|
*
|
||||||
|
* if every hotbar has elements that all not the `catalog-entity` item
|
||||||
|
*/
|
||||||
|
if (hotbars.every(hotbar => hotbar.items.every(item => item?.entity?.uid !== "catalog-entity"))) {
|
||||||
|
// note, we will add a new whole hotbar here called "default" if that was previously removed
|
||||||
|
const defaultHotbar = hotbars.find(hotbar => hotbar.name === "default");
|
||||||
|
const { metadata: { uid, name, source } } = catalogEntity;
|
||||||
|
|
||||||
|
if (defaultHotbar) {
|
||||||
|
const freeIndex = defaultHotbar.items.findIndex(isNull);
|
||||||
|
|
||||||
|
if (freeIndex === -1) {
|
||||||
|
// making a new hotbar is less destructive if the first hotbar
|
||||||
|
// called "default" is full than overriding a hotbar item
|
||||||
|
const hotbar = {
|
||||||
|
id: uuid.v4(),
|
||||||
|
name: "initial",
|
||||||
|
items: HotbarStore.getInitialItems(),
|
||||||
|
};
|
||||||
|
|
||||||
|
hotbar.items[0] = { entity: { uid, name, source } };
|
||||||
|
hotbars.unshift(hotbar);
|
||||||
|
} else {
|
||||||
|
defaultHotbar.items[freeIndex] = { entity: { uid, name, source } };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const hotbar = {
|
||||||
|
id: uuid.v4(),
|
||||||
|
name: "default",
|
||||||
|
items: HotbarStore.getInitialItems(),
|
||||||
|
};
|
||||||
|
|
||||||
|
hotbar.items[0] = { entity: { uid, name, source } };
|
||||||
|
hotbars.unshift(hotbar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
store.set("hotbars", hotbars);
|
store.set("hotbars", hotbars);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!(error.code === "ENOENT" && error.path.endsWith("lens-workspace-store.json"))) {
|
if (!(error.code === "ENOENT" && error.path.endsWith("lens-workspace-store.json"))) {
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Hotbar } from "../../common/hotbar-store";
|
import type { Hotbar } from "../../common/hotbar-store";
|
||||||
import { catalogEntityRegistry } from "../../renderer/api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "../../main/catalog";
|
||||||
import type { MigrationDeclaration } from "../helpers";
|
import type { MigrationDeclaration } from "../helpers";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@ -23,18 +23,18 @@ import fse from "fs-extra";
|
|||||||
import { app, remote } from "electron";
|
import { app, remote } from "electron";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
export async function fileNameMigration() {
|
export function fileNameMigration() {
|
||||||
const userDataPath = (app || remote.app).getPath("userData");
|
const userDataPath = (app || remote.app).getPath("userData");
|
||||||
const configJsonPath = path.join(userDataPath, "config.json");
|
const configJsonPath = path.join(userDataPath, "config.json");
|
||||||
const lensUserStoreJsonPath = path.join(userDataPath, "lens-user-store.json");
|
const lensUserStoreJsonPath = path.join(userDataPath, "lens-user-store.json");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fse.move(configJsonPath, lensUserStoreJsonPath);
|
fse.moveSync(configJsonPath, lensUserStoreJsonPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === "ENOENT" && error.path === configJsonPath) { // (No such file or directory)
|
if (error.code === "ENOENT" && error.path === configJsonPath) { // (No such file or directory)
|
||||||
return; // file already moved
|
return; // file already moved
|
||||||
} else if (error.message === "dest already exists.") {
|
} else if (error.message === "dest already exists.") {
|
||||||
await fse.remove(configJsonPath);
|
fse.removeSync(configJsonPath);
|
||||||
} else {
|
} else {
|
||||||
// pass other errors along
|
// pass other errors along
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@ -42,6 +42,11 @@ import { ExtensionInstallationStateStore } from "./components/+extensions/extens
|
|||||||
import { DefaultProps } from "./mui-base-theme";
|
import { DefaultProps } from "./mui-base-theme";
|
||||||
import configurePackages from "../common/configure-packages";
|
import configurePackages from "../common/configure-packages";
|
||||||
import * as initializers from "./initializers";
|
import * as initializers from "./initializers";
|
||||||
|
import { HotbarStore } from "../common/hotbar-store";
|
||||||
|
import { WeblinkStore } from "../common/weblink-store";
|
||||||
|
import { ExtensionsStore } from "../extensions/extensions-store";
|
||||||
|
import { FilesystemProvisionerStore } from "../main/extension-filesystem";
|
||||||
|
import { ThemeStore } from "./theme.store";
|
||||||
|
|
||||||
configurePackages();
|
configurePackages();
|
||||||
|
|
||||||
@ -79,7 +84,13 @@ export async function bootstrap(App: AppComponent) {
|
|||||||
ExtensionLoader.createInstance().init();
|
ExtensionLoader.createInstance().init();
|
||||||
ExtensionDiscovery.createInstance().init();
|
ExtensionDiscovery.createInstance().init();
|
||||||
|
|
||||||
await initializers.initStores();
|
UserStore.createInstance();
|
||||||
|
await ClusterStore.createInstance().loadInitialOnRenderer();
|
||||||
|
HotbarStore.createInstance();
|
||||||
|
ExtensionsStore.createInstance();
|
||||||
|
FilesystemProvisionerStore.createInstance();
|
||||||
|
ThemeStore.createInstance();
|
||||||
|
WeblinkStore.createInstance();
|
||||||
|
|
||||||
ExtensionInstallationStateStore.bindIpcListeners();
|
ExtensionInstallationStateStore.bindIpcListeners();
|
||||||
HelmRepoManager.createInstance(); // initialize the manager
|
HelmRepoManager.createInstance(); // initialize the manager
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import { CatalogEntityDrawerMenu } from "./catalog-entity-drawer-menu";
|
|||||||
import { CatalogEntityDetailRegistry } from "../../../extensions/registries";
|
import { CatalogEntityDetailRegistry } from "../../../extensions/registries";
|
||||||
import { HotbarIcon } from "../hotbar/hotbar-icon";
|
import { HotbarIcon } from "../hotbar/hotbar-icon";
|
||||||
import type { CatalogEntityItem } from "./catalog-entity.store";
|
import type { CatalogEntityItem } from "./catalog-entity.store";
|
||||||
|
import { isDevelopment } from "../../../common/vars";
|
||||||
|
|
||||||
interface Props<T extends CatalogEntity> {
|
interface Props<T extends CatalogEntity> {
|
||||||
item: CatalogEntityItem<T> | null | undefined;
|
item: CatalogEntityItem<T> | null | undefined;
|
||||||
@ -91,6 +92,11 @@ export class CatalogEntityDetails<T extends CatalogEntity> extends Component<Pro
|
|||||||
<DrawerItem name="Labels">
|
<DrawerItem name="Labels">
|
||||||
{...item.getLabelBadges(this.props.hideDetails)}
|
{...item.getLabelBadges(this.props.hideDetails)}
|
||||||
</DrawerItem>
|
</DrawerItem>
|
||||||
|
{isDevelopment && (
|
||||||
|
<DrawerItem name="Id">
|
||||||
|
{item.getId()}
|
||||||
|
</DrawerItem>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -61,7 +61,7 @@ describe("Extensions", () => {
|
|||||||
ExtensionInstallationStateStore.reset();
|
ExtensionInstallationStateStore.reset();
|
||||||
UserStore.resetInstance();
|
UserStore.resetInstance();
|
||||||
|
|
||||||
await UserStore.createInstance().load();
|
UserStore.createInstance();
|
||||||
|
|
||||||
ExtensionDiscovery.resetInstance();
|
ExtensionDiscovery.resetInstance();
|
||||||
ExtensionDiscovery.createInstance().uninstallExtension = jest.fn(() => Promise.resolve());
|
ExtensionDiscovery.createInstance().uninstallExtension = jest.fn(() => Promise.resolve());
|
||||||
|
|||||||
@ -30,10 +30,11 @@ import type { LogTabData } from "../log-tab.store";
|
|||||||
import { dockerPod, deploymentPod1 } from "./pod.mock";
|
import { dockerPod, deploymentPod1 } from "./pod.mock";
|
||||||
import { ThemeStore } from "../../../theme.store";
|
import { ThemeStore } from "../../../theme.store";
|
||||||
import { UserStore } from "../../../../common/user-store";
|
import { UserStore } from "../../../../common/user-store";
|
||||||
|
import mockFs from "mock-fs";
|
||||||
|
|
||||||
jest.mock("electron", () => ({
|
jest.mock("electron", () => ({
|
||||||
app: {
|
app: {
|
||||||
getPath: () => "/foo",
|
getPath: () => "tmp",
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -71,6 +72,9 @@ const getFewPodsTabData = (): LogTabData => {
|
|||||||
|
|
||||||
describe("<LogResourceSelector />", () => {
|
describe("<LogResourceSelector />", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mockFs({
|
||||||
|
"tmp": {}
|
||||||
|
});
|
||||||
UserStore.createInstance();
|
UserStore.createInstance();
|
||||||
ThemeStore.createInstance();
|
ThemeStore.createInstance();
|
||||||
});
|
});
|
||||||
@ -78,6 +82,7 @@ describe("<LogResourceSelector />", () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
UserStore.resetInstance();
|
UserStore.resetInstance();
|
||||||
ThemeStore.resetInstance();
|
ThemeStore.resetInstance();
|
||||||
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders w/o errors", () => {
|
it("renders w/o errors", () => {
|
||||||
|
|||||||
@ -26,6 +26,14 @@ import React from "react";
|
|||||||
import { ThemeStore } from "../../../theme.store";
|
import { ThemeStore } from "../../../theme.store";
|
||||||
import { UserStore } from "../../../../common/user-store";
|
import { UserStore } from "../../../../common/user-store";
|
||||||
import { Notifications } from "../../notifications";
|
import { Notifications } from "../../notifications";
|
||||||
|
import mockFs from "mock-fs";
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({
|
||||||
|
app: {
|
||||||
|
getPath: () => "tmp",
|
||||||
|
setLoginItemSettings: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const mockHotbars: {[id: string]: any} = {
|
const mockHotbars: {[id: string]: any} = {
|
||||||
"1": {
|
"1": {
|
||||||
@ -48,6 +56,9 @@ jest.mock("../../../../common/hotbar-store", () => ({
|
|||||||
|
|
||||||
describe("<HotbarRemoveCommand />", () => {
|
describe("<HotbarRemoveCommand />", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
mockFs({
|
||||||
|
"tmp": {}
|
||||||
|
});
|
||||||
UserStore.createInstance();
|
UserStore.createInstance();
|
||||||
ThemeStore.createInstance();
|
ThemeStore.createInstance();
|
||||||
});
|
});
|
||||||
@ -55,6 +66,7 @@ describe("<HotbarRemoveCommand />", () => {
|
|||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
UserStore.resetInstance();
|
UserStore.resetInstance();
|
||||||
ThemeStore.resetInstance();
|
ThemeStore.resetInstance();
|
||||||
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders w/o errors", () => {
|
it("renders w/o errors", () => {
|
||||||
@ -73,4 +85,3 @@ describe("<HotbarRemoveCommand />", () => {
|
|||||||
spy.mockRestore();
|
spy.mockRestore();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -33,22 +33,14 @@ const uniqueHotbarName: InputValidator = {
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class HotbarAddCommand extends React.Component {
|
export class HotbarAddCommand extends React.Component {
|
||||||
|
onSubmit = (name: string) => {
|
||||||
onSubmit(name: string) {
|
|
||||||
if (!name.trim()) {
|
if (!name.trim()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
HotbarStore.getInstance().add({ name }, { setActive: true });
|
||||||
|
|
||||||
const hotbar = hotbarStore.add({
|
|
||||||
name
|
|
||||||
});
|
|
||||||
|
|
||||||
hotbarStore.activeHotbarId = hotbar.id;
|
|
||||||
|
|
||||||
CommandOverlay.close();
|
CommandOverlay.close();
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
@ -58,10 +50,11 @@ export class HotbarAddCommand extends React.Component {
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
theme="round-black"
|
theme="round-black"
|
||||||
data-test-id="command-palette-hotbar-add-name"
|
data-test-id="command-palette-hotbar-add-name"
|
||||||
validators={[uniqueHotbarName]}
|
validators={uniqueHotbarName}
|
||||||
onSubmit={(v) => this.onSubmit(v)}
|
onSubmit={this.onSubmit}
|
||||||
dirty={true}
|
dirty={true}
|
||||||
showValidationLine={true} />
|
showValidationLine={true}
|
||||||
|
/>
|
||||||
<small className="hint">
|
<small className="hint">
|
||||||
Please provide a new hotbar name (Press "Enter" to confirm or "Escape" to cancel)
|
Please provide a new hotbar name (Press "Enter" to confirm or "Escape" to cancel)
|
||||||
</small>
|
</small>
|
||||||
|
|||||||
@ -27,5 +27,4 @@ export * from "./registries";
|
|||||||
export * from "./welcome-menu-registry";
|
export * from "./welcome-menu-registry";
|
||||||
export * from "./workloads-overview-detail-registry";
|
export * from "./workloads-overview-detail-registry";
|
||||||
export * from "./catalog";
|
export * from "./catalog";
|
||||||
export * from "./stores";
|
|
||||||
export * from "./ipc";
|
export * from "./ipc";
|
||||||
|
|||||||
@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 { HotbarStore } from "../../common/hotbar-store";
|
|
||||||
import { ClusterStore } from "../../common/cluster-store";
|
|
||||||
import { UserStore } from "../../common/user-store";
|
|
||||||
import { ExtensionsStore } from "../../extensions/extensions-store";
|
|
||||||
import { FilesystemProvisionerStore } from "../../main/extension-filesystem";
|
|
||||||
|
|
||||||
import { ThemeStore } from "../theme.store";
|
|
||||||
import { WeblinkStore } from "../../common/weblink-store";
|
|
||||||
|
|
||||||
export async function initStores() {
|
|
||||||
const userStore = UserStore.createInstance();
|
|
||||||
const clusterStore = ClusterStore.createInstance();
|
|
||||||
const extensionsStore = ExtensionsStore.createInstance();
|
|
||||||
const filesystemStore = FilesystemProvisionerStore.createInstance();
|
|
||||||
const themeStore = ThemeStore.createInstance();
|
|
||||||
const hotbarStore = HotbarStore.createInstance();
|
|
||||||
const weblinkStore = WeblinkStore.createInstance();
|
|
||||||
|
|
||||||
// preload common stores
|
|
||||||
await Promise.all([
|
|
||||||
userStore.load(),
|
|
||||||
hotbarStore.load(),
|
|
||||||
clusterStore.load(),
|
|
||||||
extensionsStore.load(),
|
|
||||||
filesystemStore.load(),
|
|
||||||
themeStore.init(),
|
|
||||||
weblinkStore.load()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@ -20,9 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { computed, observable, reaction, makeObservable } from "mobx";
|
import { computed, observable, reaction, makeObservable } from "mobx";
|
||||||
import { autoBind, boundMethod, Singleton } from "./utils";
|
import { autoBind, iter, Singleton } from "./utils";
|
||||||
import { UserStore } from "../common/user-store";
|
import { UserStore } from "../common/user-store";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
|
import darkTheme from "./themes/lens-dark.json";
|
||||||
|
import lightTheme from "./themes/lens-light.json";
|
||||||
|
|
||||||
export type ThemeId = string;
|
export type ThemeId = string;
|
||||||
|
|
||||||
@ -32,12 +34,15 @@ export enum ThemeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Theme {
|
export interface Theme {
|
||||||
id: ThemeId; // filename without .json-extension
|
|
||||||
type: ThemeType;
|
type: ThemeType;
|
||||||
name?: string;
|
name: string;
|
||||||
colors?: Record<string, string>;
|
colors: Record<string, string>;
|
||||||
description?: string;
|
description: string;
|
||||||
author?: string;
|
author: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ThemeItems extends Theme {
|
||||||
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ThemeStore extends Singleton {
|
export class ThemeStore extends Singleton {
|
||||||
@ -45,16 +50,12 @@ export class ThemeStore extends Singleton {
|
|||||||
|
|
||||||
// bundled themes from `themes/${themeId}.json`
|
// bundled themes from `themes/${themeId}.json`
|
||||||
private allThemes = observable.map<string, Theme>([
|
private allThemes = observable.map<string, Theme>([
|
||||||
["lens-dark", { id: "lens-dark", type: ThemeType.DARK }],
|
["lens-dark", { ...darkTheme, type: ThemeType.DARK }],
|
||||||
["lens-light", { id: "lens-light", type: ThemeType.LIGHT }],
|
["lens-light", { ...lightTheme, type: ThemeType.LIGHT }],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@computed get themeIds(): string[] {
|
@computed get themes(): ThemeItems[] {
|
||||||
return Array.from(this.allThemes.keys());
|
return Array.from(iter.map(this.allThemes, ([id, theme]) => ({ id, ...theme })));
|
||||||
}
|
|
||||||
|
|
||||||
@computed get themes(): Theme[] {
|
|
||||||
return Array.from(this.allThemes.values());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get activeThemeId(): string {
|
@computed get activeThemeId(): string {
|
||||||
@ -62,12 +63,7 @@ export class ThemeStore extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@computed get activeTheme(): Theme {
|
@computed get activeTheme(): Theme {
|
||||||
const activeTheme = this.allThemes.get(this.activeThemeId) ?? this.allThemes.get("lens-dark");
|
return this.allThemes.get(this.activeThemeId) ?? this.allThemes.get("lens-dark");
|
||||||
|
|
||||||
return {
|
|
||||||
colors: {},
|
|
||||||
...activeTheme,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -77,9 +73,9 @@ export class ThemeStore extends Singleton {
|
|||||||
autoBind(this);
|
autoBind(this);
|
||||||
|
|
||||||
// auto-apply active theme
|
// auto-apply active theme
|
||||||
reaction(() => this.activeThemeId, async themeId => {
|
reaction(() => this.activeThemeId, themeId => {
|
||||||
try {
|
try {
|
||||||
this.applyTheme(await this.loadTheme(themeId));
|
this.applyTheme(this.getThemeById(themeId));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
UserStore.getInstance().resetTheme();
|
UserStore.getInstance().resetTheme();
|
||||||
@ -89,38 +85,10 @@ export class ThemeStore extends Singleton {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
|
||||||
// preload all themes
|
|
||||||
await Promise.all(this.themeIds.map(this.loadTheme));
|
|
||||||
}
|
|
||||||
|
|
||||||
getThemeById(themeId: ThemeId): Theme {
|
getThemeById(themeId: ThemeId): Theme {
|
||||||
return this.allThemes.get(themeId);
|
return this.allThemes.get(themeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
|
||||||
protected async loadTheme(themeId: ThemeId): Promise<Theme> {
|
|
||||||
try {
|
|
||||||
const existingTheme = this.getThemeById(themeId);
|
|
||||||
|
|
||||||
if (existingTheme) {
|
|
||||||
const theme = await import(
|
|
||||||
/* webpackChunkName: "themes/[name]" */
|
|
||||||
`./themes/${themeId}.json`
|
|
||||||
);
|
|
||||||
|
|
||||||
existingTheme.author = theme.author;
|
|
||||||
existingTheme.colors = theme.colors;
|
|
||||||
existingTheme.description = theme.description;
|
|
||||||
existingTheme.name = theme.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingTheme;
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(`Can't load theme "${themeId}": ${err}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected applyTheme(theme: Theme) {
|
protected applyTheme(theme: Theme) {
|
||||||
if (!this.styles) {
|
if (!this.styles) {
|
||||||
this.styles = document.createElement("style");
|
this.styles = document.createElement("style");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user