mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Turn on strict mode in tsconfig.json, some helpful lints, and required cleanup where strictness necessitates it (#5195)
* Turn on strict mode in tsconfig.json - Add route, clusterRoute, and payloadValidatedClusterRoute helper functions to improve types with backend routes - Turn on the following new lints: - react/jsx-first-prop-new-line - react/jsx-wrap-multilines - react/jsx-one-expression-per-line - react/jsx-max-props-per-line - react/jsx-indent - react/jsx-indent-props Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix build Signed-off-by: Sebastian Malton <sebastian@malton.name> * Replace KubeObject scope strings with enum Signed-off-by: Sebastian Malton <sebastian@malton.name> * Revert package.json version changes Signed-off-by: Sebastian Malton <sebastian@malton.name> * revert move hostedCluster(Id) Signed-off-by: Sebastian Malton <sebastian@malton.name> * change some type param names to be not single letters Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove copy-extension-themes Signed-off-by: Sebastian Malton <sebastian@malton.name> * add new make clean action Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix build to not use webpack for generating types Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube-object-menu.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix select.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * revert move fileNameMigration to index Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix ref logic error Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix log-resource-selector.test.tsx tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-store.test.ts test by overriding createStorage to not touch file system Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cluster.test.ts tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube=api.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fixed hotbar-store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kubeconfig-manager.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cluster-role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix pods.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix delete-cluster-dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix daemonset.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix replicaset.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix statefulsets/dialog/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix replicasets/scale-dialog/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix deployments.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix deployments/scale/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cronjob.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix stateful-set.api.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix deployment.api.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix api-manager.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix statefulset.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix job.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix pods.store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix scroll-spy.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix hotbar-remove-command.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog-entity-registry.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix welcome.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix verify-that-all-routes-have-route-component.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix pod-tolerations.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * better fix for previous 3 fixes, plus also select.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube-object-menu.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix app-paths.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-tabs.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix isReactNode typing Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix sub-title.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix drawer.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix list-layout.tsx and header.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix error-boundary.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix upgrade-chart/store.ts and dock-tab.store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix install-chart/store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix edit-resource/store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix create-resource/store.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix namespace-select.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix namespace-select-filter.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix crd-list.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix wrong types for extensions Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix circular dependency Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix circular dependency on catalogCategoryRegistry Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix api-kube Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix type errors, most <Select /> errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * fixing more type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * some more fixing type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * convert all KubeApis to injectable with legacy global backups Signed-off-by: Sebastian Malton <sebastian@malton.name> * factor out into a common file all the exports Signed-off-by: Sebastian Malton <sebastian@malton.name> * convert all KubeObjectStores to injectable with legacy global backups Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove unused legacy KubeApi globals Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix bad previous commit Signed-off-by: Sebastian Malton <sebastian@malton.name> * more crash fixing Signed-off-by: Sebastian Malton <sebastian@malton.name> * try and fix behavioural tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix sidebar-and-tab-navigation-for-core.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix sidebar-and-tab-navigation-for-extensions.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-using-application-menu.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make ThemeStore non-singleton and fix navigation-to-terminal-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * extensions.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix catalog-entity-registry.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-using-application-menu.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix log-resource-selector.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-tabs.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix delete-cluster-dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-kubernetes-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-editor-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-proxy-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-using-application-menu.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-application-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix dock-store.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix select.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix hotbar-remove-command.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix cluster-role-bindings/__tests__/dialog.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-extension-specific-preferences.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-telemetry-preferences.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix closing-preferences.test.tsx Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-editor-preferences.test.ts Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix navigation-to-proxy-preferences.test.ts - Fix other type errors too Signed-off-by: Sebastian Malton <sebastian@malton.name> * final tweaks Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add more tsconfig files, fix bug in <Catalog> - Make all of history, navigation injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Convert all of kube-details-params/ and navigate/ to injectable - This fixes a runtime error that was encountered during testing Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix runtime errors on renderer - remove all static uses of `createPageParam` (and then removed the legacy global) - Made LensRendererExtension and LensMainExtension just used dependencies and not the getLegacyDi - Fixed circular dep in extension-loader Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move registerStore calls to after injectMany Signed-off-by: Sebastian Malton <sebastian@malton.name> * replace all the rest of the legacy uses of apiManager Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix stack overflow and cycles in DI Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix NamespaceSelectFilter not opening Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix WizardStep and AddNamespaceDialog Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix KubeApi's not being registered Signed-off-by: Sebastian Malton <sebastian@malton.name> * cleanup WindowManager Signed-off-by: Sebastian Malton <sebastian@malton.name> * Proper fix for Wizard, fix NamespaceStore.subscribe Signed-off-by: Sebastian Malton <sebastian@malton.name> * Rewrite withTooltip to be more type correct - Fixes mobx related "too many recursive actions" error - Change all the uses of withTooltips to be functional components Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add e2e test to cover kube api registration Signed-off-by: Sebastian Malton <sebastian@malton.name> * cleanup internal-commands Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove cast in <Animate> Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix command-palette e2e test Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type error after rebase Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test name Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix lint Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix code to help CodeQL scanner Signed-off-by: Sebastian Malton <sebastian@malton.name> * update intree extension lock files Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix build-extensions picking wrong @types/react Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix tests from rebase Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * Make KubeconfigSyncManager more injectable Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix crash in test mode for Dialog Signed-off-by: Sebastian Malton <sebastian@malton.name> * make Select snapshots deterministic Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix new type error Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix kube-object.store.test.ts typing Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix merge build issues Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix snapshots after merge Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix lint after merge Signed-off-by: Sebastian Malton <sebastian@malton.name> * reexport BaseKubeJsonApiObjectMetadata Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix typo in terminalSpawningPool Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove duplicate license header Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix typo to waitUntilDefined Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove iter use from getLegacyGlobalDiForExtensionApi Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove complex createStorage override Signed-off-by: Sebastian Malton <sebastian@malton.name> * override logger with mocks only when needed for tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove specialized overrideStore flags for getDiForUnitTesting Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove unnecessary | undefined types from the exactOptionalFieldTypes experiment Signed-off-by: Sebastian Malton <sebastian@malton.name> * use more descriptive name for local test mocks Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove unnecessary addition to 'make clean' target Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove oddity of KubeObjectStore.getById(undefined) being allowed Signed-off-by: Sebastian Malton <sebastian@malton.name> * rename KubeObject.getDescriptor in favour of name without fundemental JS meaning Signed-off-by: Sebastian Malton <sebastian@malton.name> * Simplify legacyRegisterApi when working in behaviour unit tests - Don't emit within main environment as there should be no auto registering there Signed-off-by: Sebastian Malton <sebastian@malton.name> * change confusing variable name in ReactiveDuration Signed-off-by: Sebastian Malton <sebastian@malton.name> * make visitor pattern more explicit for Entity contextMenuOpen Signed-off-by: Sebastian Malton <sebastian@malton.name> * toggleDetails -> toggleKubeDetailsPane is more specific Signed-off-by: Sebastian Malton <sebastian@malton.name> * remove outdated comment Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix bug where LensExtension dependencies are not set Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix tests from the revert of react 18 Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix more tests from merge Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix typings with new is-compatible-extension tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * more type fixing Signed-off-by: Sebastian Malton <sebastian@malton.name> * Revert in-tree extension versions Signed-off-by: Sebastian Malton <sebastian@malton.name> * Improve name of guarding injectable for stores and apis - New name better implies that it is just a guard state and does not do anything Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add helper for <Select>.isMulti for storing in a Set<Value> Signed-off-by: Sebastian Malton <sebastian@malton.name> * fix is-compatible-extension.test.ts types Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
381d77c633
commit
dfcb7c3330
1062 files changed
+26512
-28689
No files matched your search
@@ -3,21 +3,19 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { CatalogEntityRegistry } from "../catalog-entity-registry";
|
||||
import { catalogCategoryRegistry } from "../../../common/catalog/catalog-category-registry";
|
||||
import type { CatalogEntityData, CatalogEntityKindData } from "../catalog-entity";
|
||||
import { CatalogCategory } from "../catalog-entity";
|
||||
import { KubernetesCluster, WebLink } from "../../../common/catalog-entities";
|
||||
import { observable } from "mobx";
|
||||
import type { CatalogCategoryRegistry } from "../../../common/catalog";
|
||||
import { categoryVersion } from "../../../common/catalog";
|
||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||
import type { CatalogEntityRegistry } from "../catalog/entity/registry";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity/registry.injectable";
|
||||
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
|
||||
|
||||
class TestCatalogEntityRegistry extends CatalogEntityRegistry {
|
||||
replaceItems(items: Array<CatalogEntityData & CatalogEntityKindData>) {
|
||||
this.updateItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
class FooBarCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly apiVersion = "entityRegistry.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
name: "FooBars",
|
||||
@@ -26,10 +24,7 @@ class FooBarCategory extends CatalogCategory {
|
||||
public spec = {
|
||||
group: "entity.k8slens.dev",
|
||||
versions: [
|
||||
{
|
||||
name: "v1alpha1",
|
||||
entityClass: WebLink,
|
||||
},
|
||||
categoryVersion("v1alpha1", WebLink),
|
||||
],
|
||||
names: {
|
||||
kind: "FooBar",
|
||||
@@ -81,9 +76,18 @@ const entitykc = new KubernetesCluster({
|
||||
});
|
||||
|
||||
describe("CatalogEntityRegistry", () => {
|
||||
let entityRegistry: CatalogEntityRegistry;
|
||||
let catalogCategoryRegistry: CatalogCategoryRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
const di = getDiForUnitTesting();
|
||||
|
||||
entityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
catalogCategoryRegistry = di.inject(catalogCategoryRegistryInjectable);
|
||||
});
|
||||
|
||||
describe("updateItems", () => {
|
||||
it("adds new catalog item", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
@@ -99,8 +103,8 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {},
|
||||
}];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.items.get().length).toEqual(1);
|
||||
|
||||
items.push({
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
@@ -117,12 +121,11 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {},
|
||||
});
|
||||
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(2);
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.items.get().length).toEqual(2);
|
||||
});
|
||||
|
||||
it("updates existing items", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
@@ -138,19 +141,18 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {},
|
||||
}];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
expect(catalog.items[0].status.phase).toEqual("disconnected");
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.items.get().length).toEqual(1);
|
||||
expect(entityRegistry.items.get()[0].status.phase).toEqual("disconnected");
|
||||
|
||||
items[0].status.phase = "connected";
|
||||
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
expect(catalog.items[0].status.phase).toEqual("connected");
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.items.get().length).toEqual(1);
|
||||
expect(entityRegistry.items.get()[0].status.phase).toEqual("connected");
|
||||
});
|
||||
|
||||
it("updates activeEntity", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
kind: "KubernetesCluster",
|
||||
@@ -166,17 +168,16 @@ describe("CatalogEntityRegistry", () => {
|
||||
spec: {},
|
||||
}];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
catalog.activeEntity = catalog.items[0];
|
||||
expect(catalog.activeEntity.status.phase).toEqual("disconnected");
|
||||
entityRegistry.updateItems(items);
|
||||
entityRegistry.activeEntity = entityRegistry.items.get()[0];
|
||||
expect(entityRegistry.activeEntity.status.phase).toEqual("disconnected");
|
||||
|
||||
items[0].status.phase = "connected";
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.activeEntity.status.phase).toEqual("connected");
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.activeEntity.status.phase).toEqual("connected");
|
||||
});
|
||||
|
||||
it("removes deleted items", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [
|
||||
{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
@@ -208,17 +209,16 @@ describe("CatalogEntityRegistry", () => {
|
||||
},
|
||||
];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
entityRegistry.updateItems(items);
|
||||
items.splice(0, 1);
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toEqual(1);
|
||||
expect(catalog.items[0].metadata.uid).toEqual("456");
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.items.get().length).toEqual(1);
|
||||
expect(entityRegistry.items.get()[0].metadata.uid).toEqual("456");
|
||||
});
|
||||
});
|
||||
|
||||
describe("items", () => {
|
||||
it("does not return items without matching category", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [
|
||||
{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
@@ -250,13 +250,12 @@ describe("CatalogEntityRegistry", () => {
|
||||
},
|
||||
];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
expect(catalog.items.length).toBe(1);
|
||||
entityRegistry.updateItems(items);
|
||||
expect(entityRegistry.items.get().length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it("does return items after matching category is added", () => {
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
const items = [
|
||||
{
|
||||
apiVersion: "entity.k8slens.dev/v1alpha1",
|
||||
@@ -274,29 +273,28 @@ describe("CatalogEntityRegistry", () => {
|
||||
},
|
||||
];
|
||||
|
||||
catalog.replaceItems(items);
|
||||
entityRegistry.updateItems(items);
|
||||
catalogCategoryRegistry.add(new FooBarCategory());
|
||||
expect(catalog.items.length).toBe(1);
|
||||
expect(entityRegistry.items.get().length).toBe(1);
|
||||
});
|
||||
|
||||
it("does not return items that are filtered out", () => {
|
||||
const source = observable.array([entity, entity2, entitykc]);
|
||||
const catalog = new TestCatalogEntityRegistry(catalogCategoryRegistry);
|
||||
|
||||
catalog.replaceItems(source);
|
||||
entityRegistry.updateItems(source);
|
||||
|
||||
expect(catalog.items.length).toBe(3);
|
||||
expect(catalog.filteredItems.length).toBe(3);
|
||||
expect(entityRegistry.items.get().length).toBe(3);
|
||||
expect(entityRegistry.filteredItems.length).toBe(3);
|
||||
|
||||
const d = catalog.addCatalogFilter(entity => entity.kind === KubernetesCluster.kind);
|
||||
const d = entityRegistry.addCatalogFilter(entity => entity.kind === KubernetesCluster.kind);
|
||||
|
||||
expect(catalog.items.length).toBe(3);
|
||||
expect(catalog.filteredItems.length).toBe(1);
|
||||
expect(entityRegistry.items.get().length).toBe(3);
|
||||
expect(entityRegistry.filteredItems.length).toBe(1);
|
||||
|
||||
// Remove filter
|
||||
d();
|
||||
|
||||
expect(catalog.items.length).toBe(3);
|
||||
expect(catalog.filteredItems.length).toBe(3);
|
||||
expect(entityRegistry.items.get().length).toBe(3);
|
||||
expect(entityRegistry.filteredItems.length).toBe(3);
|
||||
});
|
||||
});
|
||||
@@ -3,5 +3,9 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export { catalogCategoryRegistry } from "../../common/catalog";
|
||||
import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable";
|
||||
import { asLegacyGlobalForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
|
||||
|
||||
export type { CategoryFilter } from "../../common/catalog";
|
||||
|
||||
export const catalogCategoryRegistry = asLegacyGlobalForExtensionApi(catalogCategoryRegistryInjectable);
|
||||
@@ -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 { catalogEntityRegistry } from "../catalog-entity-registry";
|
||||
|
||||
const catalogEntityRegistryInjectable = getInjectable({
|
||||
id: "catalog-entity-registry",
|
||||
instantiate: () => catalogEntityRegistry,
|
||||
});
|
||||
|
||||
export default catalogEntityRegistryInjectable;
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
export { catalogEntityRunContext } from "./catalog-entity-registry";
|
||||
export { CatalogCategory, CatalogEntity } from "../../common/catalog";
|
||||
export type {
|
||||
CatalogEntityData,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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 { computed } from "mobx";
|
||||
import catalogEntityRegistryInjectable from "./registry.injectable";
|
||||
|
||||
const activeEntityInjectable = getInjectable({
|
||||
id: "active-entity",
|
||||
instantiate: (di) => {
|
||||
const registry = di.inject(catalogEntityRegistryInjectable);
|
||||
|
||||
return computed(() => registry.activeEntity);
|
||||
},
|
||||
});
|
||||
|
||||
export default activeEntityInjectable;
|
||||
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* 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 catalogEntityRegistryInjectable from "./registry.injectable";
|
||||
|
||||
const catalogEnitiesInjectable = getInjectable({
|
||||
id: "catalog-enities",
|
||||
instantiate: (di) => di.inject(catalogEntityRegistryInjectable).items,
|
||||
});
|
||||
|
||||
export default catalogEnitiesInjectable;
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 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 clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
|
||||
import type { Cluster } from "../../../../common/cluster/cluster";
|
||||
import catalogEntityRegistryInjectable from "./registry.injectable";
|
||||
|
||||
export type GetActiveClusterEntity = () => Cluster | undefined;
|
||||
|
||||
const getActiveClusterEntityInjectable = getInjectable({
|
||||
id: "get-active-cluster-entity",
|
||||
instantiate: (di): GetActiveClusterEntity => {
|
||||
const store = di.inject(clusterStoreInjectable);
|
||||
const entityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
|
||||
return () => store.getById(entityRegistry.activeEntity?.getId());
|
||||
},
|
||||
});
|
||||
|
||||
export default getActiveClusterEntityInjectable;
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { asLegacyGlobalFunctionForExtensionApi } from "../../../../extensions/as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
|
||||
import getActiveClusterEntityInjectable from "./get-active-cluster-entity.injectable";
|
||||
|
||||
/**
|
||||
* @deprecated use `di.inject(getActiveClusterEntityInjectable)` instead
|
||||
*/
|
||||
export const getActiveClusterEntity = asLegacyGlobalFunctionForExtensionApi(getActiveClusterEntityInjectable);
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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 catalogCategoryRegistryInjectable from "../../../../common/catalog/category-registry.injectable";
|
||||
import navigateInjectable from "../../../navigation/navigate.injectable";
|
||||
import { CatalogEntityRegistry } from "./registry";
|
||||
|
||||
const catalogEntityRegistryInjectable = getInjectable({
|
||||
id: "catalog-entity-registry",
|
||||
instantiate: (di) => new CatalogEntityRegistry({
|
||||
categoryRegistry: di.inject(catalogCategoryRegistryInjectable),
|
||||
navigate: di.inject(navigateInjectable),
|
||||
}),
|
||||
});
|
||||
|
||||
export default catalogEntityRegistryInjectable;
|
||||
+45
-45
@@ -4,56 +4,57 @@
|
||||
*/
|
||||
|
||||
import { computed, observable, makeObservable, action } from "mobx";
|
||||
import { ipcRendererOn } from "../../common/ipc";
|
||||
import type { CatalogCategory, CatalogEntity, CatalogEntityData, CatalogCategoryRegistry, CatalogEntityKindData } from "../../common/catalog";
|
||||
import { catalogCategoryRegistry } from "../../common/catalog";
|
||||
import "../../common/catalog-entities";
|
||||
import type { Cluster } from "../../common/cluster/cluster";
|
||||
import { ClusterStore } from "../../common/cluster-store/cluster-store";
|
||||
import { iter } from "../utils";
|
||||
import type { Disposer } from "../utils";
|
||||
import { ipcRendererOn } from "../../../../common/ipc";
|
||||
import type { CatalogCategory, CatalogEntity, CatalogEntityData, CatalogCategoryRegistry, CatalogEntityKindData } from "../../../../common/catalog";
|
||||
import "../../../../common/catalog-entities";
|
||||
import { iter } from "../../../utils";
|
||||
import type { Disposer } from "../../../utils";
|
||||
import { once } from "lodash";
|
||||
import logger from "../../common/logger";
|
||||
import { CatalogRunEvent } from "../../common/catalog/catalog-run-event";
|
||||
import logger from "../../../../common/logger";
|
||||
import { CatalogRunEvent } from "../../../../common/catalog/catalog-run-event";
|
||||
import { ipcRenderer } from "electron";
|
||||
import { catalogInitChannel, catalogItemsChannel, catalogEntityRunListener } from "../../common/ipc/catalog";
|
||||
import { navigate } from "../navigation";
|
||||
import { catalogInitChannel, catalogItemsChannel, catalogEntityRunListener } from "../../../../common/ipc/catalog";
|
||||
import { isMainFrame } from "process";
|
||||
import type { Navigate } from "../../../navigation/navigate.injectable";
|
||||
|
||||
export type EntityFilter = (entity: CatalogEntity) => any;
|
||||
export type CatalogEntityOnBeforeRun = (event: CatalogRunEvent) => void | Promise<void>;
|
||||
|
||||
export const catalogEntityRunContext = {
|
||||
navigate: (url: string) => navigate(url),
|
||||
setCommandPaletteContext: (entity?: CatalogEntity) => {
|
||||
catalogEntityRegistry.activeEntity = entity;
|
||||
},
|
||||
};
|
||||
interface Dependencies {
|
||||
navigate: Navigate;
|
||||
readonly categoryRegistry: CatalogCategoryRegistry;
|
||||
}
|
||||
|
||||
export class CatalogEntityRegistry {
|
||||
@observable protected activeEntityId: string | undefined = undefined;
|
||||
protected _entities = observable.map<string, CatalogEntity>([], { deep: true });
|
||||
protected filters = observable.set<EntityFilter>([], {
|
||||
protected readonly activeEntityId = observable.box<string | undefined>(undefined);
|
||||
protected readonly _entities = observable.map<string, CatalogEntity>([], { deep: true });
|
||||
protected readonly filters = observable.set<EntityFilter>([], {
|
||||
deep: false,
|
||||
});
|
||||
protected onBeforeRunHooks = observable.set<CatalogEntityOnBeforeRun>([], {
|
||||
protected readonly onBeforeRunHooks = observable.set<CatalogEntityOnBeforeRun>([], {
|
||||
deep: false,
|
||||
});
|
||||
|
||||
/**
|
||||
* Buffer for keeping entities that don't yet have CatalogCategory synced
|
||||
*/
|
||||
protected rawEntities: (CatalogEntityData & CatalogEntityKindData)[] = [];
|
||||
protected readonly rawEntities: (CatalogEntityData & CatalogEntityKindData)[] = [];
|
||||
|
||||
constructor(private categoryRegistry: CatalogCategoryRegistry) {
|
||||
constructor(protected readonly dependencies: Dependencies) {
|
||||
makeObservable(this);
|
||||
}
|
||||
|
||||
protected getActiveEntityById() {
|
||||
return this._entities.get(this.activeEntityId) || null;
|
||||
const activeEntityId = this.activeEntityId.get();
|
||||
|
||||
if (!activeEntityId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this._entities.get(activeEntityId);
|
||||
}
|
||||
|
||||
get activeEntity(): CatalogEntity | null {
|
||||
get activeEntity(): CatalogEntity | undefined {
|
||||
const entity = this.getActiveEntityById();
|
||||
|
||||
// If the entity was not found but there are rawEntities to be processed,
|
||||
@@ -68,15 +69,15 @@ export class CatalogEntityRegistry {
|
||||
return entity;
|
||||
}
|
||||
|
||||
set activeEntity(raw: CatalogEntity | string | null) {
|
||||
set activeEntity(raw: CatalogEntity | string | undefined) {
|
||||
if (raw) {
|
||||
const id = typeof raw === "string"
|
||||
? raw
|
||||
: raw.getId();
|
||||
|
||||
this.activeEntityId = id;
|
||||
this.activeEntityId.set(id);
|
||||
} else {
|
||||
this.activeEntityId = undefined;
|
||||
this.activeEntityId.set(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +120,7 @@ export class CatalogEntityRegistry {
|
||||
const existing = this._entities.get(item.metadata.uid);
|
||||
|
||||
if (!existing) {
|
||||
const entity = this.categoryRegistry.getEntityForData(item);
|
||||
const entity = this.dependencies.categoryRegistry.getEntityForData(item);
|
||||
|
||||
if (entity) {
|
||||
this._entities.set(entity.getId(), entity);
|
||||
@@ -143,25 +144,25 @@ export class CatalogEntityRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
@computed get items() {
|
||||
readonly items = computed(() => {
|
||||
this.processRawEntities();
|
||||
|
||||
return Array.from(this._entities.values());
|
||||
}
|
||||
});
|
||||
|
||||
@computed get filteredItems() {
|
||||
return Array.from(
|
||||
iter.reduce(
|
||||
this.filters,
|
||||
iter.filter,
|
||||
this.items.values(),
|
||||
this.items.get().values(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@computed get entities(): Map<string, CatalogEntity> {
|
||||
return new Map(
|
||||
this.items.map(entity => [entity.getId(), entity]),
|
||||
this.items.get().map(entity => [entity.getId(), entity]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -171,13 +172,13 @@ export class CatalogEntityRegistry {
|
||||
);
|
||||
}
|
||||
|
||||
getById<T extends CatalogEntity>(id: string) {
|
||||
return this.entities.get(id) as T;
|
||||
getById(id: string) {
|
||||
return this.entities.get(id);
|
||||
}
|
||||
|
||||
getItemsForApiKind<T extends CatalogEntity>(apiVersion: string, kind: string, { filtered = false } = {}): T[] {
|
||||
const byApiKind = (item: CatalogEntity) => item.apiVersion === apiVersion && item.kind === kind;
|
||||
const entities = filtered ? this.filteredItems : this.items;
|
||||
const entities = filtered ? this.filteredItems : this.items.get();
|
||||
|
||||
return entities.filter(byApiKind) as T[];
|
||||
}
|
||||
@@ -185,7 +186,7 @@ export class CatalogEntityRegistry {
|
||||
getItemsForCategory<T extends CatalogEntity>(category: CatalogCategory, { filtered = false } = {}): T[] {
|
||||
const supportedVersions = new Set(category.spec.versions.map((v) => `${category.spec.group}/${v.name}`));
|
||||
const byApiVersionKind = (item: CatalogEntity) => supportedVersions.has(item.apiVersion) && item.kind === category.spec.names.kind;
|
||||
const entities = filtered ? this.filteredItems : this.items;
|
||||
const entities = filtered ? this.filteredItems : this.items.get();
|
||||
|
||||
return entities.filter(byApiVersionKind) as T[];
|
||||
}
|
||||
@@ -245,7 +246,12 @@ export class CatalogEntityRegistry {
|
||||
this.onBeforeRun(entity)
|
||||
.then(doOnRun => {
|
||||
if (doOnRun) {
|
||||
return entity.onRun?.(catalogEntityRunContext);
|
||||
return entity.onRun?.({
|
||||
navigate: this.dependencies.navigate,
|
||||
setCommandPaletteContext: (entity) => {
|
||||
this.activeEntity = entity;
|
||||
},
|
||||
});
|
||||
} else {
|
||||
logger.debug(`onBeforeRun for ${entity.getId()} returned false`);
|
||||
}
|
||||
@@ -253,9 +259,3 @@ export class CatalogEntityRegistry {
|
||||
.catch(error => logger.error(`[CATALOG-ENTITY-REGISTRY]: entity ${entity.getId()} onRun threw an error`, error));
|
||||
}
|
||||
}
|
||||
|
||||
export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry);
|
||||
|
||||
export function getActiveClusterEntity(): Cluster | undefined {
|
||||
return ClusterStore.getInstance().getById(catalogEntityRegistry.activeEntity?.getId());
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 assert from "assert";
|
||||
import hostedClusterIdInjectable from "../../common/cluster-store/hosted-cluster-id.injectable";
|
||||
import type { TerminalApiQuery } from "./terminal-api";
|
||||
import { TerminalApi } from "./terminal-api";
|
||||
|
||||
export type CreateTerminalApi = (query: TerminalApiQuery) => TerminalApi;
|
||||
|
||||
const createTerminalApiInjectable = getInjectable({
|
||||
id: "create-terminal-api",
|
||||
instantiate: (di): CreateTerminalApi => {
|
||||
const hostedClusterId = di.inject(hostedClusterIdInjectable);
|
||||
|
||||
return (query) => {
|
||||
assert(hostedClusterId, "Can only create terminal APIs within a cluster frame");
|
||||
|
||||
return new TerminalApi({
|
||||
hostedClusterId,
|
||||
}, query);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default createTerminalApiInjectable;
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { when } from "mobx";
|
||||
import { catalogCategoryRegistry } from "../../../common/catalog";
|
||||
import { catalogEntityRegistry } from "../catalog-entity-registry";
|
||||
import { isActiveRoute } from "../../navigation";
|
||||
import type { GeneralEntity } from "../../../common/catalog-entities";
|
||||
|
||||
export async function setEntityOnRouteMatch() {
|
||||
await when(() => catalogEntityRegistry.entities.size > 0);
|
||||
|
||||
const entities: GeneralEntity[] = catalogEntityRegistry.getItemsForCategory(catalogCategoryRegistry.getByName("General"));
|
||||
const activeEntity = entities.find(entity => isActiveRoute(entity.spec.path));
|
||||
|
||||
if (activeEntity) {
|
||||
catalogEntityRegistry.activeEntity = activeEntity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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 { reaction, when } from "mobx";
|
||||
import generalCategoryInjectable from "../../../common/catalog/categories/general.injectable";
|
||||
import isActiveRouteInjectable from "../../navigation/is-route-active.injectable";
|
||||
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
|
||||
import type { Disposer } from "../../utils";
|
||||
import { disposer } from "../../utils";
|
||||
import catalogEntityRegistryInjectable from "../catalog/entity/registry.injectable";
|
||||
|
||||
export type WatchForGeneralEntityNavigation = () => Disposer;
|
||||
|
||||
const watchForGeneralEntityNavigationInjectable = getInjectable({
|
||||
id: "watch-for-general-entity-navigation",
|
||||
instantiate: (di): WatchForGeneralEntityNavigation => {
|
||||
const observableHistory = di.inject(observableHistoryInjectable);
|
||||
const isActiveRoute = di.inject(isActiveRouteInjectable);
|
||||
const entityRegistry = di.inject(catalogEntityRegistryInjectable);
|
||||
const generalCategory = di.inject(generalCategoryInjectable);
|
||||
|
||||
return () => {
|
||||
const dispose = disposer();
|
||||
|
||||
dispose.push(when(
|
||||
() => entityRegistry.entities.size > 0,
|
||||
() => {
|
||||
dispose.push(reaction(
|
||||
() => observableHistory.location,
|
||||
() => {
|
||||
const entities = entityRegistry.getItemsForCategory(generalCategory);
|
||||
const activeEntity = entities.find(entity => isActiveRoute(entity.spec.path));
|
||||
|
||||
if (activeEntity) {
|
||||
entityRegistry.activeEntity = activeEntity;
|
||||
}
|
||||
},
|
||||
{
|
||||
fireImmediately: true,
|
||||
},
|
||||
));
|
||||
},
|
||||
));
|
||||
|
||||
return dispose;
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export default watchForGeneralEntityNavigationInjectable;
|
||||
@@ -3,22 +3,4 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { JsonApiErrorParsed } from "../../common/k8s-api/json-api";
|
||||
import type { Response } from "node-fetch";
|
||||
import { Notifications } from "../components/notifications";
|
||||
import { apiBase, apiKube } from "../../common/k8s-api";
|
||||
export { apiBase, apiKube } from "../../common/k8s-api";
|
||||
|
||||
|
||||
// Common handler for HTTP api errors
|
||||
export function onApiError(error: JsonApiErrorParsed, res: Response) {
|
||||
switch (res.status) {
|
||||
case 403:
|
||||
error.isUsedForNotification = true;
|
||||
Notifications.error(error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (apiBase) apiBase.onError.addListener(onApiError);
|
||||
if (apiKube) apiKube.onError.addListener(onApiError);
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import type { Response } from "node-fetch";
|
||||
import type { JsonApiErrorParsed } from "../../common/k8s-api/json-api";
|
||||
import { Notifications } from "../components/notifications";
|
||||
|
||||
export function onApiError(error: JsonApiErrorParsed, res: Response) {
|
||||
switch (res.status) {
|
||||
case 403:
|
||||
error.isUsedForNotification = true;
|
||||
Notifications.error(error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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 { apiBase } from "../../common/k8s-api";
|
||||
import { onApiError } from "./on-api-error";
|
||||
|
||||
const setupOnApiErrorListenersInjectable = getInjectable({
|
||||
id: "setup-on-api-error-listeners",
|
||||
setup: () => {
|
||||
apiBase?.onError.addListener(onApiError);
|
||||
},
|
||||
instantiate: () => undefined,
|
||||
});
|
||||
|
||||
export default setupOnApiErrorListenersInjectable;
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
|
||||
import { getHostedClusterId } from "../utils";
|
||||
import type { WebSocketEvents } from "./websocket-api";
|
||||
import { WebSocketApi } from "./websocket-api";
|
||||
import isEqual from "lodash/isEqual";
|
||||
@@ -49,23 +48,27 @@ enum TerminalColor {
|
||||
NO_COLOR = "\u001b[0m",
|
||||
}
|
||||
|
||||
export type TerminalApiQuery = Record<string, string> & {
|
||||
export interface TerminalApiQuery extends Record<string, string | undefined> {
|
||||
id: string;
|
||||
node?: string;
|
||||
type?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface TerminalEvents extends WebSocketEvents {
|
||||
ready: () => void;
|
||||
connected: () => void;
|
||||
}
|
||||
|
||||
export interface TerminalApiDependencies {
|
||||
readonly hostedClusterId: string;
|
||||
}
|
||||
|
||||
export class TerminalApi extends WebSocketApi<TerminalEvents> {
|
||||
protected size: { width: number; height: number };
|
||||
protected size?: { width: number; height: number };
|
||||
|
||||
@observable public isReady = false;
|
||||
|
||||
constructor(protected query: TerminalApiQuery) {
|
||||
constructor(protected readonly dependencies: TerminalApiDependencies, protected readonly query: TerminalApiQuery) {
|
||||
super({
|
||||
flushOnOpen: false,
|
||||
pingInterval: 30,
|
||||
@@ -86,7 +89,7 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
|
||||
this.emitStatus("Connecting ...");
|
||||
}
|
||||
|
||||
const authTokenArray = await ipcRenderer.invoke("cluster:shell-api", getHostedClusterId(), this.query.id);
|
||||
const authTokenArray = await ipcRenderer.invoke("cluster:shell-api", this.dependencies.hostedClusterId, this.query.id);
|
||||
|
||||
if (!(authTokenArray instanceof Uint8Array)) {
|
||||
throw new TypeError("ShellApi token is not a Uint8Array");
|
||||
@@ -114,11 +117,15 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
|
||||
|
||||
// data is undefined if the event that was handled is "connected"
|
||||
if (data === undefined) {
|
||||
/**
|
||||
* Output the last line, the makes sure that the terminal isn't completely
|
||||
* empty when the user refreshes.
|
||||
*/
|
||||
this.emit("data", window.localStorage.getItem(`${this.query.id}:last-data`));
|
||||
const lastData = window.localStorage.getItem(`${this.query.id}:last-data`);
|
||||
|
||||
if (lastData) {
|
||||
/**
|
||||
* Output the last line, the makes sure that the terminal isn't completely
|
||||
* empty when the user refreshes.
|
||||
*/
|
||||
this.emit("data", lastData);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -126,7 +133,9 @@ export class TerminalApi extends WebSocketApi<TerminalEvents> {
|
||||
this.prependListener("connected", onReady);
|
||||
|
||||
super.connect(socketUrl);
|
||||
this.socket.binaryType = "arraybuffer";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.socket!.binaryType = "arraybuffer";
|
||||
}
|
||||
|
||||
sendMessage(message: TerminalMessage) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import EventEmitter from "events";
|
||||
import type TypedEventEmitter from "typed-emitter";
|
||||
import type { Arguments } from "typed-emitter";
|
||||
import { isDevelopment } from "../../common/vars";
|
||||
import type { Defaulted } from "../utils";
|
||||
|
||||
interface WebsocketApiParams {
|
||||
/**
|
||||
@@ -62,18 +63,16 @@ export interface WebSocketEvents {
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
type Defaulted<Params, DefaultParams extends keyof Params> = Required<Pick<Params, DefaultParams>> & Omit<Params, DefaultParams>;
|
||||
|
||||
export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter as { new<T>(): TypedEventEmitter<T> })<Events> {
|
||||
protected socket?: WebSocket | null;
|
||||
protected socket: WebSocket | null = null;
|
||||
protected pendingCommands: (string | ArrayBufferLike | Blob | ArrayBufferView)[] = [];
|
||||
protected reconnectTimer?: any;
|
||||
protected pingTimer?: any;
|
||||
protected reconnectTimer?: number;
|
||||
protected pingTimer?: number;
|
||||
protected params: Defaulted<WebsocketApiParams, keyof typeof WebSocketApi["defaultParams"]>;
|
||||
|
||||
@observable readyState = WebSocketApiState.PENDING;
|
||||
|
||||
private static defaultParams = {
|
||||
private static readonly defaultParams = {
|
||||
logging: isDevelopment,
|
||||
reconnectDelay: 10,
|
||||
flushOnOpen: true,
|
||||
@@ -83,18 +82,25 @@ export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter
|
||||
constructor(params: WebsocketApiParams) {
|
||||
super();
|
||||
makeObservable(this);
|
||||
this.params = Object.assign({}, WebSocketApi.defaultParams, params);
|
||||
this.params = {
|
||||
...WebSocketApi.defaultParams,
|
||||
...params,
|
||||
};
|
||||
const { pingInterval } = this.params;
|
||||
|
||||
if (pingInterval) {
|
||||
this.pingTimer = setInterval(() => this.ping(), pingInterval * 1000);
|
||||
this.pingTimer = window.setInterval(() => this.ping(), pingInterval * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
get isConnected() {
|
||||
protected getIsConnected(): this is (WebSocketApi<Events> & { socket: WebSocket }) {
|
||||
return this.socket?.readyState === WebSocket.OPEN && this.isOnline;
|
||||
}
|
||||
|
||||
get isConnected() {
|
||||
return this.getIsConnected();
|
||||
}
|
||||
|
||||
get isOnline() {
|
||||
return navigator.onLine;
|
||||
}
|
||||
@@ -144,7 +150,7 @@ export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter
|
||||
}
|
||||
|
||||
send(command: string | ArrayBufferLike | Blob | ArrayBufferView) {
|
||||
if (this.isConnected) {
|
||||
if (this.getIsConnected()) {
|
||||
this.socket.send(command);
|
||||
} else {
|
||||
this.pendingCommands.push(command);
|
||||
@@ -186,7 +192,7 @@ export class WebSocketApi<Events extends WebSocketEvents> extends (EventEmitter
|
||||
|
||||
this.writeLog("will reconnect in", `${reconnectDelay}s`);
|
||||
|
||||
this.reconnectTimer = setTimeout(() => this.connect(url), reconnectDelay * 1000);
|
||||
this.reconnectTimer = window.setTimeout(() => this.connect(url), reconnectDelay * 1000);
|
||||
this.readyState = WebSocketApiState.RECONNECTING;
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user