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:
Sebastian Malton authored and GitHub committed 2022-05-16 07:17:57 -04:00
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;
-1
View File
@@ -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;
@@ -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;
-18
View File
@@ -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);
+17
View File
@@ -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;
+21 -12
View File
@@ -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) {
+17 -11
View File
@@ -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 {