mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Remove BaseStore<T> and replace with composition of dependencies (#7002)
* Rename ExtensionStore -> BaseExtensionStore - The name was too close to ExtensionsStore Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move ExtensionsStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move ClusterStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move UserStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup types to remove multiple cast locations Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move HotbarStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move WeblinkStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move FileSystemProvisionerStore to new format Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Clean up impl and rename to better describe intent Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix remaining type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the enabled extensions storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the clusters storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the hotbar storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the weblinks storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fully split apart the user preferences storage Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix crashing Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests and snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix integration test failures Signed-off-by: Sebastian Malton <sebastian@malton.name> * Improve typing to prevent errors in the future. Signed-off-by: Sebastian Malton <sebastian@malton.name> * Cleanup @k8slens/messaging and friends - To fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix lint issue Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix global override not being complete enough causing tests to fail Signed-off-by: Sebastian Malton <sebastian@malton.name> * Bump memory for unit tests on CI Signed-off-by: Sebastian Malton <sebastian@malton.name> * Attempt to fix memory issue on CI again Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fixup test because of new injectables Signed-off-by: Sebastian Malton <sebastian@malton.name> * Upgrade Jest Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix unit tests falling over Signed-off-by: Sebastian Malton <sebastian@malton.name> * Back out jest config change Signed-off-by: Sebastian Malton <sebastian@malton.name> * Remove console log Signed-off-by: Sebastian Malton <sebastian@malton.name> * Update snapshot Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests by matching equality instead of snapshots Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests by forcing specific snapshot style - Ubuntu CI seems to format arrays in snapshots differently than macOS locally Signed-off-by: Sebastian Malton <sebastian@malton.name> --------- Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
ba4a283af9
commit
49f0a1af9c
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@ -121,5 +121,7 @@ jobs:
|
|||||||
retry_on: error
|
retry_on: error
|
||||||
command: npm ci
|
command: npm ci
|
||||||
|
|
||||||
- run: npm run test:unit
|
- run: |
|
||||||
|
npm run build -- --ignore open-lens
|
||||||
|
npm run test:unit
|
||||||
name: Run tests
|
name: Run tests
|
||||||
|
|||||||
2100
package-lock.json
generated
2100
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -144,7 +144,6 @@
|
|||||||
"filehound": "^1.17.6",
|
"filehound": "^1.17.6",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
"glob-to-regexp": "^0.4.1",
|
"glob-to-regexp": "^0.4.1",
|
||||||
"got": "^11.8.6",
|
|
||||||
"grapheme-splitter": "^1.0.4",
|
"grapheme-splitter": "^1.0.4",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"history": "^4.10.1",
|
"history": "^4.10.1",
|
||||||
@ -215,7 +214,7 @@
|
|||||||
"@types/hapi__call": "^9.0.0",
|
"@types/hapi__call": "^9.0.0",
|
||||||
"@types/hapi__subtext": "^7.0.0",
|
"@types/hapi__subtext": "^7.0.0",
|
||||||
"@types/http-proxy": "^1.17.9",
|
"@types/http-proxy": "^1.17.9",
|
||||||
"@types/jest": "^28.1.6",
|
"@types/jest": "^29.5.0",
|
||||||
"@types/js-yaml": "^4.0.5",
|
"@types/js-yaml": "^4.0.5",
|
||||||
"@types/lodash": "^4.14.191",
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/marked": "^4.0.8",
|
"@types/marked": "^4.0.8",
|
||||||
@ -273,7 +272,7 @@
|
|||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"ignore-loader": "^0.1.2",
|
"ignore-loader": "^0.1.2",
|
||||||
"include-media": "^1.4.9",
|
"include-media": "^1.4.9",
|
||||||
"jest": "^28.1.3",
|
"jest": "^29.5.0",
|
||||||
"jest-canvas-mock": "^2.3.1",
|
"jest-canvas-mock": "^2.3.1",
|
||||||
"jest-environment-jsdom": "^28.1.3",
|
"jest-environment-jsdom": "^28.1.3",
|
||||||
"jest-mock-extended": "^2.0.9",
|
"jest-mock-extended": "^2.0.9",
|
||||||
|
|||||||
@ -1,356 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { anyObject } from "jest-mock-extended";
|
|
||||||
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
|
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
|
||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
|
||||||
import hotbarStoreInjectable from "../hotbars/store.injectable";
|
|
||||||
import type { HotbarStore } from "../hotbars/store";
|
|
||||||
import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable";
|
|
||||||
import { computed } from "mobx";
|
|
||||||
import hasCategoryForEntityInjectable from "../catalog/has-category-for-entity.injectable";
|
|
||||||
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import type { Logger } from "../logger";
|
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
|
||||||
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
|
||||||
|
|
||||||
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
|
|
||||||
return {
|
|
||||||
getName: jest.fn(() => data.metadata?.name),
|
|
||||||
getId: jest.fn(() => data.metadata?.uid),
|
|
||||||
getSource: jest.fn(() => data.metadata?.source ?? "unknown"),
|
|
||||||
isEnabled: jest.fn(() => data.status?.enabled ?? true),
|
|
||||||
onContextMenuOpen: jest.fn(),
|
|
||||||
onSettingsOpen: jest.fn(),
|
|
||||||
metadata: {},
|
|
||||||
spec: {},
|
|
||||||
status: {},
|
|
||||||
...data,
|
|
||||||
} as CatalogEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("HotbarStore", () => {
|
|
||||||
let di: DiContainer;
|
|
||||||
let hotbarStore: HotbarStore;
|
|
||||||
let testCluster: CatalogEntity;
|
|
||||||
let minikubeCluster: CatalogEntity;
|
|
||||||
let awsCluster: CatalogEntity;
|
|
||||||
let loggerMock: jest.Mocked<Logger>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
di = getDiForUnitTesting();
|
|
||||||
|
|
||||||
testCluster = getMockCatalogEntity({
|
|
||||||
apiVersion: "v1",
|
|
||||||
kind: "Cluster",
|
|
||||||
status: {
|
|
||||||
phase: "Running",
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
uid: "some-test-id",
|
|
||||||
name: "my-test-cluster",
|
|
||||||
source: "local",
|
|
||||||
labels: {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
minikubeCluster = getMockCatalogEntity({
|
|
||||||
apiVersion: "v1",
|
|
||||||
kind: "Cluster",
|
|
||||||
status: {
|
|
||||||
phase: "Running",
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
uid: "some-minikube-id",
|
|
||||||
name: "my-minikube-cluster",
|
|
||||||
source: "local",
|
|
||||||
labels: {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
awsCluster = getMockCatalogEntity({
|
|
||||||
apiVersion: "v1",
|
|
||||||
kind: "Cluster",
|
|
||||||
status: {
|
|
||||||
phase: "Running",
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
uid: "some-aws-id",
|
|
||||||
name: "my-aws-cluster",
|
|
||||||
source: "local",
|
|
||||||
labels: {},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
di.override(hasCategoryForEntityInjectable, () => () => true);
|
|
||||||
|
|
||||||
loggerMock = {
|
|
||||||
warn: jest.fn(),
|
|
||||||
debug: jest.fn(),
|
|
||||||
error: jest.fn(),
|
|
||||||
info: jest.fn(),
|
|
||||||
silly: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
di.override(loggerInjectable, () => loggerMock);
|
|
||||||
|
|
||||||
di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data");
|
|
||||||
|
|
||||||
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
|
|
||||||
const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable);
|
|
||||||
|
|
||||||
catalogEntityRegistry.addComputedSource("some-id", computed(() => [
|
|
||||||
testCluster,
|
|
||||||
minikubeCluster,
|
|
||||||
awsCluster,
|
|
||||||
catalogCatalogEntity,
|
|
||||||
]));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("given no previous data in store, running all migrations", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
hotbarStore = di.inject(hotbarStoreInjectable);
|
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("load", () => {
|
|
||||||
it("loads one hotbar by default", () => {
|
|
||||||
expect(hotbarStore.hotbars.length).toEqual(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("add", () => {
|
|
||||||
it("adds a hotbar", () => {
|
|
||||||
hotbarStore.add({ name: "hottest" });
|
|
||||||
expect(hotbarStore.hotbars.length).toEqual(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("hotbar items", () => {
|
|
||||||
it("initially creates 12 empty cells", () => {
|
|
||||||
expect(hotbarStore.getActive().items.length).toEqual(12);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("initially adds catalog entity as first item", () => {
|
|
||||||
expect(hotbarStore.getActive().items[0]?.entity.name).toEqual("Catalog");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds items", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
|
||||||
|
|
||||||
expect(items.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("removes items", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.removeFromHotbar("some-test-id");
|
|
||||||
hotbarStore.removeFromHotbar("catalog-entity");
|
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
|
||||||
|
|
||||||
expect(items).toStrictEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does nothing if removing with invalid uid", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.removeFromHotbar("invalid uid");
|
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
|
||||||
|
|
||||||
expect(items.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("moves item to empty cell", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[6]).toBeNull();
|
|
||||||
|
|
||||||
hotbarStore.restackItems(1, 5);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[5]).toBeTruthy();
|
|
||||||
expect(hotbarStore.getActive().items[5]?.entity.uid).toEqual("some-test-id");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("moves items down", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
|
|
||||||
// aws -> catalog
|
|
||||||
hotbarStore.restackItems(3, 0);
|
|
||||||
|
|
||||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
|
||||||
|
|
||||||
expect(items.slice(0, 4)).toEqual(["some-aws-id", "catalog-entity", "some-test-id", "some-minikube-id"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("moves items up", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
|
|
||||||
// test -> aws
|
|
||||||
hotbarStore.restackItems(1, 3);
|
|
||||||
|
|
||||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
|
||||||
|
|
||||||
expect(items.slice(0, 4)).toEqual(["catalog-entity", "some-minikube-id", "some-aws-id", "some-test-id"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("logs an error if cellIndex is out of bounds", () => {
|
|
||||||
hotbarStore.add({ name: "hottest", id: "hottest" });
|
|
||||||
hotbarStore.setActiveHotbar("hottest");
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, -1);
|
|
||||||
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, 12);
|
|
||||||
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, 13);
|
|
||||||
expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an error if getId is invalid or returns not a string", () => {
|
|
||||||
expect(() => hotbarStore.addToHotbar({} as any)).toThrowError(TypeError);
|
|
||||||
expect(() => hotbarStore.addToHotbar({ getId: () => true } as any)).toThrowError(TypeError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an error if getName is invalid or returns not a string", () => {
|
|
||||||
expect(() => hotbarStore.addToHotbar({ getId: () => "" } as any)).toThrowError(TypeError);
|
|
||||||
expect(() => hotbarStore.addToHotbar({ getId: () => "", getName: () => 4 } as any)).toThrowError(TypeError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does nothing when item moved to same cell", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.restackItems(1, 1);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[1]?.entity.uid).toEqual("some-test-id");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("new items takes first empty cell", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
hotbarStore.restackItems(0, 3);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[0]?.entity.uid).toEqual("some-minikube-id");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws if invalid arguments provided", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
|
|
||||||
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
|
||||||
expect(() => hotbarStore.restackItems(2, -1)).toThrow();
|
|
||||||
expect(() => hotbarStore.restackItems(14, 1)).toThrow();
|
|
||||||
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("checks if entity already pinned to hotbar", () => {
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
|
|
||||||
expect(hotbarStore.isAddedToActive(testCluster)).toBeTruthy();
|
|
||||||
expect(hotbarStore.isAddedToActive(awsCluster)).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const writeJsonSync = di.inject(writeJsonSyncInjectable);
|
|
||||||
|
|
||||||
writeJsonSync("/some-directory-for-user-data/lens-hotbar-store.json", {
|
|
||||||
__internal__: {
|
|
||||||
migrations: {
|
|
||||||
version: "5.0.0-beta.3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hotbars: [
|
|
||||||
{
|
|
||||||
id: "3caac17f-aec2-4723-9694-ad204465d935",
|
|
||||||
name: "myhotbar",
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
entity: {
|
|
||||||
uid: "some-aws-id",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
entity: {
|
|
||||||
uid: "55b42c3c7ba3b04193416cda405269a5",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
entity: {
|
|
||||||
uid: "176fd331968660832f62283219d7eb6e",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
entity: {
|
|
||||||
uid: "61c4fb45528840ebad1badc25da41d14",
|
|
||||||
name: "user1-context",
|
|
||||||
source: "local",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
entity: {
|
|
||||||
uid: "27d6f99fe9e7548a6e306760bfe19969",
|
|
||||||
name: "foo2",
|
|
||||||
source: "local",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
entity: {
|
|
||||||
uid: "c0b20040646849bb4dcf773e43a0bf27",
|
|
||||||
name: "multinode-demo",
|
|
||||||
source: "local",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10");
|
|
||||||
|
|
||||||
hotbarStore = di.inject(hotbarStoreInjectable);
|
|
||||||
|
|
||||||
hotbarStore.load();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("allows to retrieve a hotbar", () => {
|
|
||||||
const hotbar = hotbarStore.findById("3caac17f-aec2-4723-9694-ad204465d935");
|
|
||||||
|
|
||||||
expect(hotbar?.id).toBe("3caac17f-aec2-4723-9694-ad204465d935");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("clears cells without entity", () => {
|
|
||||||
const items = hotbarStore.hotbars[0].items;
|
|
||||||
|
|
||||||
expect(items[2]).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds extra data to cells with according entity", () => {
|
|
||||||
const items = hotbarStore.hotbars[0].items;
|
|
||||||
|
|
||||||
expect(items[0]).toEqual({
|
|
||||||
entity: {
|
|
||||||
name: "my-aws-cluster",
|
|
||||||
source: "local",
|
|
||||||
uid: "some-aws-id",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -2,11 +2,8 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import type { UserStore } from "../user-store";
|
|
||||||
import userStoreInjectable from "../user-store/user-store.injectable";
|
|
||||||
import type { DiContainer } from "@ogre-tools/injectable";
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import type { ClusterStoreModel } from "../cluster-store/cluster-store";
|
|
||||||
import { defaultThemeId } from "../vars";
|
import { defaultThemeId } from "../vars";
|
||||||
import writeFileInjectable from "../fs/write-file.injectable";
|
import writeFileInjectable from "../fs/write-file.injectable";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
@ -15,9 +12,16 @@ import releaseChannelInjectable from "../vars/release-channel.injectable";
|
|||||||
import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable";
|
import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable";
|
||||||
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
||||||
import writeFileSyncInjectable from "../fs/write-file-sync.injectable";
|
import writeFileSyncInjectable from "../fs/write-file-sync.injectable";
|
||||||
|
import type { UserPreferencesState } from "../../features/user-preferences/common/state.injectable";
|
||||||
|
import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable";
|
||||||
|
import userPreferencesPersistentStorageInjectable from "../../features/user-preferences/common/storage.injectable";
|
||||||
|
import type { ResetTheme } from "../../features/user-preferences/common/reset-theme.injectable";
|
||||||
|
import resetThemeInjectable from "../../features/user-preferences/common/reset-theme.injectable";
|
||||||
|
import type { ClusterStoreModel } from "../../features/cluster/storage/common/storage.injectable";
|
||||||
|
|
||||||
describe("user store tests", () => {
|
describe("user store tests", () => {
|
||||||
let userStore: UserStore;
|
let state: UserPreferencesState;
|
||||||
|
let resetTheme: ResetTheme;
|
||||||
let di: DiContainer;
|
let di: DiContainer;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@ -33,6 +37,8 @@ describe("user store tests", () => {
|
|||||||
|
|
||||||
await di.inject(defaultUpdateChannelInjectable).init();
|
await di.inject(defaultUpdateChannelInjectable).init();
|
||||||
|
|
||||||
|
state = di.inject(userPreferencesStateInjectable);
|
||||||
|
resetTheme = di.inject(resetThemeInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("for an empty config", () => {
|
describe("for an empty config", () => {
|
||||||
@ -42,25 +48,23 @@ describe("user store tests", () => {
|
|||||||
writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {});
|
writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {});
|
||||||
writeJsonSync("/some-directory-for-user-data/kube_config", {});
|
writeJsonSync("/some-directory-for-user-data/kube_config", {});
|
||||||
|
|
||||||
userStore = di.inject(userStoreInjectable);
|
di.inject(userPreferencesPersistentStorageInjectable).loadAndStartSyncing();
|
||||||
|
|
||||||
userStore.load();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows setting and getting preferences", () => {
|
it("allows setting and getting preferences", () => {
|
||||||
userStore.httpsProxy = "abcd://defg";
|
state.httpsProxy = "abcd://defg";
|
||||||
|
|
||||||
expect(userStore.httpsProxy).toBe("abcd://defg");
|
expect(state.httpsProxy).toBe("abcd://defg");
|
||||||
expect(userStore.colorTheme).toBe(defaultThemeId);
|
expect(state.colorTheme).toBe(defaultThemeId);
|
||||||
|
|
||||||
userStore.colorTheme = "light";
|
state.colorTheme = "light";
|
||||||
expect(userStore.colorTheme).toBe("light");
|
expect(state.colorTheme).toBe("light");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("correctly resets theme to default value", async () => {
|
it("correctly resets theme to default value", async () => {
|
||||||
userStore.colorTheme = "some other theme";
|
state.colorTheme = "some other theme";
|
||||||
userStore.resetTheme();
|
resetTheme();
|
||||||
expect(userStore.colorTheme).toBe(defaultThemeId);
|
expect(state.colorTheme).toBe(defaultThemeId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -92,18 +96,16 @@ describe("user store tests", () => {
|
|||||||
|
|
||||||
di.override(storeMigrationVersionInjectable, () => "10.0.0");
|
di.override(storeMigrationVersionInjectable, () => "10.0.0");
|
||||||
|
|
||||||
userStore = di.inject(userStoreInjectable);
|
di.inject(userPreferencesPersistentStorageInjectable).loadAndStartSyncing();
|
||||||
|
|
||||||
userStore.load();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("skips clusters for adding to kube-sync with files under extension_data/", () => {
|
it("skips clusters for adding to kube-sync with files under extension_data/", () => {
|
||||||
expect(userStore.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false);
|
expect(state.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false);
|
||||||
expect(userStore.syncKubeconfigEntries.has("/some/other/path")).toBe(true);
|
expect(state.syncKubeconfigEntries.has("/some/other/path")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows access to the colorTheme preference", () => {
|
it("allows access to the colorTheme preference", () => {
|
||||||
expect(userStore.colorTheme).toBe("light");
|
expect(state.colorTheme).toBe("light");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,156 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type Config from "conf";
|
|
||||||
import type { Migrations, Options as ConfOptions } from "conf/dist/source/types";
|
|
||||||
import type { IEqualsComparer } from "mobx";
|
|
||||||
import { makeObservable, reaction } from "mobx";
|
|
||||||
import { disposer, isPromiseLike } from "@k8slens/utilities";
|
|
||||||
import { broadcastMessage } from "../ipc";
|
|
||||||
import isEqual from "lodash/isEqual";
|
|
||||||
import { kebabCase } from "lodash";
|
|
||||||
import type { GetConfigurationFileModel } from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import type { Logger } from "../logger";
|
|
||||||
import type { PersistStateToConfig } from "./save-to-file";
|
|
||||||
import type { GetBasenameOfPath } from "../path/get-basename.injectable";
|
|
||||||
import type { EnlistMessageChannelListener } from "@k8slens/messaging";
|
|
||||||
import { toJS } from "../utils";
|
|
||||||
|
|
||||||
export interface BaseStoreParams<T> extends Omit<ConfOptions<T>, "migrations"> {
|
|
||||||
syncOptions?: {
|
|
||||||
fireImmediately?: boolean;
|
|
||||||
equals?: IEqualsComparer<T>;
|
|
||||||
};
|
|
||||||
configName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IpcChannelPrefixes {
|
|
||||||
local: string;
|
|
||||||
remote: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BaseStoreDependencies {
|
|
||||||
readonly logger: Logger;
|
|
||||||
readonly storeMigrationVersion: string;
|
|
||||||
readonly directoryForUserData: string;
|
|
||||||
readonly migrations: Migrations<Record<string, unknown>>;
|
|
||||||
readonly ipcChannelPrefixes: IpcChannelPrefixes;
|
|
||||||
readonly shouldDisableSyncInListener: boolean;
|
|
||||||
getConfigurationFileModel: GetConfigurationFileModel;
|
|
||||||
persistStateToConfig: PersistStateToConfig;
|
|
||||||
getBasenameOfPath: GetBasenameOfPath;
|
|
||||||
enlistMessageChannelListener: EnlistMessageChannelListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note: T should only contain base JSON serializable types.
|
|
||||||
*/
|
|
||||||
export abstract class BaseStore<T extends object> {
|
|
||||||
private readonly syncDisposers = disposer();
|
|
||||||
|
|
||||||
readonly displayName = kebabCase(this.params.configName).toUpperCase();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ignore
|
|
||||||
*/
|
|
||||||
protected readonly dependencies: BaseStoreDependencies;
|
|
||||||
|
|
||||||
protected constructor(
|
|
||||||
dependencies: BaseStoreDependencies,
|
|
||||||
protected readonly params: BaseStoreParams<T>,
|
|
||||||
) {
|
|
||||||
this.dependencies = dependencies;
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This must be called after the last child's constructor is finished (or just before it finishes)
|
|
||||||
*/
|
|
||||||
load() {
|
|
||||||
this.dependencies.logger.info(`[${this.displayName}]: LOADING ...`);
|
|
||||||
|
|
||||||
const config = this.dependencies.getConfigurationFileModel({
|
|
||||||
projectName: "lens",
|
|
||||||
projectVersion: this.dependencies.storeMigrationVersion,
|
|
||||||
cwd: this.cwd(),
|
|
||||||
...this.params,
|
|
||||||
migrations: this.dependencies.migrations as Migrations<T>,
|
|
||||||
});
|
|
||||||
|
|
||||||
const res = this.fromStore(config.store);
|
|
||||||
|
|
||||||
if (isPromiseLike(res)) {
|
|
||||||
this.dependencies.logger.error(`${this.displayName} extends BaseStore<T>'s fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.startSyncing(config);
|
|
||||||
this.dependencies.logger.info(`[${this.displayName}]: LOADED from ${config.path}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected cwd() {
|
|
||||||
return this.dependencies.directoryForUserData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private startSyncing(config: Config<T>) {
|
|
||||||
const name = this.dependencies.getBasenameOfPath(config.path);
|
|
||||||
|
|
||||||
const disableSync = () => this.syncDisposers();
|
|
||||||
const enableSync = () => {
|
|
||||||
this.syncDisposers.push(
|
|
||||||
reaction(
|
|
||||||
() => toJS(this.toJSON()), // unwrap possible observables and react to everything
|
|
||||||
model => {
|
|
||||||
this.dependencies.persistStateToConfig(config, model);
|
|
||||||
broadcastMessage(`${this.dependencies.ipcChannelPrefixes.remote}:${config.path}`, model);
|
|
||||||
},
|
|
||||||
this.params.syncOptions,
|
|
||||||
),
|
|
||||||
this.dependencies.enlistMessageChannelListener({
|
|
||||||
id: this.displayName,
|
|
||||||
channel: {
|
|
||||||
id: `${this.dependencies.ipcChannelPrefixes.local}:${config.path}`,
|
|
||||||
},
|
|
||||||
handler: (model) => {
|
|
||||||
this.dependencies.logger.silly(`[${this.displayName}]: syncing ${name}`, { model });
|
|
||||||
|
|
||||||
if (this.dependencies.shouldDisableSyncInListener) {
|
|
||||||
disableSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: use "resourceVersion" if merge required (to avoid equality checks => better performance)
|
|
||||||
if (!isEqual(this.toJSON(), model)) {
|
|
||||||
this.fromStore(model as T);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.dependencies.shouldDisableSyncInListener) {
|
|
||||||
enableSync();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
enableSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fromStore is called internally when a child class syncs with the file
|
|
||||||
* system.
|
|
||||||
*
|
|
||||||
* Note: This function **must** be synchronous.
|
|
||||||
*
|
|
||||||
* @param data the parsed information read from the stored JSON file
|
|
||||||
*/
|
|
||||||
protected abstract fromStore(data: T): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* toJSON is called when syncing the store to the filesystem. It should
|
|
||||||
* produce a JSON serializable object representation of the current state.
|
|
||||||
*
|
|
||||||
* It is recommended that a round trip is valid. Namely, calling
|
|
||||||
* `this.fromStore(this.toJSON())` shouldn't change the state.
|
|
||||||
*/
|
|
||||||
abstract toJSON(): T;
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
|
||||||
import type { IpcChannelPrefixes } from "./base-store";
|
|
||||||
|
|
||||||
export const baseStoreIpcChannelPrefixesInjectionToken = getInjectionToken<IpcChannelPrefixes>({
|
|
||||||
id: "base-store-ipc-channel-prefix-token",
|
|
||||||
});
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import type { InjectionToken } from "@ogre-tools/injectable";
|
|
||||||
import { lifecycleEnum, getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import type Conf from "conf/dist/source";
|
|
||||||
import type { Migrations } from "conf/dist/source/types";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import { getOrInsert, iter } from "@k8slens/utilities";
|
|
||||||
|
|
||||||
export interface MigrationDeclaration {
|
|
||||||
version: string;
|
|
||||||
run(store: Conf<Partial<Record<string, unknown>>>): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const storeMigrationsInjectable = getInjectable({
|
|
||||||
id: "store-migrations",
|
|
||||||
instantiate: (di, token): Migrations<Record<string, unknown>> => {
|
|
||||||
const logger = di.inject(loggerInjectable);
|
|
||||||
const declarations = di.injectMany(token);
|
|
||||||
const migrations = new Map<string, MigrationDeclaration["run"][]>();
|
|
||||||
|
|
||||||
for (const decl of declarations) {
|
|
||||||
getOrInsert(migrations, decl.version, []).push(decl.run);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.fromEntries(
|
|
||||||
iter.map(
|
|
||||||
migrations,
|
|
||||||
([v, fns]) => [v, (store) => {
|
|
||||||
logger.info(`Running ${v} migration for ${store.path}`);
|
|
||||||
|
|
||||||
for (const fn of fns) {
|
|
||||||
fn(store);
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
lifecycle: lifecycleEnum.keyedSingleton({
|
|
||||||
getInstanceKey: (di, token: InjectionToken<MigrationDeclaration, void>) => token.id,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default storeMigrationsInjectable;
|
|
||||||
@ -4,10 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
import { getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
import removeWeblinkInjectable from "../../features/weblinks/common/remove.injectable";
|
||||||
import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
||||||
import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity";
|
import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity";
|
||||||
import productNameInjectable from "../vars/product-name.injectable";
|
import productNameInjectable from "../vars/product-name.injectable";
|
||||||
import weblinkStoreInjectable from "../weblinks-store/weblink-store.injectable";
|
|
||||||
|
|
||||||
export type WebLinkStatusPhase = "available" | "unavailable";
|
export type WebLinkStatusPhase = "available" | "unavailable";
|
||||||
|
|
||||||
@ -34,13 +34,13 @@ export class WebLink extends CatalogEntity<CatalogEntityMetadata, WebLinkStatus,
|
|||||||
// NOTE: this is safe because `onContextMenuOpen` is only supposed to be called in the renderer
|
// NOTE: this is safe because `onContextMenuOpen` is only supposed to be called in the renderer
|
||||||
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi("renderer");
|
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi("renderer");
|
||||||
const productName = di.inject(productNameInjectable);
|
const productName = di.inject(productNameInjectable);
|
||||||
const weblinkStore = di.inject(weblinkStoreInjectable);
|
const removeWeblink = di.inject(removeWeblinkInjectable);
|
||||||
|
|
||||||
if (this.metadata.source === "local") {
|
if (this.metadata.source === "local") {
|
||||||
context.menuItems.push({
|
context.menuItems.push({
|
||||||
title: "Delete",
|
title: "Delete",
|
||||||
icon: "delete",
|
icon: "delete",
|
||||||
onClick: async () => weblinkStore.removeById(this.getId()),
|
onClick: () => removeWeblink(this.getId()),
|
||||||
confirm: {
|
confirm: {
|
||||||
message: `Remove Web Link "${this.getName()}" from ${productName}?`,
|
message: `Remove Web Link "${this.getName()}" from ${productName}?`,
|
||||||
},
|
},
|
||||||
|
|||||||
83
packages/core/src/common/catalog/helpers.ts
Normal file
83
packages/core/src/common/catalog/helpers.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { CatalogEntity } from "./catalog-entity";
|
||||||
|
import GraphemeSplitter from "grapheme-splitter";
|
||||||
|
import { hasOwnProperty, hasTypedProperty, isObject, isString, iter } from "@k8slens/utilities";
|
||||||
|
|
||||||
|
function getNameParts(name: string): string[] {
|
||||||
|
const byWhitespace = name.split(/\s+/);
|
||||||
|
|
||||||
|
if (byWhitespace.length > 1) {
|
||||||
|
return byWhitespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
const byDashes = name.split(/[-_]+/);
|
||||||
|
|
||||||
|
if (byDashes.length > 1) {
|
||||||
|
return byDashes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name.split(/@+/);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function limitGraphemeLengthOf(src: string, count: number): string {
|
||||||
|
const splitter = new GraphemeSplitter();
|
||||||
|
|
||||||
|
return iter
|
||||||
|
.chain(splitter.iterateGraphemes(src))
|
||||||
|
.take(count)
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeDefaultShortName(name: string) {
|
||||||
|
if (!name || typeof name !== "string") {
|
||||||
|
return "??";
|
||||||
|
}
|
||||||
|
|
||||||
|
const [rawFirst, rawSecond, rawThird] = getNameParts(name);
|
||||||
|
const splitter = new GraphemeSplitter();
|
||||||
|
const first = splitter.iterateGraphemes(rawFirst);
|
||||||
|
const second = rawSecond ? splitter.iterateGraphemes(rawSecond): first;
|
||||||
|
const third = rawThird ? splitter.iterateGraphemes(rawThird) : iter.newEmpty<string>();
|
||||||
|
|
||||||
|
return iter.chain(iter.take(first, 1))
|
||||||
|
.concat(iter.take(second, 1))
|
||||||
|
.concat(iter.take(third, 1))
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getShortName(entity: CatalogEntity): string {
|
||||||
|
return entity.metadata.shortName || computeDefaultShortName(entity.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIconColourHash(entity: CatalogEntity): string {
|
||||||
|
return `${entity.metadata.name}-${entity.metadata.source}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIconBackground(entity: CatalogEntity): string | undefined {
|
||||||
|
if (isObject(entity.spec.icon)) {
|
||||||
|
if (hasTypedProperty(entity.spec.icon, "background", isString)) {
|
||||||
|
return entity.spec.icon.background;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasOwnProperty(entity.spec.icon, "src")
|
||||||
|
? "transparent"
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getIconMaterial(entity: CatalogEntity): string | undefined {
|
||||||
|
if (
|
||||||
|
isObject(entity.spec.icon)
|
||||||
|
&& hasTypedProperty(entity.spec.icon, "material", isString)
|
||||||
|
) {
|
||||||
|
return entity.spec.icon.material;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@ -1,40 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import { ClusterStore } from "./cluster-store";
|
|
||||||
import readClusterConfigSyncInjectable from "./read-cluster-config.injectable";
|
|
||||||
import emitAppEventInjectable from "../app-event-bus/emit-event.injectable";
|
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
|
||||||
import storeMigrationsInjectable from "../base-store/migrations.injectable";
|
|
||||||
import { clusterStoreMigrationInjectionToken } from "./migration-token";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../base-store/save-to-file";
|
|
||||||
import getBasenameOfPathInjectable from "../path/get-basename.injectable";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
|
|
||||||
const clusterStoreInjectable = getInjectable({
|
|
||||||
id: "cluster-store",
|
|
||||||
|
|
||||||
instantiate: (di) => new ClusterStore({
|
|
||||||
readClusterConfigSync: di.inject(readClusterConfigSyncInjectable),
|
|
||||||
emitAppEvent: di.inject(emitAppEventInjectable),
|
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
|
||||||
logger: di.inject(loggerInjectable),
|
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: di.inject(storeMigrationsInjectable, clusterStoreMigrationInjectionToken),
|
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default clusterStoreInjectable;
|
|
||||||
@ -1,107 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import { action, comparer, computed, makeObservable, observable } from "mobx";
|
|
||||||
import type { BaseStoreDependencies } from "../base-store/base-store";
|
|
||||||
import { BaseStore } from "../base-store/base-store";
|
|
||||||
import { Cluster } from "../cluster/cluster";
|
|
||||||
import { toJS } from "../utils";
|
|
||||||
import type { ClusterModel, ClusterId } from "../cluster-types";
|
|
||||||
import type { ReadClusterConfigSync } from "./read-cluster-config.injectable";
|
|
||||||
import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable";
|
|
||||||
|
|
||||||
export interface ClusterStoreModel {
|
|
||||||
clusters?: ClusterModel[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies extends BaseStoreDependencies {
|
|
||||||
readClusterConfigSync: ReadClusterConfigSync;
|
|
||||||
emitAppEvent: EmitAppEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|
||||||
readonly clusters = observable.map<ClusterId, Cluster>();
|
|
||||||
|
|
||||||
constructor(protected readonly dependencies: Dependencies) {
|
|
||||||
super(dependencies, {
|
|
||||||
configName: "lens-cluster-store",
|
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
|
||||||
syncOptions: {
|
|
||||||
equals: comparer.structural,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get clustersList(): Cluster[] {
|
|
||||||
return Array.from(this.clusters.values());
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get connectedClustersList(): Cluster[] {
|
|
||||||
return this.clustersList.filter((c) => !c.disconnected);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasClusters() {
|
|
||||||
return this.clusters.size > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getById(id: ClusterId | undefined): Cluster | undefined {
|
|
||||||
if (id) {
|
|
||||||
return this.clusters.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
addCluster(clusterOrModel: ClusterModel | Cluster): Cluster {
|
|
||||||
this.dependencies.emitAppEvent({ name: "cluster", action: "add" });
|
|
||||||
|
|
||||||
const cluster = clusterOrModel instanceof Cluster
|
|
||||||
? clusterOrModel
|
|
||||||
: new Cluster(
|
|
||||||
clusterOrModel,
|
|
||||||
this.dependencies.readClusterConfigSync(clusterOrModel),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.clusters.set(cluster.id, cluster);
|
|
||||||
|
|
||||||
return cluster;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore({ clusters = [] }: ClusterStoreModel = {}) {
|
|
||||||
const currentClusters = new Map(this.clusters);
|
|
||||||
const newClusters = new Map<ClusterId, Cluster>();
|
|
||||||
|
|
||||||
// update new clusters
|
|
||||||
for (const clusterModel of clusters) {
|
|
||||||
try {
|
|
||||||
let cluster = currentClusters.get(clusterModel.id);
|
|
||||||
|
|
||||||
if (cluster) {
|
|
||||||
cluster.updateModel(clusterModel);
|
|
||||||
} else {
|
|
||||||
cluster = new Cluster(
|
|
||||||
clusterModel,
|
|
||||||
this.dependencies.readClusterConfigSync(clusterModel),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
newClusters.set(clusterModel.id, cluster);
|
|
||||||
} catch (error) {
|
|
||||||
this.dependencies.logger.warn(`[CLUSTER-STORE]: Failed to update/create a cluster: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clusters.replace(newClusters);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): ClusterStoreModel {
|
|
||||||
return toJS({
|
|
||||||
clusters: this.clustersList.map(cluster => cluster.toJSON()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,7 +9,7 @@ import isProductionInjectable from "../vars/is-production.injectable";
|
|||||||
import sentryDataSourceNameInjectable from "../vars/sentry-dsn-url.injectable";
|
import sentryDataSourceNameInjectable from "../vars/sentry-dsn-url.injectable";
|
||||||
import { Dedupe, Offline } from "@sentry/integrations";
|
import { Dedupe, Offline } from "@sentry/integrations";
|
||||||
import { inspect } from "util";
|
import { inspect } from "util";
|
||||||
import userStoreInjectable from "../user-store/user-store.injectable";
|
import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable";
|
||||||
|
|
||||||
export type InitializeSentryReportingWith = (initSentry: (opts: BrowserOptions | ElectronMainOptions) => void) => void;
|
export type InitializeSentryReportingWith = (initSentry: (opts: BrowserOptions | ElectronMainOptions) => void) => void;
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ const initializeSentryReportingWithInjectable = getInjectable({
|
|||||||
instantiate: (di): InitializeSentryReportingWith => {
|
instantiate: (di): InitializeSentryReportingWith => {
|
||||||
const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable);
|
const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable);
|
||||||
const isProduction = di.inject(isProductionInjectable);
|
const isProduction = di.inject(isProductionInjectable);
|
||||||
const userStore = di.inject(userStoreInjectable);
|
const state = di.inject(userPreferencesStateInjectable);
|
||||||
|
|
||||||
if (!sentryDataSourceName) {
|
if (!sentryDataSourceName) {
|
||||||
return () => {};
|
return () => {};
|
||||||
@ -28,7 +28,7 @@ const initializeSentryReportingWithInjectable = getInjectable({
|
|||||||
|
|
||||||
return (initSentry) => initSentry({
|
return (initSentry) => initSentry({
|
||||||
beforeSend: (event) => {
|
beforeSend: (event) => {
|
||||||
if (userStore.allowErrorReporting) {
|
if (state.allowErrorReporting) {
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { HttpsProxyAgent } from "hpagent";
|
import { HttpsProxyAgent } from "hpagent";
|
||||||
import userStoreInjectable from "../user-store/user-store.injectable";
|
import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable";
|
||||||
import type { Fetch } from "./fetch.injectable";
|
import type { Fetch } from "./fetch.injectable";
|
||||||
import fetchInjectable from "./fetch.injectable";
|
import fetchInjectable from "./fetch.injectable";
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ const proxyFetchInjectable = getInjectable({
|
|||||||
id: "proxy-fetch",
|
id: "proxy-fetch",
|
||||||
instantiate: (di): Fetch => {
|
instantiate: (di): Fetch => {
|
||||||
const fetch = di.inject(fetchInjectable);
|
const fetch = di.inject(fetchInjectable);
|
||||||
const { httpsProxy, allowUntrustedCAs } = di.inject(userStoreInjectable);
|
const { httpsProxy, allowUntrustedCAs } = di.inject(userPreferencesStateInjectable);
|
||||||
const agent = httpsProxy
|
const agent = httpsProxy
|
||||||
? new HttpsProxyAgent({
|
? new HttpsProxyAgent({
|
||||||
proxy: httpsProxy,
|
proxy: httpsProxy,
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const configMapsRouteInjectable = getInjectable({
|
const configMapsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const horizontalPodAutoscalersRouteInjectable = getInjectable({
|
const horizontalPodAutoscalersRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const leasesRouteInjectable = getInjectable({
|
const leasesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const limitRangesRouteInjectable = getInjectable({
|
const limitRangesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const podDisruptionBudgetsRouteInjectable = getInjectable({
|
const podDisruptionBudgetsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const priorityClassesRouteInjectable = getInjectable({
|
const priorityClassesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const resourceQuotasRouteInjectable = getInjectable({
|
const resourceQuotasRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const runtimeClassesRouteInjectable = getInjectable({
|
const runtimeClassesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const secretsRouteInjectable = getInjectable({
|
const secretsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const verticalPodAutoscalersRouteInjectable = getInjectable({
|
const verticalPodAutoscalersRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const eventsRouteInjectable = getInjectable({
|
const eventsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const namespacesRouteInjectable = getInjectable({
|
const namespacesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const endpointsRouteInjectable = getInjectable({
|
const endpointsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
import {
|
import {
|
||||||
shouldShowResourceInjectionToken,
|
shouldShowResourceInjectionToken,
|
||||||
} from "../../../../../cluster-store/allowed-resources-injection-token";
|
} from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
|
|
||||||
const ingressClassesesRouteInjectable = getInjectable({
|
const ingressClassesesRouteInjectable = getInjectable({
|
||||||
id: "ingress-classes-route",
|
id: "ingress-classes-route",
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { computedOr } from "@k8slens/utilities";
|
import { computedOr } from "@k8slens/utilities";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const networkPoliciesRouteInjectable = getInjectable({
|
const networkPoliciesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const servicesRouteInjectable = getInjectable({
|
const servicesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const nodesRouteInjectable = getInjectable({
|
const nodesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const clusterOverviewRouteInjectable = getInjectable({
|
const clusterOverviewRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const persistentVolumeClaimsRouteInjectable = getInjectable({
|
const persistentVolumeClaimsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const persistentVolumesRouteInjectable = getInjectable({
|
const persistentVolumesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const storageClassesRouteInjectable = getInjectable({
|
const storageClassesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const clusterRoleBindingsRouteInjectable = getInjectable({
|
const clusterRoleBindingsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const clusterRolesRouteInjectable = getInjectable({
|
const clusterRolesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const podSecurityPoliciesRouteInjectable = getInjectable({
|
const podSecurityPoliciesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const roleBindingsRouteInjectable = getInjectable({
|
const roleBindingsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const rolesRouteInjectable = getInjectable({
|
const rolesRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const serviceAccountsRouteInjectable = getInjectable({
|
const serviceAccountsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const cronJobsRouteInjectable = getInjectable({
|
const cronJobsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const daemonsetsRouteInjectable = getInjectable({
|
const daemonsetsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const deploymentsRouteInjectable = getInjectable({
|
const deploymentsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const jobsRouteInjectable = getInjectable({
|
const jobsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const podsRouteInjectable = getInjectable({
|
const podsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const replicasetsRouteInjectable = getInjectable({
|
const replicasetsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const replicationControllersRouteInjectable = getInjectable({
|
const replicationControllersRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token";
|
||||||
|
|
||||||
const statefulsetsRouteInjectable = getInjectable({
|
const statefulsetsRouteInjectable = getInjectable({
|
||||||
|
|||||||
@ -6,22 +6,14 @@ import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
|
|||||||
import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token";
|
import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token";
|
||||||
import { frontEndRouteInjectionToken } from "./front-end-route-injection-token";
|
import { frontEndRouteInjectionToken } from "./front-end-route-injection-token";
|
||||||
import { filter, map } from "lodash/fp";
|
import { filter, map } from "lodash/fp";
|
||||||
import clusterStoreInjectable from "../cluster-store/cluster-store.injectable";
|
|
||||||
import type { ClusterStore } from "../cluster-store/cluster-store";
|
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
|
||||||
describe("verify-that-all-routes-have-component", () => {
|
describe("verify-that-all-routes-have-component", () => {
|
||||||
it("verify that routes have route component", () => {
|
it("verify that routes have route component", () => {
|
||||||
const rendererDi = getDiForUnitTesting();
|
const rendererDi = getDiForUnitTesting();
|
||||||
|
|
||||||
rendererDi.override(clusterStoreInjectable, () => ({
|
|
||||||
getById: () => null,
|
|
||||||
} as unknown as ClusterStore));
|
|
||||||
|
|
||||||
const routes = rendererDi.injectMany(frontEndRouteInjectionToken);
|
const routes = rendererDi.injectMany(frontEndRouteInjectionToken);
|
||||||
const routeComponents = rendererDi.injectMany(
|
const routeComponents = rendererDi.injectMany(routeSpecificComponentInjectionToken);
|
||||||
routeSpecificComponentInjectionToken,
|
|
||||||
);
|
|
||||||
|
|
||||||
const routesMissingComponent = pipeline(
|
const routesMissingComponent = pipeline(
|
||||||
routes,
|
routes,
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const fsInjectable = getInjectable({
|
|||||||
access,
|
access,
|
||||||
stat,
|
stat,
|
||||||
unlink,
|
unlink,
|
||||||
|
rename,
|
||||||
},
|
},
|
||||||
ensureDir,
|
ensureDir,
|
||||||
ensureDirSync,
|
ensureDirSync,
|
||||||
@ -58,6 +59,7 @@ const fsInjectable = getInjectable({
|
|||||||
createReadStream,
|
createReadStream,
|
||||||
stat,
|
stat,
|
||||||
unlink,
|
unlink,
|
||||||
|
rename,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import getConfigurationFileModelInjectable from "./get-configuration-file-model.
|
|||||||
import type Config from "conf";
|
import type Config from "conf";
|
||||||
import readJsonSyncInjectable from "../fs/read-json-sync.injectable";
|
import readJsonSyncInjectable from "../fs/read-json-sync.injectable";
|
||||||
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
import writeJsonSyncInjectable from "../fs/write-json-sync.injectable";
|
||||||
import { get, set } from "lodash";
|
import { get, has, set } from "lodash";
|
||||||
import semver from "semver";
|
import semver from "semver";
|
||||||
|
|
||||||
const MIGRATION_KEY = `__internal__.migrations.version`;
|
const MIGRATION_KEY = `__internal__.migrations.version`;
|
||||||
@ -64,7 +64,7 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
|
|||||||
path: configFilePath,
|
path: configFilePath,
|
||||||
get: (key: string) => get(store, key),
|
get: (key: string) => get(store, key),
|
||||||
set: (key: string, value: unknown) => {
|
set: (key: string, value: unknown) => {
|
||||||
let currentState: object;
|
let currentState: Partial<Record<string, unknown>>;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
currentState = readJsonSync(configFilePath);
|
currentState = readJsonSync(configFilePath);
|
||||||
@ -78,6 +78,25 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => {
|
|||||||
});
|
});
|
||||||
store = readJsonSync(configFilePath);
|
store = readJsonSync(configFilePath);
|
||||||
},
|
},
|
||||||
|
delete: (key: string) => {
|
||||||
|
let currentState: Partial<Record<string, unknown>>;
|
||||||
|
|
||||||
|
try {
|
||||||
|
currentState = readJsonSync(configFilePath);
|
||||||
|
} catch {
|
||||||
|
currentState = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
delete currentState[key];
|
||||||
|
|
||||||
|
writeJsonSync(configFilePath, currentState);
|
||||||
|
store = readJsonSync(configFilePath);
|
||||||
|
},
|
||||||
|
has: (key: string) => has(store, key),
|
||||||
|
clear: () => {
|
||||||
|
writeJsonSync(configFilePath, {});
|
||||||
|
store = readJsonSync(configFilePath);
|
||||||
|
},
|
||||||
} as Partial<Config> as Config<any>;
|
} as Partial<Config> as Config<any>;
|
||||||
|
|
||||||
// Migrate
|
// Migrate
|
||||||
|
|||||||
@ -1,20 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import hotbarStoreInjectable from "./store.injectable";
|
|
||||||
import type { CreateHotbarData, CreateHotbarOptions } from "./types";
|
|
||||||
|
|
||||||
export type AddHotbar = (data: CreateHotbarData, opts?: CreateHotbarOptions) => void;
|
|
||||||
|
|
||||||
const addHotbarInjectable = getInjectable({
|
|
||||||
id: "add-hotbar",
|
|
||||||
instantiate: (di): AddHotbar => {
|
|
||||||
const store = di.inject(hotbarStoreInjectable);
|
|
||||||
|
|
||||||
return (data, opts) => store.add(data, opts);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default addHotbarInjectable;
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
|
||||||
import { HotbarStore } from "./store";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
|
||||||
import storeMigrationsInjectable from "../base-store/migrations.injectable";
|
|
||||||
import { hotbarStoreMigrationInjectionToken } from "./migrations-token";
|
|
||||||
import getBasenameOfPathInjectable from "../path/get-basename.injectable";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../base-store/save-to-file";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync";
|
|
||||||
|
|
||||||
const hotbarStoreInjectable = getInjectable({
|
|
||||||
id: "hotbar-store",
|
|
||||||
|
|
||||||
instantiate: (di) => new HotbarStore({
|
|
||||||
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
|
||||||
logger: di.inject(loggerInjectable),
|
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: di.inject(storeMigrationsInjectable, hotbarStoreMigrationInjectionToken),
|
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default hotbarStoreInjectable;
|
|
||||||
@ -1,351 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { action, comparer, observable, makeObservable, computed } from "mobx";
|
|
||||||
import type { BaseStoreDependencies } from "../base-store/base-store";
|
|
||||||
import { BaseStore } from "../base-store/base-store";
|
|
||||||
import { toJS } from "../utils";
|
|
||||||
import type { CatalogEntity } from "../catalog";
|
|
||||||
import { broadcastMessage } from "../ipc";
|
|
||||||
import type { Hotbar, CreateHotbarData, CreateHotbarOptions } from "./types";
|
|
||||||
import { defaultHotbarCells, getEmptyHotbar } from "./types";
|
|
||||||
import { hotbarTooManyItemsChannel } from "../ipc/hotbar";
|
|
||||||
import type { GeneralEntity } from "../catalog-entities";
|
|
||||||
import type { Logger } from "../logger";
|
|
||||||
import assert from "assert";
|
|
||||||
|
|
||||||
export interface HotbarStoreModel {
|
|
||||||
hotbars: Hotbar[];
|
|
||||||
activeHotbarId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies extends BaseStoreDependencies {
|
|
||||||
readonly catalogCatalogEntity: GeneralEntity;
|
|
||||||
readonly logger: Logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|
||||||
@observable hotbars: Hotbar[] = [];
|
|
||||||
@observable private _activeHotbarId!: string;
|
|
||||||
|
|
||||||
constructor(protected readonly dependencies: Dependencies) {
|
|
||||||
super(dependencies, {
|
|
||||||
configName: "lens-hotbar-store",
|
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
|
||||||
syncOptions: {
|
|
||||||
equals: comparer.structural,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get activeHotbarId() {
|
|
||||||
return this._activeHotbarId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If `hotbar` points to a known hotbar, make it active. Otherwise, ignore
|
|
||||||
* @param hotbar The hotbar instance, or the index, or its ID
|
|
||||||
*/
|
|
||||||
setActiveHotbar(hotbar: Hotbar | number | string) {
|
|
||||||
if (typeof hotbar === "number") {
|
|
||||||
if (hotbar >= 0 && hotbar < this.hotbars.length) {
|
|
||||||
this._activeHotbarId = this.hotbars[hotbar].id;
|
|
||||||
}
|
|
||||||
} else if (typeof hotbar === "string") {
|
|
||||||
if (this.findById(hotbar)) {
|
|
||||||
this._activeHotbarId = hotbar;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.hotbars.indexOf(hotbar) >= 0) {
|
|
||||||
this._activeHotbarId = hotbar.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private hotbarIndexById(id: string) {
|
|
||||||
return this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private hotbarIndex(hotbar: Hotbar) {
|
|
||||||
return this.hotbars.indexOf(hotbar);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get activeHotbarIndex() {
|
|
||||||
return this.hotbarIndexById(this.activeHotbarId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore(data: Partial<HotbarStoreModel> = {}) {
|
|
||||||
if (!data.hotbars || !data.hotbars.length) {
|
|
||||||
const hotbar = getEmptyHotbar("Default");
|
|
||||||
const {
|
|
||||||
metadata: { uid, name, source },
|
|
||||||
} = this.dependencies.catalogCatalogEntity;
|
|
||||||
const initialItem = { entity: { uid, name, source }};
|
|
||||||
|
|
||||||
hotbar.items[0] = initialItem;
|
|
||||||
|
|
||||||
this.hotbars = [hotbar];
|
|
||||||
} else {
|
|
||||||
this.hotbars = data.hotbars;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotbars.forEach(ensureExactHotbarItemLength);
|
|
||||||
|
|
||||||
if (data.activeHotbarId) {
|
|
||||||
this._activeHotbarId = data.activeHotbarId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._activeHotbarId) {
|
|
||||||
this._activeHotbarId = this.hotbars[0].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): HotbarStoreModel {
|
|
||||||
return toJS({
|
|
||||||
hotbars: this.hotbars,
|
|
||||||
activeHotbarId: this.activeHotbarId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getActive(): Hotbar {
|
|
||||||
const hotbar = this.findById(this.activeHotbarId);
|
|
||||||
|
|
||||||
assert(hotbar, "There MUST always be an active hotbar");
|
|
||||||
|
|
||||||
return hotbar;
|
|
||||||
}
|
|
||||||
|
|
||||||
findByName(name: string) {
|
|
||||||
return this.hotbars.find((hotbar) => hotbar.name === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
findById(id: string) {
|
|
||||||
return this.hotbars.find((hotbar) => hotbar.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
add(data: CreateHotbarData, { setActive = false }: CreateHotbarOptions = {}) {
|
|
||||||
const hotbar = getEmptyHotbar(data.name, data.id);
|
|
||||||
|
|
||||||
this.hotbars.push(hotbar);
|
|
||||||
|
|
||||||
if (setActive) {
|
|
||||||
this._activeHotbarId = hotbar.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
setHotbarName(id: string, name: string): void {
|
|
||||||
const index = this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
return this.dependencies.logger.warn(
|
|
||||||
`[HOTBAR-STORE]: cannot setHotbarName: unknown id`,
|
|
||||||
{ id },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotbars[index].name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
remove(hotbar: Hotbar) {
|
|
||||||
assert(this.hotbars.length >= 2, "Cannot remove the last hotbar");
|
|
||||||
|
|
||||||
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
|
||||||
|
|
||||||
if (this.activeHotbarId === hotbar.id) {
|
|
||||||
this.setActiveHotbar(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
addToHotbar(item: CatalogEntity, cellIndex?: number) {
|
|
||||||
const hotbar = this.getActive();
|
|
||||||
const uid = item.getId();
|
|
||||||
const name = item.getName();
|
|
||||||
|
|
||||||
if (typeof uid !== "string") {
|
|
||||||
throw new TypeError("CatalogEntity's ID must be a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
|
||||||
throw new TypeError("CatalogEntity's NAME must be a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isAddedToActive(item)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entity = {
|
|
||||||
uid,
|
|
||||||
name,
|
|
||||||
source: item.metadata.source,
|
|
||||||
};
|
|
||||||
const newItem = { entity };
|
|
||||||
|
|
||||||
if (cellIndex === undefined) {
|
|
||||||
// Add item to empty cell
|
|
||||||
const emptyCellIndex = hotbar.items.indexOf(null);
|
|
||||||
|
|
||||||
if (emptyCellIndex != -1) {
|
|
||||||
hotbar.items[emptyCellIndex] = newItem;
|
|
||||||
} else {
|
|
||||||
broadcastMessage(hotbarTooManyItemsChannel);
|
|
||||||
}
|
|
||||||
} else if (0 <= cellIndex && cellIndex < hotbar.items.length) {
|
|
||||||
hotbar.items[cellIndex] = newItem;
|
|
||||||
} else {
|
|
||||||
this.dependencies.logger.error(
|
|
||||||
`[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`,
|
|
||||||
{ entityId: uid, hotbarId: hotbar.id, cellIndex },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeFromHotbar(uid: string): void {
|
|
||||||
const hotbar = this.getActive();
|
|
||||||
const index = hotbar.items.findIndex((item) => item?.entity.uid === uid);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbar.items[index] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all hotbar items that reference the `uid`.
|
|
||||||
* @param uid The `EntityId` that each hotbar item refers to
|
|
||||||
* @returns A function that will (in an action) undo the removing of the hotbar items. This function will not complete if the hotbar has changed.
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
removeAllHotbarItems(uid: string) {
|
|
||||||
for (const hotbar of this.hotbars) {
|
|
||||||
const index = hotbar.items.findIndex((i) => i?.entity.uid === uid);
|
|
||||||
|
|
||||||
if (index >= 0) {
|
|
||||||
hotbar.items[index] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findClosestEmptyIndex(from: number, direction = 1) {
|
|
||||||
let index = from;
|
|
||||||
const hotbar = this.getActive();
|
|
||||||
|
|
||||||
while (hotbar.items[index] != null) {
|
|
||||||
index += direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
restackItems(from: number, to: number): void {
|
|
||||||
const { items } = this.getActive();
|
|
||||||
const source = items[from];
|
|
||||||
const moveDown = from < to;
|
|
||||||
|
|
||||||
if (
|
|
||||||
from < 0 ||
|
|
||||||
to < 0 ||
|
|
||||||
from >= items.length ||
|
|
||||||
to >= items.length ||
|
|
||||||
isNaN(from) ||
|
|
||||||
isNaN(to)
|
|
||||||
) {
|
|
||||||
throw new Error("Invalid 'from' or 'to' arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from == to) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.splice(from, 1, null);
|
|
||||||
|
|
||||||
if (items[to] == null) {
|
|
||||||
items.splice(to, 1, source);
|
|
||||||
} else {
|
|
||||||
// Move cells up or down to closes empty cell
|
|
||||||
items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1);
|
|
||||||
items.splice(to, 0, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToPrevious() {
|
|
||||||
let index = this.activeHotbarIndex - 1;
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
index = this.hotbars.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setActiveHotbar(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToNext() {
|
|
||||||
let index = this.activeHotbarIndex + 1;
|
|
||||||
|
|
||||||
if (index >= this.hotbars.length) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setActiveHotbar(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if entity already pinned to the active hotbar
|
|
||||||
*/
|
|
||||||
isAddedToActive(entity: CatalogEntity | null | undefined): boolean {
|
|
||||||
if (!entity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const indexInActiveHotbar = this.getActive().items.findIndex(item => item?.entity.uid === entity.getId());
|
|
||||||
|
|
||||||
return indexInActiveHotbar >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDisplayLabel(hotbar: Hotbar): string {
|
|
||||||
return `${this.getDisplayIndex(hotbar)}: ${hotbar.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDisplayIndex(hotbar: Hotbar): string {
|
|
||||||
const index = this.hotbarIndex(hotbar);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
return "??";
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${index + 1}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function ensures that there are always exactly `defaultHotbarCells`
|
|
||||||
* worth of items in the hotbar.
|
|
||||||
* @param hotbar The hotbar to modify
|
|
||||||
*/
|
|
||||||
function ensureExactHotbarItemLength(hotbar: Hotbar) {
|
|
||||||
// if there are not enough items
|
|
||||||
while (hotbar.items.length < defaultHotbarCells) {
|
|
||||||
hotbar.items.push(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if for some reason the hotbar was overfilled before, remove as many entries
|
|
||||||
// as needed, but prefer empty slots and items at the end first.
|
|
||||||
while (hotbar.items.length > defaultHotbarCells) {
|
|
||||||
const lastNull = hotbar.items.lastIndexOf(null);
|
|
||||||
|
|
||||||
if (lastNull >= 0) {
|
|
||||||
hotbar.items.splice(lastNull, 1);
|
|
||||||
} else {
|
|
||||||
hotbar.items.length = defaultHotbarCells;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import userInfoInjectable from "../user-store/user-info.injectable";
|
import userInfoInjectable from "../vars/user-info.injectable";
|
||||||
|
|
||||||
const homeDirectoryPathInjectable = getInjectable({
|
const homeDirectoryPathInjectable = getInjectable({
|
||||||
id: "home-directory-path",
|
id: "home-directory-path",
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
|
export interface IpcChannelPrefixes {
|
||||||
|
readonly local: string;
|
||||||
|
readonly remote: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const persistentStorageIpcChannelPrefixesInjectionToken = getInjectionToken<IpcChannelPrefixes>({
|
||||||
|
id: "persistent-storage-ipc-channel-prefix-token",
|
||||||
|
});
|
||||||
158
packages/core/src/common/persistent-storage/create.injectable.ts
Normal file
158
packages/core/src/common/persistent-storage/create.injectable.ts
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { disposer, isPromiseLike } from "@k8slens/utilities";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { Options } from "conf/dist/source";
|
||||||
|
import { isEqual, kebabCase } from "lodash";
|
||||||
|
import type { IEqualsComparer } from "mobx";
|
||||||
|
import { reaction } from "mobx";
|
||||||
|
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
|
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
||||||
|
import loggerInjectable from "../logger.injectable";
|
||||||
|
import getBasenameOfPathInjectable from "../path/get-basename.injectable";
|
||||||
|
import { enlistMessageChannelListenerInjectionToken, sendMessageToChannelInjectionToken } from "@k8slens/messaging";
|
||||||
|
import type { MessageChannel } from "@k8slens/messaging";
|
||||||
|
import { persistentStorageIpcChannelPrefixesInjectionToken } from "./channel-prefix";
|
||||||
|
import { shouldPersistentStorageDisableSyncInIpcListenerInjectionToken } from "./disable-sync";
|
||||||
|
import { persistStateToConfigInjectionToken } from "./save-to-file";
|
||||||
|
import type { Migrations } from "./migrations.injectable";
|
||||||
|
import { nextTick } from "process";
|
||||||
|
|
||||||
|
export interface PersistentStorage {
|
||||||
|
/**
|
||||||
|
* This method does the initial synchronous load from disk and then starts writing the state
|
||||||
|
* back to disk whenever it changes.
|
||||||
|
*/
|
||||||
|
loadAndStartSyncing: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PersistentStorageParams<T extends object> extends Omit<Options<T>, "migrations"> {
|
||||||
|
readonly syncOptions?: {
|
||||||
|
readonly fireImmediately?: boolean;
|
||||||
|
equals?: IEqualsComparer<T>;
|
||||||
|
};
|
||||||
|
readonly configName: string;
|
||||||
|
readonly migrations?: Migrations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fromStore is called internally when a child class syncs with the file
|
||||||
|
* system.
|
||||||
|
*
|
||||||
|
* Note: This function **must** be synchronous.
|
||||||
|
*
|
||||||
|
* @param data the parsed information read from the stored JSON file
|
||||||
|
*/
|
||||||
|
fromStore(data: Partial<T>): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toJSON is called when syncing the store to the filesystem. It should
|
||||||
|
* produce a JSON serializable object representation of the current state.
|
||||||
|
*
|
||||||
|
* It is recommended that a round trip is valid. Namely, calling
|
||||||
|
* `this.fromStore(this.toJSON())` shouldn't change the state.
|
||||||
|
*/
|
||||||
|
toJSON(): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CreatePersistentStorage = <T extends object>(params: PersistentStorageParams<T>) => PersistentStorage;
|
||||||
|
|
||||||
|
const createPersistentStorageInjectable = getInjectable({
|
||||||
|
id: "create-persistent-storage",
|
||||||
|
instantiate: (di): CreatePersistentStorage => {
|
||||||
|
const directoryForUserData = di.inject(directoryForUserDataInjectable);
|
||||||
|
const getConfigurationFileModel = di.inject(getConfigurationFileModelInjectable);
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
const getBasenameOfPath = di.inject(getBasenameOfPathInjectable);
|
||||||
|
const ipcChannelPrefixes = di.inject(persistentStorageIpcChannelPrefixesInjectionToken);
|
||||||
|
const persistStateToConfig = di.inject(persistStateToConfigInjectionToken);
|
||||||
|
const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken);
|
||||||
|
const shouldDisableSyncInListener = di.inject(shouldPersistentStorageDisableSyncInIpcListenerInjectionToken);
|
||||||
|
const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken);
|
||||||
|
|
||||||
|
return <T extends object>(rawParams: PersistentStorageParams<T>) => {
|
||||||
|
const {
|
||||||
|
fromStore,
|
||||||
|
toJSON,
|
||||||
|
syncOptions,
|
||||||
|
migrations,
|
||||||
|
cwd = directoryForUserData,
|
||||||
|
...params
|
||||||
|
} = rawParams;
|
||||||
|
const displayName = kebabCase(params.configName).toUpperCase();
|
||||||
|
|
||||||
|
const loadAndStartSyncing = () => {
|
||||||
|
logger.info(`[${displayName}]: LOADING ...`);
|
||||||
|
|
||||||
|
const config = getConfigurationFileModel({
|
||||||
|
projectName: "lens",
|
||||||
|
cwd,
|
||||||
|
migrations: migrations as Options<T>["migrations"],
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = fromStore(config.store);
|
||||||
|
|
||||||
|
if (isPromiseLike(res)) {
|
||||||
|
logger.error(`${displayName} extends BaseStore<T>'s fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`[${displayName}]: LOADED from ${config.path}`);
|
||||||
|
|
||||||
|
const syncDisposers = disposer();
|
||||||
|
const sendChannel: MessageChannel<T> = {
|
||||||
|
id: `${ipcChannelPrefixes.remote}:${config.path}`,
|
||||||
|
};
|
||||||
|
const receiveChannel: MessageChannel<T> = {
|
||||||
|
id: `${ipcChannelPrefixes.local}:${config.path}`,
|
||||||
|
};
|
||||||
|
const name = getBasenameOfPath(config.path);
|
||||||
|
|
||||||
|
const disableSync = () => syncDisposers();
|
||||||
|
const enableSync = () => {
|
||||||
|
syncDisposers.push(
|
||||||
|
reaction(
|
||||||
|
() => toJSON(),
|
||||||
|
model => {
|
||||||
|
persistStateToConfig(config, model);
|
||||||
|
sendMessageToChannel(sendChannel, model);
|
||||||
|
},
|
||||||
|
syncOptions,
|
||||||
|
),
|
||||||
|
enlistMessageChannelListener({
|
||||||
|
id: "persistent-storage-sync",
|
||||||
|
channel: receiveChannel,
|
||||||
|
handler: (model) => {
|
||||||
|
logger.silly(`[${displayName}]: syncing ${name}`, { model });
|
||||||
|
|
||||||
|
if (shouldDisableSyncInListener) {
|
||||||
|
disableSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: use "resourceVersion" if merge required (to avoid equality checks => better performance)
|
||||||
|
if (!isEqual(toJSON(), model)) {
|
||||||
|
fromStore(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldDisableSyncInListener) {
|
||||||
|
nextTick(() => {
|
||||||
|
enableSync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
enableSync();
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadAndStartSyncing,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createPersistentStorageInjectable;
|
||||||
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
export const shouldBaseStoreDisableSyncInIpcListenerInjectionToken = getInjectionToken<boolean>({
|
export const shouldPersistentStorageDisableSyncInIpcListenerInjectionToken = getInjectionToken<boolean>({
|
||||||
id: "should-base-store-disable-sync-in-ipc-listener-token",
|
id: "should-persistent-storage-disable-sync-in-ipc-listener-token",
|
||||||
});
|
});
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { InjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import { lifecycleEnum, getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import loggerInjectable from "../logger.injectable";
|
||||||
|
import { getOrInsert, iter, object } from "@k8slens/utilities";
|
||||||
|
|
||||||
|
export type AllowedSetValue<T> = T extends (...args: any[]) => any
|
||||||
|
? never
|
||||||
|
: T extends undefined | symbol
|
||||||
|
? never
|
||||||
|
: T;
|
||||||
|
|
||||||
|
export interface MigrationStore {
|
||||||
|
readonly path: string;
|
||||||
|
get(key: string): unknown;
|
||||||
|
delete(key: string): void;
|
||||||
|
has(key: string): boolean;
|
||||||
|
clear(): void;
|
||||||
|
set<T>(key: string, value: AllowedSetValue<T>): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Migrations = Partial<Record<string, (store: MigrationStore) => void>>;
|
||||||
|
|
||||||
|
export interface MigrationDeclaration {
|
||||||
|
version: string;
|
||||||
|
run(store: MigrationStore): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const persistentStorageMigrationsInjectable = getInjectable({
|
||||||
|
id: "persistent-storage-migrations",
|
||||||
|
instantiate: (di, token) => {
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
const declarations = di.injectMany(token);
|
||||||
|
const migrations = new Map<string, MigrationDeclaration["run"][]>();
|
||||||
|
|
||||||
|
for (const decl of declarations) {
|
||||||
|
getOrInsert(migrations, decl.version, []).push(decl.run);
|
||||||
|
}
|
||||||
|
|
||||||
|
return iter.chain(migrations.entries())
|
||||||
|
.map(([v, fns]) => [v, (store: MigrationStore) => {
|
||||||
|
logger.info(`Running ${v} migration for ${store.path}`);
|
||||||
|
|
||||||
|
for (const fn of fns) {
|
||||||
|
fn(store);
|
||||||
|
}
|
||||||
|
}] as const)
|
||||||
|
.collect(object.fromEntries);
|
||||||
|
},
|
||||||
|
lifecycle: lifecycleEnum.keyedSingleton({
|
||||||
|
getInstanceKey: (di, token: InjectionToken<MigrationDeclaration, void>) => token.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default persistentStorageMigrationsInjectable;
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { InjectionToken } from "@ogre-tools/injectable";
|
||||||
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import * as semver from "semver";
|
||||||
|
import type { MigrationDeclaration } from "./migrations.injectable";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: not all stores can use this computed version, namely if any migration uses a range for
|
||||||
|
* the version selector.
|
||||||
|
*/
|
||||||
|
const storageMigrationVersionInjectable = getInjectable({
|
||||||
|
id: "storage-migration-version",
|
||||||
|
instantiate: (di, token) => {
|
||||||
|
const declarations = di.injectMany(token);
|
||||||
|
|
||||||
|
return declarations.reduce((version, decl) => {
|
||||||
|
if (semver.gte(decl.version, version)) {
|
||||||
|
return decl.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}, "1.0.0");
|
||||||
|
},
|
||||||
|
lifecycle: lifecycleEnum.keyedSingleton({
|
||||||
|
getInstanceKey: (di, token: InjectionToken<MigrationDeclaration, void>) => token.id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default storageMigrationVersionInjectable;
|
||||||
@ -10,13 +10,13 @@ import { isDefined, iter } from "@k8slens/utilities";
|
|||||||
import { pathToRegexp } from "path-to-regexp";
|
import { pathToRegexp } from "path-to-regexp";
|
||||||
import type Url from "url-parse";
|
import type Url from "url-parse";
|
||||||
import { RoutingError, RoutingErrorType } from "./error";
|
import { RoutingError, RoutingErrorType } from "./error";
|
||||||
import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store";
|
|
||||||
import type { ExtensionLoader } from "../../extensions/extension-loader";
|
import type { ExtensionLoader } from "../../extensions/extension-loader";
|
||||||
import type { LensExtension } from "../../extensions/lens-extension";
|
import type { LensExtension } from "../../extensions/lens-extension";
|
||||||
import type { RouteHandler, RouteParams } from "./registration";
|
import type { RouteHandler, RouteParams } from "./registration";
|
||||||
import { when } from "mobx";
|
import { when } from "mobx";
|
||||||
import { ipcRenderer } from "electron";
|
import { ipcRenderer } from "electron";
|
||||||
import type { Logger } from "../logger";
|
import type { Logger } from "../logger";
|
||||||
|
import type { IsExtensionEnabled } from "../../features/extensions/enabled/common/is-enabled.injectable";
|
||||||
|
|
||||||
// IPC channel for protocol actions. Main broadcasts the open-url events to this channel.
|
// IPC channel for protocol actions. Main broadcasts the open-url events to this channel.
|
||||||
export const ProtocolHandlerIpcPrefix = "protocol-handler";
|
export const ProtocolHandlerIpcPrefix = "protocol-handler";
|
||||||
@ -65,8 +65,8 @@ export function foldAttemptResults(mainAttempt: RouteAttempt, rendererAttempt: R
|
|||||||
|
|
||||||
export interface LensProtocolRouterDependencies {
|
export interface LensProtocolRouterDependencies {
|
||||||
readonly extensionLoader: ExtensionLoader;
|
readonly extensionLoader: ExtensionLoader;
|
||||||
readonly extensionsStore: ExtensionsStore;
|
|
||||||
readonly logger: Logger;
|
readonly logger: Logger;
|
||||||
|
isExtensionEnabled: IsExtensionEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class LensProtocolRouter {
|
export abstract class LensProtocolRouter {
|
||||||
@ -209,7 +209,7 @@ export abstract class LensProtocolRouter {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.dependencies.extensionsStore.isEnabled(extension)) {
|
if (!this.dependencies.isExtensionEnabled(extension)) {
|
||||||
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`);
|
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`);
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from "./user-store";
|
|
||||||
export type { KubeconfigSyncEntry, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers";
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
|
||||||
import type { MigrationDeclaration } from "../base-store/migrations.injectable";
|
|
||||||
|
|
||||||
export const userStoreMigrationInjectionToken = getInjectionToken<MigrationDeclaration>({
|
|
||||||
id: "user-store-migration-token",
|
|
||||||
});
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import { UserStore } from "./user-store";
|
|
||||||
import selectedUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/selected-update-channel.injectable";
|
|
||||||
import emitAppEventInjectable from "../app-event-bus/emit-event.injectable";
|
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
|
||||||
import storeMigrationsInjectable from "../base-store/migrations.injectable";
|
|
||||||
import { userStoreMigrationInjectionToken } from "./migrations-token";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../base-store/save-to-file";
|
|
||||||
import getBasenameOfPathInjectable from "../path/get-basename.injectable";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
import userStorePreferenceDescriptorsInjectable from "./preference-descriptors.injectable";
|
|
||||||
|
|
||||||
const userStoreInjectable = getInjectable({
|
|
||||||
id: "user-store",
|
|
||||||
|
|
||||||
instantiate: (di) => new UserStore({
|
|
||||||
selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable),
|
|
||||||
emitAppEvent: di.inject(emitAppEventInjectable),
|
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
|
||||||
logger: di.inject(loggerInjectable),
|
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: di.inject(storeMigrationsInjectable, userStoreMigrationInjectionToken),
|
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
|
||||||
preferenceDescriptors: di.inject(userStorePreferenceDescriptorsInjectable),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default userStoreInjectable;
|
|
||||||
@ -1,156 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { action, observable, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx";
|
|
||||||
import type { BaseStoreDependencies } from "../base-store/base-store";
|
|
||||||
import { BaseStore } from "../base-store/base-store";
|
|
||||||
import { getOrInsertSet, toggle, object } from "@k8slens/utilities";
|
|
||||||
import type { UserPreferencesModel, StoreType } from "./preferences-helpers";
|
|
||||||
import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable";
|
|
||||||
|
|
||||||
// TODO: Remove coupling with Feature
|
|
||||||
import type { SelectedUpdateChannel } from "../../features/application-update/common/selected-update-channel/selected-update-channel.injectable";
|
|
||||||
import type { ReleaseChannel } from "../../features/application-update/common/update-channels";
|
|
||||||
import type { PreferenceDescriptors } from "./preference-descriptors.injectable";
|
|
||||||
import { toJS } from "../utils";
|
|
||||||
|
|
||||||
export interface UserStoreModel {
|
|
||||||
preferences: UserPreferencesModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies extends BaseStoreDependencies {
|
|
||||||
readonly selectedUpdateChannel: SelectedUpdateChannel;
|
|
||||||
readonly preferenceDescriptors: PreferenceDescriptors;
|
|
||||||
emitAppEvent: EmitAppEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UserStore extends BaseStore<UserStoreModel> /* implements UserStoreFlatModel (when strict null is enabled) */ {
|
|
||||||
constructor(protected readonly dependencies: Dependencies) {
|
|
||||||
super(dependencies, {
|
|
||||||
configName: "lens-user-store",
|
|
||||||
});
|
|
||||||
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated No longer used
|
|
||||||
*/
|
|
||||||
@observable seenContexts = observable.set<string>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated No longer used
|
|
||||||
*/
|
|
||||||
@observable newContexts = observable.set<string>();
|
|
||||||
|
|
||||||
@observable allowErrorReporting!: StoreType<PreferenceDescriptors["allowErrorReporting"]>;
|
|
||||||
@observable allowUntrustedCAs!: StoreType<PreferenceDescriptors["allowUntrustedCAs"]>;
|
|
||||||
@observable colorTheme!: StoreType<PreferenceDescriptors["colorTheme"]>;
|
|
||||||
@observable terminalTheme!: StoreType<PreferenceDescriptors["terminalTheme"]>;
|
|
||||||
@observable localeTimezone!: StoreType<PreferenceDescriptors["localeTimezone"]>;
|
|
||||||
@observable downloadMirror!: StoreType<PreferenceDescriptors["downloadMirror"]>;
|
|
||||||
@observable httpsProxy!: StoreType<PreferenceDescriptors["httpsProxy"]>;
|
|
||||||
@observable shell!: StoreType<PreferenceDescriptors["shell"]>;
|
|
||||||
@observable downloadBinariesPath!: StoreType<PreferenceDescriptors["downloadBinariesPath"]>;
|
|
||||||
@observable kubectlBinariesPath!: StoreType<PreferenceDescriptors["kubectlBinariesPath"]>;
|
|
||||||
@observable terminalCopyOnSelect!: StoreType<PreferenceDescriptors["terminalCopyOnSelect"]>;
|
|
||||||
@observable terminalConfig!: StoreType<PreferenceDescriptors["terminalConfig"]>;
|
|
||||||
@observable extensionRegistryUrl!: StoreType<PreferenceDescriptors["extensionRegistryUrl"]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download kubectl binaries matching cluster version
|
|
||||||
*/
|
|
||||||
@observable downloadKubectlBinaries!: StoreType<PreferenceDescriptors["downloadKubectlBinaries"]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the application should open itself at login.
|
|
||||||
*/
|
|
||||||
@observable openAtLogin!: StoreType<PreferenceDescriptors["openAtLogin"]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The column IDs under each configurable table ID that have been configured
|
|
||||||
* to not be shown
|
|
||||||
*/
|
|
||||||
@observable hiddenTableColumns!: StoreType<PreferenceDescriptors["hiddenTableColumns"]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Monaco editor configs
|
|
||||||
*/
|
|
||||||
@observable editorConfiguration!: StoreType<PreferenceDescriptors["editorConfiguration"]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The set of file/folder paths to be synced
|
|
||||||
*/
|
|
||||||
@observable syncKubeconfigEntries!: StoreType<PreferenceDescriptors["syncKubeconfigEntries"]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a column (by ID) for a table (by ID) is configured to be hidden
|
|
||||||
* @param tableId The ID of the table to be checked against
|
|
||||||
* @param columnIds The list of IDs the check if one is hidden
|
|
||||||
* @returns true if at least one column under the table is set to hidden
|
|
||||||
*/
|
|
||||||
isTableColumnHidden(tableId: string, ...columnIds: (string | undefined)[]): boolean {
|
|
||||||
if (columnIds.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = this.hiddenTableColumns.get(tableId);
|
|
||||||
|
|
||||||
if (!config) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return columnIds.some(columnId => columnId && config.has(columnId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the hidden configuration of a table's column
|
|
||||||
*/
|
|
||||||
toggleTableColumnVisibility(tableId: string, columnId: string) {
|
|
||||||
toggle(getOrInsertSet(this.hiddenTableColumns, tableId), columnId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
resetTheme() {
|
|
||||||
this.colorTheme = this.dependencies.preferenceDescriptors.colorTheme.fromStore(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore({ preferences }: Partial<UserStoreModel> = {}) {
|
|
||||||
this.dependencies.logger.debug("UserStore.fromStore()", { preferences });
|
|
||||||
|
|
||||||
for (const [key, { fromStore }] of object.entries(this.dependencies.preferenceDescriptors)) {
|
|
||||||
const curVal = this[key];
|
|
||||||
const newVal = fromStore((preferences)?.[key] as never) as never;
|
|
||||||
|
|
||||||
if (isObservableArray(curVal)) {
|
|
||||||
curVal.replace(newVal);
|
|
||||||
} else if (isObservableSet(curVal) || isObservableMap(curVal)) {
|
|
||||||
curVal.replace(newVal);
|
|
||||||
} else {
|
|
||||||
this[key] = newVal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Switch to action-based saving instead saving stores by reaction
|
|
||||||
if (preferences?.updateChannel) {
|
|
||||||
this.dependencies.selectedUpdateChannel.setValue(preferences?.updateChannel as ReleaseChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): UserStoreModel {
|
|
||||||
const preferences = object.fromEntries(
|
|
||||||
object.entries(this.dependencies.preferenceDescriptors)
|
|
||||||
.map(([key, { toStore }]) => [key, toStore(this[key] as never)]),
|
|
||||||
) as UserPreferencesModel;
|
|
||||||
|
|
||||||
return toJS({
|
|
||||||
preferences: {
|
|
||||||
...preferences,
|
|
||||||
updateChannel: this.dependencies.selectedUpdateChannel.value.get().id,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync";
|
|
||||||
import storeMigrationsInjectable from "../base-store/migrations.injectable";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../base-store/save-to-file";
|
|
||||||
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import loggerInjectable from "../logger.injectable";
|
|
||||||
import getBasenameOfPathInjectable from "../path/get-basename.injectable";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable";
|
|
||||||
import { weblinkStoreMigrationInjectionToken } from "./migration-token";
|
|
||||||
import { WeblinkStore } from "./weblink-store";
|
|
||||||
|
|
||||||
const weblinkStoreInjectable = getInjectable({
|
|
||||||
id: "weblink-store",
|
|
||||||
instantiate: (di) => new WeblinkStore({
|
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
|
||||||
logger: di.inject(loggerInjectable),
|
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: di.inject(storeMigrationsInjectable, weblinkStoreMigrationInjectionToken),
|
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default weblinkStoreInjectable;
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { action, comparer, observable, makeObservable } from "mobx";
|
|
||||||
import type { BaseStoreDependencies } from "../base-store/base-store";
|
|
||||||
import { BaseStore } from "../base-store/base-store";
|
|
||||||
import * as uuid from "uuid";
|
|
||||||
import { toJS } from "../utils";
|
|
||||||
|
|
||||||
export interface WeblinkData {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WeblinkCreateOptions {
|
|
||||||
id?: string;
|
|
||||||
name: string;
|
|
||||||
url: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface WeblinkStoreModel {
|
|
||||||
weblinks: WeblinkData[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WeblinkStore extends BaseStore<WeblinkStoreModel> {
|
|
||||||
@observable weblinks: WeblinkData[] = [];
|
|
||||||
|
|
||||||
constructor(deps: BaseStoreDependencies) {
|
|
||||||
super(deps, {
|
|
||||||
configName: "lens-weblink-store",
|
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
|
||||||
syncOptions: {
|
|
||||||
equals: comparer.structural,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
makeObservable(this);
|
|
||||||
this.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore(data: Partial<WeblinkStoreModel> = {}) {
|
|
||||||
this.weblinks = data.weblinks || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
add(data: WeblinkCreateOptions) {
|
|
||||||
const {
|
|
||||||
id = uuid.v4(),
|
|
||||||
name,
|
|
||||||
url,
|
|
||||||
} = data;
|
|
||||||
const weblink: WeblinkData = { id, name, url };
|
|
||||||
|
|
||||||
this.weblinks.push(weblink);
|
|
||||||
|
|
||||||
return weblink;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeById(id: string) {
|
|
||||||
this.weblinks = this.weblinks.filter((w) => w.id !== id);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): WeblinkStoreModel {
|
|
||||||
const model: WeblinkStoreModel = {
|
|
||||||
weblinks: this.weblinks,
|
|
||||||
};
|
|
||||||
|
|
||||||
return toJS(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -5,14 +5,16 @@
|
|||||||
|
|
||||||
import type { ExtensionLoader } from "../extension-loader";
|
import type { ExtensionLoader } from "../extension-loader";
|
||||||
import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable";
|
import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable";
|
||||||
|
import type { ObservableMap } from "mobx";
|
||||||
import { runInAction } from "mobx";
|
import { runInAction } from "mobx";
|
||||||
import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable";
|
|
||||||
import { delay } from "@k8slens/utilities";
|
import { delay } from "@k8slens/utilities";
|
||||||
import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting";
|
||||||
import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable";
|
import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable";
|
||||||
import type { IpcRenderer } from "electron";
|
import type { IpcRenderer } from "electron";
|
||||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
import currentlyInClusterFrameInjectable from "../../renderer/routes/currently-in-cluster-frame.injectable";
|
import currentlyInClusterFrameInjectable from "../../renderer/routes/currently-in-cluster-frame.injectable";
|
||||||
|
import type { LensExtensionState } from "../../features/extensions/enabled/common/state.injectable";
|
||||||
|
import enabledExtensionsStateInjectable from "../../features/extensions/enabled/common/state.injectable";
|
||||||
|
|
||||||
const manifestPath = "manifest/path";
|
const manifestPath = "manifest/path";
|
||||||
const manifestPath2 = "manifest/path2";
|
const manifestPath2 = "manifest/path2";
|
||||||
@ -20,7 +22,7 @@ const manifestPath3 = "manifest/path3";
|
|||||||
|
|
||||||
describe("ExtensionLoader", () => {
|
describe("ExtensionLoader", () => {
|
||||||
let extensionLoader: ExtensionLoader;
|
let extensionLoader: ExtensionLoader;
|
||||||
let updateExtensionStateMock: jest.Mock;
|
let enabledExtensionsState: ObservableMap<string, LensExtensionState>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const di = getDiForUnitTesting();
|
const di = getDiForUnitTesting();
|
||||||
@ -106,69 +108,60 @@ describe("ExtensionLoader", () => {
|
|||||||
},
|
},
|
||||||
}) as unknown as IpcRenderer);
|
}) as unknown as IpcRenderer);
|
||||||
|
|
||||||
updateExtensionStateMock = jest.fn();
|
|
||||||
|
|
||||||
di.override(updateExtensionsStateInjectable, () => updateExtensionStateMock);
|
|
||||||
|
|
||||||
extensionLoader = di.inject(extensionLoaderInjectable);
|
extensionLoader = di.inject(extensionLoaderInjectable);
|
||||||
|
enabledExtensionsState = di.inject(enabledExtensionsStateInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renderer updates extension after ipc broadcast", async () => {
|
it("renderer updates extension after ipc broadcast", async () => {
|
||||||
expect(extensionLoader.userExtensions).toMatchInlineSnapshot(`Map {}`);
|
expect(extensionLoader.userExtensions).toEqual(new Map());
|
||||||
|
|
||||||
await extensionLoader.init();
|
await extensionLoader.init();
|
||||||
await delay(10);
|
await delay(10);
|
||||||
|
|
||||||
// Assert the extensions after the extension broadcast event
|
// Assert the extensions after the extension broadcast event
|
||||||
expect(extensionLoader.userExtensions).toMatchInlineSnapshot(`
|
expect(extensionLoader.userExtensions).toEqual(
|
||||||
Map {
|
new Map([
|
||||||
"manifest/path" => Object {
|
["manifest/path", {
|
||||||
"absolutePath": "/test/1",
|
absolutePath: "/test/1",
|
||||||
"id": "manifest/path",
|
id: "manifest/path",
|
||||||
"isBundled": false,
|
isBundled: false,
|
||||||
"isEnabled": true,
|
isEnabled: true,
|
||||||
"manifest": Object {
|
manifest: {
|
||||||
"name": "TestExtension",
|
name: "TestExtension",
|
||||||
"version": "1.0.0",
|
version: "1.0.0",
|
||||||
},
|
},
|
||||||
"manifestPath": "manifest/path",
|
manifestPath: "manifest/path",
|
||||||
|
}],
|
||||||
|
["manifest/path3", {
|
||||||
|
absolutePath: "/test/3",
|
||||||
|
id: "manifest/path3",
|
||||||
|
isBundled: false,
|
||||||
|
isEnabled: true,
|
||||||
|
manifest: {
|
||||||
|
name: "TestExtension3",
|
||||||
|
version: "3.0.0",
|
||||||
},
|
},
|
||||||
"manifest/path3" => Object {
|
manifestPath: "manifest/path3",
|
||||||
"absolutePath": "/test/3",
|
}],
|
||||||
"id": "manifest/path3",
|
]),
|
||||||
"isBundled": false,
|
);
|
||||||
"isEnabled": true,
|
|
||||||
"manifest": Object {
|
|
||||||
"name": "TestExtension3",
|
|
||||||
"version": "3.0.0",
|
|
||||||
},
|
|
||||||
"manifestPath": "manifest/path3",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates ExtensionsStore after isEnabled is changed", async () => {
|
it("updates ExtensionsStore after isEnabled is changed", async () => {
|
||||||
await extensionLoader.init();
|
await extensionLoader.init();
|
||||||
|
|
||||||
expect(updateExtensionStateMock).not.toHaveBeenCalled();
|
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
extensionLoader.setIsEnabled("manifest/path", false);
|
extensionLoader.setIsEnabled("manifest/path", false);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(updateExtensionStateMock).toHaveBeenCalledWith(
|
expect(enabledExtensionsState.size).toBe(2);
|
||||||
expect.objectContaining({
|
expect(enabledExtensionsState.get("manifest/path")).toMatchObject({
|
||||||
"manifest/path": {
|
|
||||||
enabled: false,
|
enabled: false,
|
||||||
name: "TestExtension",
|
name: "TestExtension",
|
||||||
},
|
});
|
||||||
|
expect(enabledExtensionsState.get("manifest/path2")).toMatchObject({
|
||||||
"manifest/path2": {
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
name: "TestExtension2",
|
name: "TestExtension2",
|
||||||
},
|
});
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
103
packages/core/src/extensions/base-extension-store.ts
Normal file
103
packages/core/src/extensions/base-extension-store.ts
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as path from "path";
|
||||||
|
import type { LensExtension } from "./lens-extension";
|
||||||
|
import type { StaticThis } from "../common/utils/singleton";
|
||||||
|
import { getOrInsertWith } from "@k8slens/utilities";
|
||||||
|
import { getLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
|
import type { PersistentStorage, PersistentStorageParams } from "../common/persistent-storage/create.injectable";
|
||||||
|
import createPersistentStorageInjectable from "../common/persistent-storage/create.injectable";
|
||||||
|
import directoryForUserDataInjectable from "../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
||||||
|
import assert from "assert";
|
||||||
|
import type { Options } from "conf";
|
||||||
|
import type { Migrations } from "../common/persistent-storage/migrations.injectable";
|
||||||
|
|
||||||
|
export interface ExtensionStoreParams<T extends object> extends Omit<PersistentStorageParams<T>, "migrations" | "cwd" | "fromStore" | "toJSON"> {
|
||||||
|
migrations?: Options<T>["migrations"];
|
||||||
|
cwd?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class BaseExtensionStore<T extends object> {
|
||||||
|
private static readonly instances = new WeakMap<object, any>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This is a form of global shared state. Just call `new Store(...)`
|
||||||
|
*/
|
||||||
|
static createInstance<T, R extends any[]>(this: StaticThis<T, R>, ...args: R): T {
|
||||||
|
return getOrInsertWith(BaseExtensionStore.instances, this, () => new this(...args)) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This is a form of global shared state. Just call `new Store(...)`
|
||||||
|
*/
|
||||||
|
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict?: true): T;
|
||||||
|
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict: false): T | undefined;
|
||||||
|
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict = true): T | undefined {
|
||||||
|
if (!BaseExtensionStore.instances.has(this) && strict) {
|
||||||
|
throw new TypeError(`instance of ${this.name} is not created`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseExtensionStore.instances.get(this) as (T | undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected persistentStorage?: PersistentStorage;
|
||||||
|
private readonly dependencies = (() => {
|
||||||
|
const di = getLegacyGlobalDiForExtensionApi();
|
||||||
|
|
||||||
|
return {
|
||||||
|
createPersistentStorage: di.inject(createPersistentStorageInjectable),
|
||||||
|
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
||||||
|
} as const;
|
||||||
|
})();
|
||||||
|
|
||||||
|
constructor(protected readonly rawParams: ExtensionStoreParams<T>) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This is a form of global shared state. Just call `new Store(...)`
|
||||||
|
*/
|
||||||
|
static resetInstance() {
|
||||||
|
BaseExtensionStore.instances.delete(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected extension?: LensExtension;
|
||||||
|
|
||||||
|
loadExtension(extension: LensExtension) {
|
||||||
|
this.extension = extension;
|
||||||
|
const {
|
||||||
|
projectVersion = this.extension.version,
|
||||||
|
cwd: _cwd, // This is ignored to maintain backwards compatibility
|
||||||
|
migrations = {},
|
||||||
|
...params
|
||||||
|
} = this.rawParams;
|
||||||
|
|
||||||
|
this.persistentStorage = this.dependencies.createPersistentStorage({
|
||||||
|
...params,
|
||||||
|
cwd: this.cwd(),
|
||||||
|
projectVersion,
|
||||||
|
migrations: migrations as Migrations,
|
||||||
|
fromStore: (data) => this.fromStore(data),
|
||||||
|
toJSON: () => this.toJSON(),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.persistentStorage.loadAndStartSyncing();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Never use this method. Instead call {@link BaseExtensionStore.loadExtension}
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
this.persistentStorage?.loadAndStartSyncing();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected cwd() {
|
||||||
|
assert(this.extension, "cwd can only be called in loadExtension");
|
||||||
|
|
||||||
|
return this.rawParams.cwd ?? path.join(this.dependencies.directoryForUserData, "extension-store", this.extension.storeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fromStore(data: Partial<T>): void;
|
||||||
|
abstract toJSON(): T;
|
||||||
|
}
|
||||||
@ -8,21 +8,21 @@ import isLinuxInjectable from "../../common/vars/is-linux.injectable";
|
|||||||
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
import isMacInjectable from "../../common/vars/is-mac.injectable";
|
||||||
import isSnapPackageInjectable from "../../common/vars/is-snap-package.injectable";
|
import isSnapPackageInjectable from "../../common/vars/is-snap-package.injectable";
|
||||||
import isWindowsInjectable from "../../common/vars/is-windows.injectable";
|
import isWindowsInjectable from "../../common/vars/is-windows.injectable";
|
||||||
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
|
||||||
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
||||||
import getEnabledExtensionsInjectable from "./get-enabled-extensions/get-enabled-extensions.injectable";
|
|
||||||
import { issuesTrackerUrl } from "../../common/vars";
|
import { issuesTrackerUrl } from "../../common/vars";
|
||||||
import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable";
|
import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable";
|
||||||
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||||
import userStoreInjectable from "../../common/user-store/user-store.injectable";
|
import enabledExtensionsInjectable from "../../features/extensions/enabled/common/enabled-extensions.injectable";
|
||||||
|
import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable";
|
||||||
|
|
||||||
const userStore = asLegacyGlobalForExtensionApi(userStoreInjectable);
|
const userStore = asLegacyGlobalForExtensionApi(userPreferencesStateInjectable);
|
||||||
|
const enabledExtensions = asLegacyGlobalForExtensionApi(enabledExtensionsInjectable);
|
||||||
|
|
||||||
export const App = {
|
export const App = {
|
||||||
Preferences: {
|
Preferences: {
|
||||||
getKubectlPath: () => userStore.kubectlBinariesPath,
|
getKubectlPath: () => userStore.kubectlBinariesPath,
|
||||||
},
|
},
|
||||||
getEnabledExtensions: asLegacyGlobalFunctionForExtensionApi(getEnabledExtensionsInjectable),
|
getEnabledExtensions: () => enabledExtensions.get(),
|
||||||
get version() {
|
get version() {
|
||||||
const di = getLegacyGlobalDiForExtensionApi();
|
const di = getLegacyGlobalDiForExtensionApi();
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ export const App = {
|
|||||||
return di.inject(isLinuxInjectable);
|
return di.inject(isLinuxInjectable);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @deprecated This value is now `""` and is left here for backwards compatability.
|
* @deprecated This value is now `""` and is left here for backwards compatibility.
|
||||||
*/
|
*/
|
||||||
slackUrl: "",
|
slackUrl: "",
|
||||||
issuesTrackerUrl,
|
issuesTrackerUrl,
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import extensionsStoreInjectable from "../../extensions-store/extensions-store.injectable";
|
|
||||||
|
|
||||||
const getEnabledExtensionsInjectable = getInjectable({
|
|
||||||
id: "get-enabled-extensions",
|
|
||||||
|
|
||||||
instantiate: (di) => () =>
|
|
||||||
di.inject(extensionsStoreInjectable).enabledExtensions,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default getEnabledExtensionsInjectable;
|
|
||||||
@ -3,10 +3,13 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BaseStoreParams } from "../../common/base-store/base-store";
|
import type { PersistentStorageParams } from "../../common/persistent-storage/create.injectable";
|
||||||
import { ExtensionStore } from "../extension-store";
|
import type { ExtensionStoreParams } from "../base-extension-store";
|
||||||
|
import { BaseExtensionStore as ExtensionStore } from "../base-extension-store";
|
||||||
|
|
||||||
export {
|
export type {
|
||||||
BaseStoreParams,
|
ExtensionStoreParams,
|
||||||
ExtensionStore,
|
PersistentStorageParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export { ExtensionStore };
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { ExtensionDiscovery } from "./extension-discovery";
|
import { ExtensionDiscovery } from "./extension-discovery";
|
||||||
import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable";
|
import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable";
|
||||||
import isCompatibleExtensionInjectable from "./is-compatible-extension/is-compatible-extension.injectable";
|
import isCompatibleExtensionInjectable from "./is-compatible-extension/is-compatible-extension.injectable";
|
||||||
import extensionsStoreInjectable from "../extensions-store/extensions-store.injectable";
|
|
||||||
import extensionInstallationStateStoreInjectable from "../extension-installation-state-store/extension-installation-state-store.injectable";
|
import extensionInstallationStateStoreInjectable from "../extension-installation-state-store/extension-installation-state-store.injectable";
|
||||||
import installExtensionInjectable from "../install-extension/install-extension.injectable";
|
import installExtensionInjectable from "../install-extension/install-extension.injectable";
|
||||||
import extensionPackageRootDirectoryInjectable from "../install-extension/extension-package-root-directory.injectable";
|
import extensionPackageRootDirectoryInjectable from "../install-extension/extension-package-root-directory.injectable";
|
||||||
@ -28,13 +27,14 @@ import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
|||||||
import removePathInjectable from "../../common/fs/remove.injectable";
|
import removePathInjectable from "../../common/fs/remove.injectable";
|
||||||
import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable";
|
import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable";
|
||||||
import lensResourcesDirInjectable from "../../common/vars/lens-resources-dir.injectable";
|
import lensResourcesDirInjectable from "../../common/vars/lens-resources-dir.injectable";
|
||||||
|
import isExtensionEnabledInjectable from "../../features/extensions/enabled/common/is-enabled.injectable";
|
||||||
|
|
||||||
const extensionDiscoveryInjectable = getInjectable({
|
const extensionDiscoveryInjectable = getInjectable({
|
||||||
id: "extension-discovery",
|
id: "extension-discovery",
|
||||||
|
|
||||||
instantiate: (di) => new ExtensionDiscovery({
|
instantiate: (di) => new ExtensionDiscovery({
|
||||||
extensionLoader: di.inject(extensionLoaderInjectable),
|
extensionLoader: di.inject(extensionLoaderInjectable),
|
||||||
extensionsStore: di.inject(extensionsStoreInjectable),
|
isExtensionEnabled: di.inject(isExtensionEnabledInjectable),
|
||||||
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
|
extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable),
|
||||||
isCompatibleExtension: di.inject(isCompatibleExtensionInjectable),
|
isCompatibleExtension: di.inject(isCompatibleExtensionInjectable),
|
||||||
installExtension: di.inject(installExtensionInjectable),
|
installExtension: di.inject(installExtensionInjectable),
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { makeObservable, observable, reaction, when } from "mobx";
|
|||||||
import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc";
|
import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc";
|
||||||
import { toJS } from "../../common/utils";
|
import { toJS } from "../../common/utils";
|
||||||
import { isErrnoException } from "@k8slens/utilities";
|
import { isErrnoException } from "@k8slens/utilities";
|
||||||
import type { ExtensionsStore } from "../extensions-store/extensions-store";
|
|
||||||
import type { ExtensionLoader } from "../extension-loader";
|
import type { ExtensionLoader } from "../extension-loader";
|
||||||
import type { InstalledExtension, LensExtensionId, LensExtensionManifest } from "@k8slens/legacy-extensions";
|
import type { InstalledExtension, LensExtensionId, LensExtensionManifest } from "@k8slens/legacy-extensions";
|
||||||
import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store";
|
import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store";
|
||||||
@ -31,10 +30,10 @@ import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"
|
|||||||
import type { GetRelativePath } from "../../common/path/get-relative-path.injectable";
|
import type { GetRelativePath } from "../../common/path/get-relative-path.injectable";
|
||||||
import type { RemovePath } from "../../common/fs/remove.injectable";
|
import type { RemovePath } from "../../common/fs/remove.injectable";
|
||||||
import type TypedEventEmitter from "typed-emitter";
|
import type TypedEventEmitter from "typed-emitter";
|
||||||
|
import type { IsExtensionEnabled } from "../../features/extensions/enabled/common/is-enabled.injectable";
|
||||||
|
|
||||||
interface Dependencies {
|
interface Dependencies {
|
||||||
readonly extensionLoader: ExtensionLoader;
|
readonly extensionLoader: ExtensionLoader;
|
||||||
readonly extensionsStore: ExtensionsStore;
|
|
||||||
readonly extensionInstallationStateStore: ExtensionInstallationStateStore;
|
readonly extensionInstallationStateStore: ExtensionInstallationStateStore;
|
||||||
readonly extensionPackageRootDirectory: string;
|
readonly extensionPackageRootDirectory: string;
|
||||||
readonly resourcesDirectory: string;
|
readonly resourcesDirectory: string;
|
||||||
@ -42,6 +41,7 @@ interface Dependencies {
|
|||||||
readonly isProduction: boolean;
|
readonly isProduction: boolean;
|
||||||
readonly fileSystemSeparator: string;
|
readonly fileSystemSeparator: string;
|
||||||
readonly homeDirectoryPath: string;
|
readonly homeDirectoryPath: string;
|
||||||
|
isExtensionEnabled: IsExtensionEnabled;
|
||||||
isCompatibleExtension: (manifest: LensExtensionManifest) => boolean;
|
isCompatibleExtension: (manifest: LensExtensionManifest) => boolean;
|
||||||
installExtension: (name: string) => Promise<void>;
|
installExtension: (name: string) => Promise<void>;
|
||||||
readJsonFile: ReadJson;
|
readJsonFile: ReadJson;
|
||||||
@ -334,7 +334,7 @@ export class ExtensionDiscovery {
|
|||||||
try {
|
try {
|
||||||
const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest;
|
const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest;
|
||||||
const id = isBundled ? manifestPath : this.getInstalledManifestPath(manifest.name);
|
const id = isBundled ? manifestPath : this.getInstalledManifestPath(manifest.name);
|
||||||
const isEnabled = this.dependencies.extensionsStore.isEnabled({ id, isBundled });
|
const isEnabled = this.dependencies.isExtensionEnabled({ id, isBundled });
|
||||||
const extensionDir = this.dependencies.getDirnameOfPath(manifestPath);
|
const extensionDir = this.dependencies.getDirnameOfPath(manifestPath);
|
||||||
const npmPackage = this.dependencies.joinPaths(extensionDir, `${manifest.name}-${manifest.version}.tgz`);
|
const npmPackage = this.dependencies.joinPaths(extensionDir, `${manifest.name}-${manifest.version}.tgz`);
|
||||||
const absolutePath = this.dependencies.isProduction && await this.dependencies.pathExists(npmPackage)
|
const absolutePath = this.dependencies.isProduction && await this.dependencies.pathExists(npmPackage)
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { ExtensionLoader } from "./extension-loader";
|
import { ExtensionLoader } from "./extension-loader";
|
||||||
import updateExtensionsStateInjectable from "./update-extensions-state/update-extensions-state.injectable";
|
|
||||||
import { createExtensionInstanceInjectionToken } from "./create-extension-instance.token";
|
import { createExtensionInstanceInjectionToken } from "./create-extension-instance.token";
|
||||||
import extensionInstancesInjectable from "./extension-instances.injectable";
|
import extensionInstancesInjectable from "./extension-instances.injectable";
|
||||||
import type { LensExtension } from "../lens-extension";
|
import type { LensExtension } from "../lens-extension";
|
||||||
@ -14,6 +13,7 @@ import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
|||||||
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
|
import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable";
|
||||||
import { bundledExtensionInjectionToken } from "@k8slens/legacy-extensions";
|
import { bundledExtensionInjectionToken } from "@k8slens/legacy-extensions";
|
||||||
import { extensionEntryPointNameInjectionToken } from "./entry-point-name";
|
import { extensionEntryPointNameInjectionToken } from "./entry-point-name";
|
||||||
|
import updateExtensionsStateInjectable from "../../features/extensions/enabled/common/update-state.injectable";
|
||||||
|
|
||||||
const extensionLoaderInjectable = getInjectable({
|
const extensionLoaderInjectable = getInjectable({
|
||||||
id: "extension-loader",
|
id: "extension-loader",
|
||||||
|
|||||||
@ -10,7 +10,6 @@ import { action, computed, makeObservable, toJS, observable, observe, reaction,
|
|||||||
import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc";
|
import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc";
|
||||||
import { isDefined } from "@k8slens/utilities";
|
import { isDefined } from "@k8slens/utilities";
|
||||||
import type { LensExtension } from "../lens-extension";
|
import type { LensExtension } from "../lens-extension";
|
||||||
import type { LensExtensionState } from "../extensions-store/extensions-store";
|
|
||||||
import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling";
|
import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling";
|
||||||
import { requestExtensionLoaderInitialState } from "../../renderer/ipc";
|
import { requestExtensionLoaderInitialState } from "../../renderer/ipc";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
@ -21,6 +20,7 @@ import type { Logger } from "../../common/logger";
|
|||||||
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
import type { JoinPaths } from "../../common/path/join-paths.injectable";
|
||||||
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
|
import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable";
|
||||||
import type { LensExtensionId, BundledExtension, InstalledExtension, LensExtensionConstructor } from "@k8slens/legacy-extensions";
|
import type { LensExtensionId, BundledExtension, InstalledExtension, LensExtensionConstructor } from "@k8slens/legacy-extensions";
|
||||||
|
import type { UpdateExtensionsState } from "../../features/extensions/enabled/common/update-state.injectable";
|
||||||
|
|
||||||
const logModule = "[EXTENSIONS-LOADER]";
|
const logModule = "[EXTENSIONS-LOADER]";
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ interface Dependencies {
|
|||||||
readonly bundledExtensions: BundledExtension[];
|
readonly bundledExtensions: BundledExtension[];
|
||||||
readonly logger: Logger;
|
readonly logger: Logger;
|
||||||
readonly extensionEntryPointName: "main" | "renderer";
|
readonly extensionEntryPointName: "main" | "renderer";
|
||||||
updateExtensionsState: (extensionsState: Record<LensExtensionId, LensExtensionState>) => void;
|
updateExtensionsState: UpdateExtensionsState;
|
||||||
createExtensionInstance: CreateExtensionInstance;
|
createExtensionInstance: CreateExtensionInstance;
|
||||||
getExtension: (instance: LensExtension) => Extension;
|
getExtension: (instance: LensExtension) => Extension;
|
||||||
joinPaths: JoinPaths;
|
joinPaths: JoinPaths;
|
||||||
@ -125,13 +125,11 @@ export class ExtensionLoader {
|
|||||||
|
|
||||||
// Transform userExtensions to a state object for storing into ExtensionsStore
|
// Transform userExtensions to a state object for storing into ExtensionsStore
|
||||||
@computed get storeState() {
|
@computed get storeState() {
|
||||||
return Object.fromEntries(
|
return Array.from(this.userExtensions)
|
||||||
Array.from(this.userExtensions)
|
|
||||||
.map(([extId, extension]) => [extId, {
|
.map(([extId, extension]) => [extId, {
|
||||||
enabled: extension.isEnabled,
|
enabled: extension.isEnabled,
|
||||||
name: extension.manifest.name,
|
name: extension.manifest.name,
|
||||||
}]),
|
}] as const);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { MigrationDeclaration } from "../../../common/base-store/migrations.injectable";
|
import type { MigrationDeclaration } from "../../../common/persistent-storage/migrations.injectable";
|
||||||
|
|
||||||
export const fileSystemProvisionerStoreInjectionToken = getInjectionToken<MigrationDeclaration>({
|
export const fileSystemProvisionerStoreMigrationDeclarationInjectionToken = getInjectionToken<MigrationDeclaration>({
|
||||||
id: "file-system-provisioner-store-injection-token",
|
id: "file-system-provisioner-store-migration-declaration",
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,36 +3,36 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { FileSystemProvisionerStore } from "./file-system-provisioner-store";
|
|
||||||
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import loggerInjectable from "../../../common/logger.injectable";
|
|
||||||
import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../../../common/base-store/channel-prefix";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../../common/base-store/disable-sync";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../../../common/base-store/save-to-file";
|
|
||||||
import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
import ensureHashedDirectoryForExtensionInjectable from "./ensure-hashed-directory-for-extension.injectable";
|
|
||||||
import { registeredExtensionsInjectable } from "./registered-extensions.injectable";
|
import { registeredExtensionsInjectable } from "./registered-extensions.injectable";
|
||||||
|
import createPersistentStorageInjectable from "../../../common/persistent-storage/create.injectable";
|
||||||
|
import { action } from "mobx";
|
||||||
|
import { object } from "@k8slens/utilities";
|
||||||
|
import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable";
|
||||||
|
|
||||||
const fileSystemProvisionerStoreInjectable = getInjectable({
|
const fileSystemProvisionerStoreInjectable = getInjectable({
|
||||||
id: "file-system-provisioner-store",
|
id: "file-system-provisioner-store",
|
||||||
|
|
||||||
instantiate: (di) => new FileSystemProvisionerStore({
|
instantiate: (di) => {
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
const registeredExtensions = di.inject(registeredExtensionsInjectable);
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
const createPersistentStorage = di.inject(createPersistentStorageInjectable);
|
||||||
logger: di.inject(loggerInjectable),
|
const storeMigrationVersion = di.inject(storeMigrationVersionInjectable);
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: {},
|
const store = createPersistentStorage({
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
configName: "lens-filesystem-provisioner-store",
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
projectVersion: storeMigrationVersion,
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
fromStore: action(({ extensions = {}}) => {
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
registeredExtensions.replace(object.entries(extensions));
|
||||||
ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable),
|
}),
|
||||||
registeredExtensions: di.inject(registeredExtensionsInjectable),
|
toJSON: () => ({
|
||||||
|
extensions: Object.fromEntries(registeredExtensions),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
load: () => store.loadAndStartSyncing(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export default fileSystemProvisionerStoreInjectable;
|
export default fileSystemProvisionerStoreInjectable;
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { LensExtensionId } from "@k8slens/legacy-extensions";
|
|
||||||
import type { ObservableMap } from "mobx";
|
|
||||||
import { action, makeObservable } from "mobx";
|
|
||||||
import type { BaseStoreDependencies } from "../../../common/base-store/base-store";
|
|
||||||
import { BaseStore } from "../../../common/base-store/base-store";
|
|
||||||
import type { EnsureHashedDirectoryForExtension } from "./ensure-hashed-directory-for-extension.injectable";
|
|
||||||
|
|
||||||
interface FSProvisionModel {
|
|
||||||
extensions: Record<string, string>; // extension names to paths
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies extends BaseStoreDependencies {
|
|
||||||
ensureHashedDirectoryForExtension: EnsureHashedDirectoryForExtension;
|
|
||||||
registeredExtensions: ObservableMap<LensExtensionId, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FileSystemProvisionerStore extends BaseStore<FSProvisionModel> {
|
|
||||||
constructor(protected readonly dependencies: Dependencies) {
|
|
||||||
super(dependencies, {
|
|
||||||
configName: "lens-filesystem-provisioner-store",
|
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
|
||||||
});
|
|
||||||
|
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function retrieves the saved path to the folder which the extension
|
|
||||||
* can saves files to. If the folder is not present then it is created.
|
|
||||||
* @param extensionName the name of the extension requesting the path
|
|
||||||
* @returns path to the folder that the extension can safely write files to.
|
|
||||||
*/
|
|
||||||
async requestDirectory(extensionName: string): Promise<string> {
|
|
||||||
return this.dependencies.ensureHashedDirectoryForExtension(extensionName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore({ extensions }: FSProvisionModel = { extensions: {}}): void {
|
|
||||||
this.dependencies.registeredExtensions.merge(extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): FSProvisionModel {
|
|
||||||
return {
|
|
||||||
extensions: Object.fromEntries(this.dependencies.registeredExtensions.toJSON()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import extensionsStoreInjectable from "../../extensions-store/extensions-store.injectable";
|
|
||||||
|
|
||||||
const updateExtensionsStateInjectable = getInjectable({
|
|
||||||
id: "update-extensions-state",
|
|
||||||
instantiate: (di) => di.inject(extensionsStoreInjectable).mergeState,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default updateExtensionsStateInjectable;
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { BaseStoreParams } from "../common/base-store/base-store";
|
|
||||||
import { BaseStore } from "../common/base-store/base-store";
|
|
||||||
import * as path from "path";
|
|
||||||
import type { LensExtension } from "./lens-extension";
|
|
||||||
import assert from "assert";
|
|
||||||
import type { StaticThis } from "../common/utils/singleton";
|
|
||||||
import { getOrInsertWith } from "@k8slens/utilities";
|
|
||||||
import { getLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
|
|
||||||
import directoryForUserDataInjectable from "../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import getConfigurationFileModelInjectable from "../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import loggerInjectable from "../common/logger.injectable";
|
|
||||||
import storeMigrationVersionInjectable from "../common/vars/store-migration-version.injectable";
|
|
||||||
import type { Migrations } from "conf/dist/source/types";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../common/base-store/channel-prefix";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../common/base-store/disable-sync";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../common/base-store/save-to-file";
|
|
||||||
import getBasenameOfPathInjectable from "../common/path/get-basename.injectable";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
|
|
||||||
export interface ExtensionStoreParams<T extends object> extends BaseStoreParams<T> {
|
|
||||||
migrations?: Migrations<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export abstract class ExtensionStore<T extends object> extends BaseStore<T> {
|
|
||||||
private static readonly instances = new WeakMap<object, any>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This is a form of global shared state. Just call `new Store(...)`
|
|
||||||
*/
|
|
||||||
static createInstance<T, R extends any[]>(this: StaticThis<T, R>, ...args: R): T {
|
|
||||||
return getOrInsertWith(ExtensionStore.instances, this, () => new this(...args)) as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This is a form of global shared state. Just call `new Store(...)`
|
|
||||||
*/
|
|
||||||
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict?: true): T;
|
|
||||||
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict: false): T | undefined;
|
|
||||||
static getInstance<T, R extends any[]>(this: StaticThis<T, R>, strict = true): T | undefined {
|
|
||||||
if (!ExtensionStore.instances.has(this) && strict) {
|
|
||||||
throw new TypeError(`instance of ${this.name} is not created`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExtensionStore.instances.get(this) as (T | undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor({ migrations, ...params }: ExtensionStoreParams<T>) {
|
|
||||||
const di = getLegacyGlobalDiForExtensionApi();
|
|
||||||
|
|
||||||
super({
|
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
|
||||||
logger: di.inject(loggerInjectable),
|
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: migrations as Migrations<Record<string, unknown>>,
|
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
|
||||||
}, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This is a form of global shared state. Just call `new Store(...)`
|
|
||||||
*/
|
|
||||||
static resetInstance() {
|
|
||||||
ExtensionStore.instances.delete(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected extension?: LensExtension;
|
|
||||||
|
|
||||||
loadExtension(extension: LensExtension) {
|
|
||||||
this.extension = extension;
|
|
||||||
|
|
||||||
this.params.projectVersion ??= this.extension.version;
|
|
||||||
|
|
||||||
return super.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
load() {
|
|
||||||
if (!this.extension) { return; }
|
|
||||||
|
|
||||||
return super.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected cwd() {
|
|
||||||
assert(this.extension, "must call this.load() first");
|
|
||||||
|
|
||||||
return path.join(super.cwd(), "extension-store", this.extension.storeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
|
||||||
import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import { baseStoreIpcChannelPrefixesInjectionToken } from "../../common/base-store/channel-prefix";
|
|
||||||
import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../common/base-store/disable-sync";
|
|
||||||
import { persistStateToConfigInjectionToken } from "../../common/base-store/save-to-file";
|
|
||||||
import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable";
|
|
||||||
import loggerInjectable from "../../common/logger.injectable";
|
|
||||||
import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable";
|
|
||||||
import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging";
|
|
||||||
import storeMigrationVersionInjectable from "../../common/vars/store-migration-version.injectable";
|
|
||||||
import { ExtensionsStore } from "./extensions-store";
|
|
||||||
|
|
||||||
const extensionsStoreInjectable = getInjectable({
|
|
||||||
id: "extensions-store",
|
|
||||||
instantiate: (di) => new ExtensionsStore({
|
|
||||||
directoryForUserData: di.inject(directoryForUserDataInjectable),
|
|
||||||
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
|
|
||||||
logger: di.inject(loggerInjectable),
|
|
||||||
storeMigrationVersion: di.inject(storeMigrationVersionInjectable),
|
|
||||||
migrations: {},
|
|
||||||
getBasenameOfPath: di.inject(getBasenameOfPathInjectable),
|
|
||||||
ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken),
|
|
||||||
persistStateToConfig: di.inject(persistStateToConfigInjectionToken),
|
|
||||||
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
|
|
||||||
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default extensionsStoreInjectable;
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { LensExtensionId } from "@k8slens/legacy-extensions";
|
|
||||||
import { action, computed, makeObservable, observable } from "mobx";
|
|
||||||
import type { BaseStoreDependencies } from "../../common/base-store/base-store";
|
|
||||||
import { BaseStore } from "../../common/base-store/base-store";
|
|
||||||
|
|
||||||
export interface LensExtensionsStoreModel {
|
|
||||||
extensions: Record<LensExtensionId, LensExtensionState>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface LensExtensionState {
|
|
||||||
enabled?: boolean;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IsEnabledExtensionDescriptor {
|
|
||||||
id: string;
|
|
||||||
isBundled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ExtensionsStore extends BaseStore<LensExtensionsStoreModel> {
|
|
||||||
constructor(deps: BaseStoreDependencies) {
|
|
||||||
super(deps, {
|
|
||||||
configName: "lens-extensions",
|
|
||||||
});
|
|
||||||
makeObservable(this);
|
|
||||||
this.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed
|
|
||||||
get enabledExtensions() {
|
|
||||||
return Array.from(this.state.values())
|
|
||||||
.filter(({ enabled }) => enabled)
|
|
||||||
.map(({ name }) => name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected state = observable.map<LensExtensionId, LensExtensionState>();
|
|
||||||
|
|
||||||
isEnabled({ id, isBundled }: IsEnabledExtensionDescriptor): boolean {
|
|
||||||
// By default false, so that copied extensions are disabled by default.
|
|
||||||
// If user installs the extension from the UI, the Extensions component will specifically enable it.
|
|
||||||
return isBundled || Boolean(this.state.get(id)?.enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
mergeState = action((extensionsState: Record<LensExtensionId, LensExtensionState> | [LensExtensionId, LensExtensionState][]) => {
|
|
||||||
this.state.merge(extensionsState);
|
|
||||||
});
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore({ extensions }: LensExtensionsStoreModel) {
|
|
||||||
this.state.merge(extensions);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): LensExtensionsStoreModel {
|
|
||||||
return {
|
|
||||||
extensions: Object.fromEntries(this.state.toJSON()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,13 +10,13 @@ import type { Route } from "../common/front-end-routing/front-end-route-injectio
|
|||||||
import type { CatalogEntityRegistry as MainCatalogEntityRegistry } from "../main/catalog";
|
import type { CatalogEntityRegistry as MainCatalogEntityRegistry } from "../main/catalog";
|
||||||
import type { CatalogEntityRegistry as RendererCatalogEntityRegistry } from "../renderer/api/catalog/entity/registry";
|
import type { CatalogEntityRegistry as RendererCatalogEntityRegistry } from "../renderer/api/catalog/entity/registry";
|
||||||
import type { GetExtensionPageParameters } from "../renderer/routes/get-extension-page-parameters.injectable";
|
import type { GetExtensionPageParameters } from "../renderer/routes/get-extension-page-parameters.injectable";
|
||||||
import type { FileSystemProvisionerStore } from "./extension-loader/file-system-provisioner-store/file-system-provisioner-store";
|
|
||||||
import type { NavigateForExtension } from "../main/start-main-application/lens-window/navigate-for-extension.injectable";
|
import type { NavigateForExtension } from "../main/start-main-application/lens-window/navigate-for-extension.injectable";
|
||||||
import type { Logger } from "../common/logger";
|
import type { Logger } from "../common/logger";
|
||||||
|
import type { EnsureHashedDirectoryForExtension } from "./extension-loader/file-system-provisioner-store/ensure-hashed-directory-for-extension.injectable";
|
||||||
|
|
||||||
export interface LensExtensionDependencies {
|
export interface LensExtensionDependencies {
|
||||||
readonly fileSystemProvisionerStore: FileSystemProvisionerStore;
|
|
||||||
readonly logger: Logger;
|
readonly logger: Logger;
|
||||||
|
ensureHashedDirectoryForExtension: EnsureHashedDirectoryForExtension;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LensMainExtensionDependencies extends LensExtensionDependencies {
|
export interface LensMainExtensionDependencies extends LensExtensionDependencies {
|
||||||
|
|||||||
@ -83,7 +83,7 @@ export class LensExtension<
|
|||||||
*/
|
*/
|
||||||
async getExtensionFileFolder(): Promise<string> {
|
async getExtensionFileFolder(): Promise<string> {
|
||||||
// storeName is read from the manifest and has a fallback to the manifest name, which equals id
|
// storeName is read from the manifest and has a fallback to the manifest name, which equals id
|
||||||
return this[lensExtensionDependencies].fileSystemProvisionerStore.requestDirectory(this.storeName);
|
return this[lensExtensionDependencies].ensureHashedDirectoryForExtension(this.storeName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
|||||||
@ -37,7 +37,7 @@ import namespaceApiInjectable from "../../common/k8s-api/endpoints/namespace.api
|
|||||||
import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable";
|
import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable";
|
||||||
import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable";
|
import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable";
|
||||||
import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
|
import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
|
||||||
import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token";
|
import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
|
||||||
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||||
import requestMetricsInjectable from "../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable";
|
import requestMetricsInjectable from "../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable";
|
||||||
|
|
||||||
|
|||||||
@ -230,7 +230,7 @@ exports[`extension special characters in page registrations renders 1`] = `
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -434,7 +434,7 @@ exports[`extension special characters in page registrations when navigating to r
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@ -230,7 +230,7 @@ exports[`navigate to extension page renders 1`] = `
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -434,7 +434,7 @@ exports[`navigate to extension page when extension navigates to child route rend
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -654,7 +654,7 @@ exports[`navigate to extension page when extension navigates to route with param
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -874,7 +874,7 @@ exports[`navigate to extension page when extension navigates to route without pa
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1094,7 +1094,7 @@ exports[`navigate to extension page when extension navigates to route without pa
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@ -154,7 +154,7 @@ exports[`navigating between routes given route with optional path parameters whe
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -358,7 +358,7 @@ exports[`navigating between routes given route without path parameters when navi
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@ -230,7 +230,7 @@ exports[`add-cluster - navigation using application menu renders 1`] = `
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -518,7 +518,7 @@ exports[`add-cluster - navigation using application menu when navigating to add
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
@ -1,132 +1,132 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`application-menu, given platform is 'darwin' given enough time passes populates application menu 1`] = `
|
exports[`application-menu, given platform is 'darwin' given enough time passes populates application menu 1`] = `
|
||||||
Array [
|
"[
|
||||||
"root",
|
'root',
|
||||||
"root -> mac",
|
'root -> mac',
|
||||||
"root -> mac -> about",
|
'root -> mac -> about',
|
||||||
"root -> mac -> separator-1",
|
'root -> mac -> separator-1',
|
||||||
"root -> mac -> navigate-to-preferences",
|
'root -> mac -> navigate-to-preferences',
|
||||||
"root -> mac -> navigate-to-extensions",
|
'root -> mac -> navigate-to-extensions',
|
||||||
"root -> mac -> separator-2",
|
'root -> mac -> separator-2',
|
||||||
"root -> mac -> services",
|
'root -> mac -> services',
|
||||||
"root -> mac -> separator-3",
|
'root -> mac -> separator-3',
|
||||||
"root -> mac -> hide",
|
'root -> mac -> hide',
|
||||||
"root -> mac -> hide-others",
|
'root -> mac -> hide-others',
|
||||||
"root -> mac -> unhide",
|
'root -> mac -> unhide',
|
||||||
"root -> mac -> separator-4",
|
'root -> mac -> separator-4',
|
||||||
"root -> mac -> quit",
|
'root -> mac -> quit',
|
||||||
"root -> file",
|
'root -> file',
|
||||||
"root -> file -> add-cluster",
|
'root -> file -> add-cluster',
|
||||||
"root -> file -> separator-1-for-file",
|
'root -> file -> separator-1-for-file',
|
||||||
"root -> file -> close-window",
|
'root -> file -> close-window',
|
||||||
"root -> edit",
|
'root -> edit',
|
||||||
"root -> edit -> undo",
|
'root -> edit -> undo',
|
||||||
"root -> edit -> redo",
|
'root -> edit -> redo',
|
||||||
"root -> edit -> separator-1-in-edit",
|
'root -> edit -> separator-1-in-edit',
|
||||||
"root -> edit -> cut",
|
'root -> edit -> cut',
|
||||||
"root -> edit -> copy",
|
'root -> edit -> copy',
|
||||||
"root -> edit -> paste",
|
'root -> edit -> paste',
|
||||||
"root -> edit -> delete",
|
'root -> edit -> delete',
|
||||||
"root -> edit -> separator-2-in-edit",
|
'root -> edit -> separator-2-in-edit',
|
||||||
"root -> edit -> selectAll",
|
'root -> edit -> selectAll',
|
||||||
"root -> view",
|
'root -> view',
|
||||||
"root -> view -> navigate-to-catalog",
|
'root -> view -> navigate-to-catalog',
|
||||||
"root -> view -> open-command-palette",
|
'root -> view -> open-command-palette',
|
||||||
"root -> view -> separator-1-for-view",
|
'root -> view -> separator-1-for-view',
|
||||||
"root -> view -> go-back",
|
'root -> view -> go-back',
|
||||||
"root -> view -> go-forward",
|
'root -> view -> go-forward',
|
||||||
"root -> view -> reload",
|
'root -> view -> reload',
|
||||||
"root -> view -> toggle-dev-tools",
|
'root -> view -> toggle-dev-tools',
|
||||||
"root -> view -> separator-2-for-view",
|
'root -> view -> separator-2-for-view',
|
||||||
"root -> view -> reset-zoom",
|
'root -> view -> reset-zoom',
|
||||||
"root -> view -> zoom-in",
|
'root -> view -> zoom-in',
|
||||||
"root -> view -> zoom-out",
|
'root -> view -> zoom-out',
|
||||||
"root -> view -> separator-3-for-view",
|
'root -> view -> separator-3-for-view',
|
||||||
"root -> view -> toggle-full-screen",
|
'root -> view -> toggle-full-screen',
|
||||||
"root -> help",
|
'root -> help',
|
||||||
"root -> help -> navigate-to-welcome",
|
'root -> help -> navigate-to-welcome',
|
||||||
"root -> help -> open-documentation",
|
'root -> help -> open-documentation',
|
||||||
"root -> help -> open-support",
|
'root -> help -> open-support'
|
||||||
]
|
]"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`application-menu, given platform is 'linux' given enough time passes populates application menu 1`] = `
|
exports[`application-menu, given platform is 'linux' given enough time passes populates application menu 1`] = `
|
||||||
Array [
|
"[
|
||||||
"root",
|
'root',
|
||||||
"root -> file",
|
'root -> file',
|
||||||
"root -> file -> add-cluster",
|
'root -> file -> add-cluster',
|
||||||
"root -> file -> navigate-to-preferences",
|
'root -> file -> navigate-to-preferences',
|
||||||
"root -> file -> navigate-to-extensions",
|
'root -> file -> navigate-to-extensions',
|
||||||
"root -> file -> quit",
|
'root -> file -> quit',
|
||||||
"root -> edit",
|
'root -> edit',
|
||||||
"root -> edit -> undo",
|
'root -> edit -> undo',
|
||||||
"root -> edit -> redo",
|
'root -> edit -> redo',
|
||||||
"root -> edit -> separator-1-in-edit",
|
'root -> edit -> separator-1-in-edit',
|
||||||
"root -> edit -> cut",
|
'root -> edit -> cut',
|
||||||
"root -> edit -> copy",
|
'root -> edit -> copy',
|
||||||
"root -> edit -> paste",
|
'root -> edit -> paste',
|
||||||
"root -> edit -> delete",
|
'root -> edit -> delete',
|
||||||
"root -> edit -> separator-2-in-edit",
|
'root -> edit -> separator-2-in-edit',
|
||||||
"root -> edit -> selectAll",
|
'root -> edit -> selectAll',
|
||||||
"root -> view",
|
'root -> view',
|
||||||
"root -> view -> navigate-to-catalog",
|
'root -> view -> navigate-to-catalog',
|
||||||
"root -> view -> open-command-palette",
|
'root -> view -> open-command-palette',
|
||||||
"root -> view -> separator-1-for-view",
|
'root -> view -> separator-1-for-view',
|
||||||
"root -> view -> go-back",
|
'root -> view -> go-back',
|
||||||
"root -> view -> go-forward",
|
'root -> view -> go-forward',
|
||||||
"root -> view -> reload",
|
'root -> view -> reload',
|
||||||
"root -> view -> toggle-dev-tools",
|
'root -> view -> toggle-dev-tools',
|
||||||
"root -> view -> separator-2-for-view",
|
'root -> view -> separator-2-for-view',
|
||||||
"root -> view -> reset-zoom",
|
'root -> view -> reset-zoom',
|
||||||
"root -> view -> zoom-in",
|
'root -> view -> zoom-in',
|
||||||
"root -> view -> zoom-out",
|
'root -> view -> zoom-out',
|
||||||
"root -> view -> separator-3-for-view",
|
'root -> view -> separator-3-for-view',
|
||||||
"root -> view -> toggle-full-screen",
|
'root -> view -> toggle-full-screen',
|
||||||
"root -> help",
|
'root -> help',
|
||||||
"root -> help -> navigate-to-welcome",
|
'root -> help -> navigate-to-welcome',
|
||||||
"root -> help -> open-documentation",
|
'root -> help -> open-documentation',
|
||||||
"root -> help -> open-support",
|
'root -> help -> open-support',
|
||||||
"root -> help -> about",
|
'root -> help -> about'
|
||||||
]
|
]"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`application-menu, given platform is 'win32' given enough time passes populates application menu 1`] = `
|
exports[`application-menu, given platform is 'win32' given enough time passes populates application menu 1`] = `
|
||||||
Array [
|
"[
|
||||||
"root",
|
'root',
|
||||||
"root -> file",
|
'root -> file',
|
||||||
"root -> file -> add-cluster",
|
'root -> file -> add-cluster',
|
||||||
"root -> file -> navigate-to-preferences",
|
'root -> file -> navigate-to-preferences',
|
||||||
"root -> file -> navigate-to-extensions",
|
'root -> file -> navigate-to-extensions',
|
||||||
"root -> file -> quit",
|
'root -> file -> quit',
|
||||||
"root -> edit",
|
'root -> edit',
|
||||||
"root -> edit -> undo",
|
'root -> edit -> undo',
|
||||||
"root -> edit -> redo",
|
'root -> edit -> redo',
|
||||||
"root -> edit -> separator-1-in-edit",
|
'root -> edit -> separator-1-in-edit',
|
||||||
"root -> edit -> cut",
|
'root -> edit -> cut',
|
||||||
"root -> edit -> copy",
|
'root -> edit -> copy',
|
||||||
"root -> edit -> paste",
|
'root -> edit -> paste',
|
||||||
"root -> edit -> delete",
|
'root -> edit -> delete',
|
||||||
"root -> edit -> separator-2-in-edit",
|
'root -> edit -> separator-2-in-edit',
|
||||||
"root -> edit -> selectAll",
|
'root -> edit -> selectAll',
|
||||||
"root -> view",
|
'root -> view',
|
||||||
"root -> view -> navigate-to-catalog",
|
'root -> view -> navigate-to-catalog',
|
||||||
"root -> view -> open-command-palette",
|
'root -> view -> open-command-palette',
|
||||||
"root -> view -> separator-1-for-view",
|
'root -> view -> separator-1-for-view',
|
||||||
"root -> view -> go-back",
|
'root -> view -> go-back',
|
||||||
"root -> view -> go-forward",
|
'root -> view -> go-forward',
|
||||||
"root -> view -> reload",
|
'root -> view -> reload',
|
||||||
"root -> view -> toggle-dev-tools",
|
'root -> view -> toggle-dev-tools',
|
||||||
"root -> view -> separator-2-for-view",
|
'root -> view -> separator-2-for-view',
|
||||||
"root -> view -> reset-zoom",
|
'root -> view -> reset-zoom',
|
||||||
"root -> view -> zoom-in",
|
'root -> view -> zoom-in',
|
||||||
"root -> view -> zoom-out",
|
'root -> view -> zoom-out',
|
||||||
"root -> view -> separator-3-for-view",
|
'root -> view -> separator-3-for-view',
|
||||||
"root -> view -> toggle-full-screen",
|
'root -> view -> toggle-full-screen',
|
||||||
"root -> help",
|
'root -> help',
|
||||||
"root -> help -> navigate-to-welcome",
|
'root -> help -> navigate-to-welcome',
|
||||||
"root -> help -> open-documentation",
|
'root -> help -> open-documentation',
|
||||||
"root -> help -> open-support",
|
'root -> help -> open-support',
|
||||||
"root -> help -> about",
|
'root -> help -> about'
|
||||||
]
|
]"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import populateApplicationMenuInjectable from "./main/populate-application-menu.
|
|||||||
import { advanceFakeTime, testUsingFakeTime } from "../../test-utils/use-fake-time";
|
import { advanceFakeTime, testUsingFakeTime } from "../../test-utils/use-fake-time";
|
||||||
import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths";
|
import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths";
|
||||||
import platformInjectable, { allPlatforms } from "../../common/vars/platform.injectable";
|
import platformInjectable, { allPlatforms } from "../../common/vars/platform.injectable";
|
||||||
|
import { inspect } from "util";
|
||||||
|
|
||||||
describe.each(allPlatforms)("application-menu, given platform is '%s'", (platform) => {
|
describe.each(allPlatforms)("application-menu, given platform is '%s'", (platform) => {
|
||||||
let builder: ApplicationBuilder;
|
let builder: ApplicationBuilder;
|
||||||
@ -53,7 +54,14 @@ describe.each(allPlatforms)("application-menu, given platform is '%s'", (platfor
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("populates application menu", () => {
|
it("populates application menu", () => {
|
||||||
expect(applicationMenuPaths.map(x => x.join(" -> "))).toMatchSnapshot();
|
expect(inspect(applicationMenuPaths.map(x => x.join(" -> ")), {
|
||||||
|
compact: false,
|
||||||
|
breakLength: Infinity,
|
||||||
|
colors: false,
|
||||||
|
depth: Infinity,
|
||||||
|
maxArrayLength: Infinity,
|
||||||
|
maxStringLength: Infinity,
|
||||||
|
})).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -231,7 +231,7 @@ exports[`installing update when started renders 1`] = `
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -516,7 +516,7 @@ exports[`installing update when started when user checks for updates renders 1`]
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -801,7 +801,7 @@ exports[`installing update when started when user checks for updates when new up
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1086,7 +1086,7 @@ exports[`installing update when started when user checks for updates when new up
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1396,7 +1396,7 @@ exports[`installing update when started when user checks for updates when new up
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -1706,7 +1706,7 @@ exports[`installing update when started when user checks for updates when new up
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -2016,7 +2016,7 @@ exports[`installing update when started when user checks for updates when new up
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
@ -2301,7 +2301,7 @@ exports[`installing update when started when user checks for updates when no new
|
|||||||
class="HotbarSelector"
|
class="HotbarSelector"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
class="Icon Icon previous material interactive focusable"
|
class="Icon material interactive focusable"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user