diff --git a/package-lock.json b/package-lock.json index 4904d88e78..be33d9151a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23326,9 +23326,9 @@ } }, "node_modules/joi": { - "version": "17.8.4", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.8.4.tgz", - "integrity": "sha512-jjdRHb5WtL+KgSHvOULQEPPv4kcl+ixd1ybOFQq3rWLgEEqc03QMmilodL0GVJE14U/SQDXkUhQUSZANGDH/AA==", + "version": "17.9.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.1.tgz", + "integrity": "sha512-FariIi9j6QODKATGBrEX7HZcja8Bsh3rfdGYy/Sb65sGlZWK/QWesU1ghk7aJWDj95knjXlQfSmzFSPPkLVsfw==", "dependencies": { "@hapi/hoek": "^9.0.0", "@hapi/topo": "^5.0.0", @@ -38180,7 +38180,7 @@ "hpagent": "^1.2.0", "http-proxy": "^1.18.1", "immer": "^9.0.19", - "joi": "^17.7.1", + "joi": "^17.9.1", "js-yaml": "^4.1.0", "lodash": "^4.17.15", "marked": "^4.2.12", diff --git a/package.json b/package.json index 016cb3464c..2f9a957217 100644 --- a/package.json +++ b/package.json @@ -21,13 +21,13 @@ "postdev": "lerna watch -- lerna run build --stream --scope \\$LERNA_PACKAGE_NAME", "prestart-dev": "cd packages/open-lens && rimraf static/build/ && npm run build:tray-icons && npm run download:binaries", "start-dev": "lerna run start", - "lint": "lerna run lint --stream", + "lint": "lerna run lint --stream --no-bail", "lint:fix": "lerna run lint:fix --stream", "mkdocs:serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest", "mkdocs:verify": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -v ${PWD}:/docs mkdocs-serve-local:latest build --strict", - "test:unit": "lerna run --stream test:unit", + "test:unit": "lerna run --stream test:unit --no-bail", "test:unit:watch": "jest --watch", - "test:integration": "lerna run --stream test:integration", + "test:integration": "lerna run --stream test:integration --no-bail", "bump-version": "lerna version --no-git-tag-version --no-push", "precreate-release-pr": "cd packages/release-tool && npm run build", "create-release-pr": "node packages/release-tool/dist/index.js" diff --git a/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts b/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts index 5b0c324203..ecab52138b 100644 --- a/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts +++ b/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts @@ -1,11 +1,7 @@ import { pipeline } from "@ogre-tools/fp"; import { filter, isString } from "lodash/fp"; import { getInjectable } from "@ogre-tools/injectable"; -import { - Binding, - KeyboardShortcut, - keyboardShortcutInjectionToken, -} from "./keyboard-shortcut-injection-token"; +import { Binding, KeyboardShortcut, keyboardShortcutInjectionToken } from "./keyboard-shortcut-injection-token"; import platformInjectable from "./platform.injectable"; export type InvokeShortcut = (event: KeyboardEvent) => void; @@ -46,29 +42,26 @@ const toBindingWithDefaults = (binding: Binding) => ...binding, }; -const toShortcutsWithMatchingBinding = - (event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => { - const binding = toBindingWithDefaults(shortcut.binding); +const toShortcutsWithMatchingBinding = (event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => { + const binding = toBindingWithDefaults(shortcut.binding); - const shiftModifierMatches = binding.shift === event.shiftKey; - const altModifierMatches = binding.altOrOption === event.altKey; + const shiftModifierMatches = binding.shift === event.shiftKey; + const altModifierMatches = binding.altOrOption === event.altKey; - const isMac = platform === "darwin"; + const isMac = platform === "darwin"; - const ctrlModifierMatches = - binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey); + const ctrlModifierMatches = binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey); - const metaModifierMatches = - binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey); + const metaModifierMatches = binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey); - return ( - event.code === binding.code && - shiftModifierMatches && - ctrlModifierMatches && - altModifierMatches && - metaModifierMatches - ); - }; + return ( + event.code === binding.code && + shiftModifierMatches && + ctrlModifierMatches && + altModifierMatches && + metaModifierMatches + ); +}; const invokeShortcutInjectable = getInjectable({ id: "invoke-shortcut", diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx index 5f2479d059..5dad663129 100644 --- a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx @@ -26,10 +26,7 @@ const NonInjectedKeyboardShortcutListener = ({ return <>{children}; }; -export const KeyboardShortcutListener = withInjectables< - Dependencies, - KeyboardShortcutListenerProps ->( +export const KeyboardShortcutListener = withInjectables( NonInjectedKeyboardShortcutListener, { diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx index d2bbfb85a4..3631a4fa98 100644 --- a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx @@ -175,8 +175,7 @@ describe("keyboard-shortcuts", () => { shouldCallCallback: true, }, { - scenario: - "given shortcut with shift modifier, when shortcut is pressed, calls the callback", + scenario: "given shortcut with shift modifier, when shortcut is pressed, calls the callback", binding: { shift: true, code: "F1" }, keyboard: "{Shift>}[F1]", diff --git a/packages/core/package.json b/packages/core/package.json index 5663c36b36..20abccc569 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -143,7 +143,7 @@ "hpagent": "^1.2.0", "http-proxy": "^1.18.1", "immer": "^9.0.19", - "joi": "^17.7.1", + "joi": "^17.9.1", "js-yaml": "^4.1.0", "lodash": "^4.17.15", "marked": "^4.2.12", diff --git a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts index ce117d9475..0490f446fe 100644 --- a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts @@ -206,3 +206,17 @@ describe("ApiManager", () => { }); }); }); + +describe("ApiManger without storesAndApisCanBeCreated", () => { + let di: DiContainer; + + beforeEach(() => { + di = getDiForUnitTesting(); + + di.override(storesAndApisCanBeCreatedInjectable, () => false); + }); + + it("should not throw when creating apiManager", () => { + di.inject(apiManagerInjectable); + }); +}); diff --git a/packages/core/src/common/k8s-api/api-manager/api-manager.ts b/packages/core/src/common/k8s-api/api-manager/api-manager.ts index 6b60ba8e7a..23574822ed 100644 --- a/packages/core/src/common/k8s-api/api-manager/api-manager.ts +++ b/packages/core/src/common/k8s-api/api-manager/api-manager.ts @@ -25,7 +25,7 @@ export type KubeObjectStoreFrom = Api extends KubeApi) => boolean; -interface Dependencies { +export interface ApiManagerDependencies { readonly apis: IComputedValue; readonly crdApis: IComputedValue; readonly stores: IComputedValue; @@ -38,7 +38,7 @@ export class ApiManager { private readonly defaultCrdStores = observable.map(); private readonly apis = observable.map(); - constructor(private readonly dependencies: Dependencies) { + constructor(private readonly dependencies: ApiManagerDependencies) { // NOTE: this is done to preserve the old behaviour of an API being discoverable using all previous apiBases autorun(() => { const apis = iter.chain(this.dependencies.apis.get().values()) diff --git a/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts b/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts index 2ffd41a296..6caca127f5 100644 --- a/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts +++ b/packages/core/src/common/k8s-api/api-manager/manager.injectable.ts @@ -18,18 +18,23 @@ const apiManagerInjectable = getInjectable({ const computedInjectMany = di.inject(computedInjectManyInjectable); const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken); - return new ApiManager({ - apis: storesAndApisCanBeCreated - ? computedInjectMany(kubeApiInjectionToken) - : computed(() => []), - stores: storesAndApisCanBeCreated - ? computedInjectMany(kubeObjectStoreInjectionToken) - : computed(() => []), - crdApis: storesAndApisCanBeCreated - ? computedInjectMany(customResourceDefinitionApiInjectionToken) - : computed(() => []), - createCustomResourceStore: di.inject(createCustomResourceStoreInjectable), - }); + return new ApiManager(( + storesAndApisCanBeCreated + ? { + apis: computedInjectMany(kubeApiInjectionToken), + stores: computedInjectMany(kubeObjectStoreInjectionToken), + crdApis: computedInjectMany(customResourceDefinitionApiInjectionToken), + createCustomResourceStore: di.inject(createCustomResourceStoreInjectable), + } + : { + apis: computed(() => []), + stores: computed(() => []), + crdApis: computed(() => []), + createCustomResourceStore: () => { + throw new Error("Tried to create a KubeObjectStore for a CustomResource in a disallowed environment"); + }, + } + )); }, }); diff --git a/packages/core/src/common/k8s-api/kube-api-parse.ts b/packages/core/src/common/k8s-api/kube-api-parse.ts index 2bb397a8be..522e2812b8 100644 --- a/packages/core/src/common/k8s-api/kube-api-parse.ts +++ b/packages/core/src/common/k8s-api/kube-api-parse.ts @@ -109,7 +109,25 @@ export function parseKubeApi(path: string): IKubeApiParsed { }; } -export function createKubeApiURL({ apiPrefix = "/apis", resource, apiVersion, name, namespace }: IKubeApiLinkRef): string { +function isIKubeApiParsed(refOrParsed: IKubeApiLinkRef | IKubeApiParsed): refOrParsed is IKubeApiParsed { + return "apiGroup" in refOrParsed; +} + +export function createKubeApiURL(linkRef: IKubeApiLinkRef): string; +export function createKubeApiURL(linkParsed: IKubeApiParsed): string; + +export function createKubeApiURL(ref: IKubeApiLinkRef | IKubeApiParsed): string { + if (isIKubeApiParsed(ref)) { + return createKubeApiURL({ + apiPrefix: ref.apiPrefix, + resource: ref.resource, + name: ref.name, + namespace: ref.namespace, + apiVersion: `${ref.apiGroup}/${ref.apiVersion}`, + }); + } + + const { apiPrefix = "/apis", resource, apiVersion, name, namespace } = ref; const parts = [apiPrefix, apiVersion]; if (namespace) { diff --git a/packages/core/src/common/k8s-api/kube-api.ts b/packages/core/src/common/k8s-api/kube-api.ts index c5ed03abad..cf9d8f15a7 100644 --- a/packages/core/src/common/k8s-api/kube-api.ts +++ b/packages/core/src/common/k8s-api/kube-api.ts @@ -193,7 +193,7 @@ export interface KubeApiWatchOptions = { +export const patchTypeHeaders: Record = { "merge": "application/merge-patch+json", "json": "application/json-patch+json", "strategic": "application/strategic-merge-patch+json", diff --git a/packages/core/src/common/k8s-api/kube-object.ts b/packages/core/src/common/k8s-api/kube-object.ts index 1feb028aff..a45d5c415f 100644 --- a/packages/core/src/common/k8s-api/kube-object.ts +++ b/packages/core/src/common/k8s-api/kube-object.ts @@ -437,6 +437,19 @@ const resourceApplierAnnotationsForFiltering = [ const filterOutResourceApplierAnnotations = (label: string) => !resourceApplierAnnotationsForFiltering.some(key => label.startsWith(key)); +export interface RawKubeObject< + Metadata extends KubeObjectMetadata = KubeObjectMetadata, + Status = Record, + Spec = Record, +> { + apiVersion: string; + kind: string; + metadata: Metadata; + status?: Status; + spec?: Spec; + managedFields?: any; +} + export class KubeObject< Metadata extends KubeObjectMetadata = KubeObjectMetadata, Status = unknown, @@ -538,23 +551,6 @@ export class KubeObject< return Object.entries(labels).map(([name, value]) => `${name}=${value}`); } - /** - * These must be RFC6902 compliant paths - */ - private static readonly nonEditablePathPrefixes = [ - "/metadata/managedFields", - "/status", - ]; - private static readonly nonEditablePaths = new Set([ - "/apiVersion", - "/kind", - "/metadata/name", - "/metadata/selfLink", - "/metadata/resourceVersion", - "/metadata/uid", - ...KubeObject.nonEditablePathPrefixes, - ]); - constructor(data: KubeJsonApiData) { if (typeof data !== "object") { throw new TypeError(`Cannot create a KubeObject from ${typeof data}`); @@ -684,18 +680,6 @@ export class KubeObject< * @deprecated use KubeApi.patch instead */ async patch(patch: Patch): Promise { - for (const op of patch) { - if (KubeObject.nonEditablePaths.has(op.path)) { - throw new Error(`Failed to update ${this.kind}: JSON pointer ${op.path} has been modified`); - } - - for (const pathPrefix of KubeObject.nonEditablePathPrefixes) { - if (op.path.startsWith(`${pathPrefix}/`)) { - throw new Error(`Failed to update ${this.kind}: Child JSON pointer of ${op.path} has been modified`); - } - } - } - const di = getLegacyGlobalDiForExtensionApi(); const requestKubeObjectPatch = di.inject(requestKubeObjectPatchInjectable); const result = await requestKubeObjectPatch(this.getName(), this.kind, this.getNs(), patch); diff --git a/packages/core/src/common/protocol-handler/router.ts b/packages/core/src/common/protocol-handler/router.ts index 7a48dbb343..2147e932e2 100644 --- a/packages/core/src/common/protocol-handler/router.ts +++ b/packages/core/src/common/protocol-handler/router.ts @@ -209,7 +209,7 @@ export abstract class LensProtocolRouter { return name; } - if (!this.dependencies.isExtensionEnabled(extension)) { + if (!extension.isBundled && !this.dependencies.isExtensionEnabled(extension.id)) { this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`); return name; diff --git a/packages/core/src/common/utils/platform-specific-version.injectable.ts b/packages/core/src/common/utils/platform-specific-version.injectable.ts new file mode 100644 index 0000000000..79abad6a49 --- /dev/null +++ b/packages/core/src/common/utils/platform-specific-version.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { DiContainerForInjection, InjectionToken } from "@ogre-tools/injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import platformInjectable from "../vars/platform.injectable"; + +export interface PlatformSpecific { + instantiate: () => T; + readonly platform: NodeJS.Platform; +} + +const platformSpecificVersionInjectable = getInjectable({ + id: "platform-specific-version", + instantiate: (di: DiContainerForInjection) => { + const targetPlatform = di.inject(platformInjectable); + + return (token: InjectionToken, void>) => ( + di.injectMany(token) + .find(impl => impl.platform === targetPlatform) + ?.instantiate() + ); + }, +}); + +export default platformSpecificVersionInjectable; + diff --git a/packages/core/src/extensions/__tests__/extension-loader.test.ts b/packages/core/src/extensions/__tests__/extension-loader.test.ts index 9be0507f6d..51a43dfeff 100644 --- a/packages/core/src/extensions/__tests__/extension-loader.test.ts +++ b/packages/core/src/extensions/__tests__/extension-loader.test.ts @@ -113,13 +113,13 @@ describe("ExtensionLoader", () => { }); it("renderer updates extension after ipc broadcast", async () => { - expect(extensionLoader.userExtensions).toEqual(new Map()); + expect(extensionLoader.userExtensions.get()).toEqual(new Map()); await extensionLoader.init(); await delay(10); // Assert the extensions after the extension broadcast event - expect(extensionLoader.userExtensions).toEqual( + expect(extensionLoader.userExtensions.get()).toEqual( new Map([ ["manifest/path", { absolutePath: "/test/1", diff --git a/packages/core/src/extensions/common-api/app.ts b/packages/core/src/extensions/common-api/app.ts index 9e0c84f4e0..a7b33c35cc 100644 --- a/packages/core/src/extensions/common-api/app.ts +++ b/packages/core/src/extensions/common-api/app.ts @@ -14,6 +14,7 @@ import { buildVersionInjectionToken } from "../../common/vars/build-semantic-ver import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; import enabledExtensionsInjectable from "../../features/extensions/enabled/common/enabled-extensions.injectable"; import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; +import { lensBuildEnvironmentInjectionToken } from "@k8slens/application"; const userStore = asLegacyGlobalForExtensionApi(userPreferencesStateInjectable); const enabledExtensions = asLegacyGlobalForExtensionApi(enabledExtensionsInjectable); @@ -53,6 +54,11 @@ export const App = { return di.inject(isLinuxInjectable); }, + get lensBuildEnvironment() { + const di = getLegacyGlobalDiForExtensionApi(); + + return di.inject(lensBuildEnvironmentInjectionToken); + }, /** * @deprecated This value is now `""` and is left here for backwards compatibility. */ diff --git a/packages/core/src/extensions/extension-discovery/extension-discovery.ts b/packages/core/src/extensions/extension-discovery/extension-discovery.ts index 7a39fa7467..107a61f886 100644 --- a/packages/core/src/extensions/extension-discovery/extension-discovery.ts +++ b/packages/core/src/extensions/extension-discovery/extension-discovery.ts @@ -10,14 +10,14 @@ import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc import { toJS } from "../../common/utils"; import { isErrnoException } from "@k8slens/utilities"; import type { ExtensionLoader } from "../extension-loader"; -import type { InstalledExtension, LensExtensionId, LensExtensionManifest } from "@k8slens/legacy-extensions"; +import type { InstalledExtension, LensExtensionId, LensExtensionManifest, ExternalInstalledExtension } from "@k8slens/legacy-extensions"; import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store"; import { extensionDiscoveryStateChannel } from "../../common/ipc/extension-handling"; import { requestInitialExtensionDiscovery } from "../../renderer/ipc"; import type { ReadJson } from "../../common/fs/read-json-file.injectable"; import type { Logger } from "../../common/logger"; import type { PathExists } from "../../common/fs/path-exists.injectable"; -import type { Watch } from "../../common/fs/watch/watch.injectable"; +import type { Watch, Watcher } from "../../common/fs/watch/watch.injectable"; import type { Stats } from "fs"; import type { LStat } from "../../common/fs/lstat.injectable"; import type { ReadDirectory } from "../../common/fs/read-directory.injectable"; @@ -73,10 +73,6 @@ interface ExtensionDiscoveryChannelMessage { */ const isDirectoryLike = (lstat: Stats) => lstat.isDirectory() || lstat.isSymbolicLink(); -interface LoadFromFolderOptions { - isBundled?: boolean; -} - interface ExtensionDiscoveryEvents { add: (ext: InstalledExtension) => void; remove: (extId: LensExtensionId) => void; @@ -153,6 +149,8 @@ export class ExtensionDiscovery { }); } + private _watch: Watcher|undefined; + /** * Watches for added/removed local extensions. * Dependencies are installed automatically after an extension folder is copied. @@ -163,7 +161,7 @@ export class ExtensionDiscovery { // Wait until .load() has been called and has been resolved await this.whenLoaded; - this.dependencies.watch(this.localFolderPath, { + this._watch = this.dependencies.watch(this.localFolderPath, { // For adding and removing symlinks to work, the depth has to be 1. depth: 1, ignoreInitial: true, @@ -183,6 +181,12 @@ export class ExtensionDiscovery { .on("unlink", this.handleWatchUnlinkEvent); } + async stopWatchingExtensions() { + this.dependencies.logger.info(`${logModule} stopping the watch for extensions`); + + await this._watch?.close(); + } + handleWatchFileAdd = async (manifestPath: string): Promise => { // e.g. "foo/package.json" const relativePath = this.dependencies.getRelativePath(this.localFolderPath, manifestPath); @@ -271,7 +275,7 @@ export class ExtensionDiscovery { * @param extensionId The ID of the extension to uninstall. */ async uninstallExtension(extensionId: LensExtensionId): Promise { - const extension = this.extensions.get(extensionId) ?? this.dependencies.extensionLoader.getExtension(extensionId); + const extension = this.extensions.get(extensionId) ?? this.dependencies.extensionLoader.getExtensionById(extensionId); if (!extension) { return void this.dependencies.logger.warn(`${logModule} could not uninstall extension, not found`, { id: extensionId }); @@ -330,24 +334,26 @@ export class ExtensionDiscovery { * Returns InstalledExtension from path to package.json file. * Also updates this.packagesJson. */ - protected async getByManifest(manifestPath: string, { isBundled = false } = {}): Promise { + protected async loadExtensionFromFolder(folderPath: string): Promise { + const manifestPath = this.dependencies.joinPaths(folderPath, manifestFilename); + try { const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest; - const id = isBundled ? manifestPath : this.getInstalledManifestPath(manifest.name); - const isEnabled = this.dependencies.isExtensionEnabled({ id, isBundled }); + const id = this.getInstalledManifestPath(manifest.name); + const isEnabled = this.dependencies.isExtensionEnabled(id); const extensionDir = this.dependencies.getDirnameOfPath(manifestPath); const npmPackage = this.dependencies.joinPaths(extensionDir, `${manifest.name}-${manifest.version}.tgz`); const absolutePath = this.dependencies.isProduction && await this.dependencies.pathExists(npmPackage) ? npmPackage : extensionDir; - const isCompatible = isBundled || this.dependencies.isCompatibleExtension(manifest); + const isCompatible = this.dependencies.isCompatibleExtension(manifest); return { id, absolutePath, manifestPath: id, manifest, - isBundled, + isBundled: false, isEnabled, isCompatible, }; @@ -363,14 +369,14 @@ export class ExtensionDiscovery { } } - async ensureExtensions(): Promise> { + async ensureExtensions(): Promise> { const userExtensions = await this.loadFromFolder(this.localFolderPath); return this.extensions = new Map(userExtensions.map(extension => [extension.id, extension])); } - async loadFromFolder(folderPath: string): Promise { - const extensions: InstalledExtension[] = []; + async loadFromFolder(folderPath: string): Promise { + const extensions: ExternalInstalledExtension[] = []; const paths = await this.dependencies.readDirectory(folderPath); for (const fileName of paths) { @@ -403,16 +409,6 @@ export class ExtensionDiscovery { return extensions; } - /** - * Loads extension from absolute path, updates this.packagesJson to include it and returns the extension. - * @param folderPath Folder path to extension - */ - async loadExtensionFromFolder(folderPath: string, { isBundled = false }: LoadFromFolderOptions = {}): Promise { - const manifestPath = this.dependencies.joinPaths(folderPath, manifestFilename); - - return this.getByManifest(manifestPath, { isBundled }); - } - toJSON(): ExtensionDiscoveryChannelMessage { return toJS({ isLoaded: this.isLoaded, diff --git a/packages/core/src/extensions/extension-discovery/stop-watching-extensions-on-quit.injectable.ts b/packages/core/src/extensions/extension-discovery/stop-watching-extensions-on-quit.injectable.ts new file mode 100644 index 0000000000..a8e6eafb9e --- /dev/null +++ b/packages/core/src/extensions/extension-discovery/stop-watching-extensions-on-quit.injectable.ts @@ -0,0 +1,23 @@ +/** + * 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 { beforeQuitOfBackEndInjectionToken } from "../../main/start-main-application/runnable-tokens/phases"; +import extensionDiscoveryInjectable from "./extension-discovery.injectable"; + +const stopWatchingExtensionsOnQuitInjectable = getInjectable({ + id: "stop-watching-extensions-on-quit", + + instantiate: (di) => ({ + run: async () => { + const extensionDiscovery = di.inject(extensionDiscoveryInjectable); + + await extensionDiscovery.stopWatchingExtensions(); + }, + }), + + injectionToken: beforeQuitOfBackEndInjectionToken, +}); + +export default stopWatchingExtensionsOnQuitInjectable; diff --git a/packages/core/src/extensions/extension-loader/create-extension-instance.token.ts b/packages/core/src/extensions/extension-loader/create-extension-instance.token.ts index a064cf55ec..97ded49d84 100644 --- a/packages/core/src/extensions/extension-loader/create-extension-instance.token.ts +++ b/packages/core/src/extensions/extension-loader/create-extension-instance.token.ts @@ -3,11 +3,14 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { LensExtensionConstructor, InstalledExtension } from "@k8slens/legacy-extensions"; +import type { LensExtensionConstructor, BundledInstalledExtension, ExternalInstalledExtension, BundledLensExtensionConstructor } from "@k8slens/legacy-extensions"; import { getInjectionToken } from "@ogre-tools/injectable"; import type { LensExtension } from "../lens-extension"; -export type CreateExtensionInstance = (ExtensionClass: LensExtensionConstructor, extension: InstalledExtension) => LensExtension; +export interface CreateExtensionInstance { + (ExtensionClass: LensExtensionConstructor, extension: ExternalInstalledExtension): LensExtension; + (ExtensionClass: BundledLensExtensionConstructor, extension: BundledInstalledExtension): LensExtension; +} export const createExtensionInstanceInjectionToken = getInjectionToken({ id: "create-extension-instance-token", diff --git a/packages/core/src/extensions/extension-loader/extension-loader.ts b/packages/core/src/extensions/extension-loader/extension-loader.ts index 4a24ad23a1..3ba5a16728 100644 --- a/packages/core/src/extensions/extension-loader/extension-loader.ts +++ b/packages/core/src/extensions/extension-loader/extension-loader.ts @@ -6,9 +6,10 @@ import { ipcMain, ipcRenderer } from "electron"; import { isEqual } from "lodash"; import type { ObservableMap } from "mobx"; -import { action, computed, makeObservable, toJS, observable, observe, reaction, when } from "mobx"; +import { runInAction, action, computed, toJS, observable, reaction, when } from "mobx"; import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc"; -import { isDefined } from "@k8slens/utilities"; +import { isDefined, iter } from "@k8slens/utilities"; +import type { ExternalInstalledExtension, InstalledExtension, LensExtensionConstructor, LensExtensionId, BundledExtension } from "@k8slens/legacy-extensions"; import type { LensExtension } from "../lens-extension"; import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling"; import { requestExtensionLoaderInitialState } from "../../renderer/ipc"; @@ -19,7 +20,6 @@ import type { Extension } from "./extension/extension.injectable"; import type { Logger } from "../../common/logger"; import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; -import type { LensExtensionId, BundledExtension, InstalledExtension, LensExtensionConstructor } from "@k8slens/legacy-extensions"; import type { UpdateExtensionsState } from "../../features/extensions/enabled/common/update-state.injectable"; const logModule = "[EXTENSIONS-LOADER]"; @@ -60,51 +60,21 @@ export class ExtensionLoader { */ protected readonly nonInstancesByName = observable.set(); - /** - * This is updated by the `observe` in the constructor. DO NOT write directly to it - */ - protected readonly instancesByName = observable.map(); + protected readonly instancesByName = computed(() => new Map(( + iter.chain(this.dependencies.extensionInstances.entries()) + .map(([, instance]) => [instance.name, instance]) + ))); private readonly onRemoveExtensionId = new EventEmitter<[string]>(); - @observable isLoaded = false; + readonly isLoaded = observable.box(false); - get whenLoaded() { - return when(() => this.isLoaded); - } + constructor(protected readonly dependencies: Dependencies) {} - constructor(protected readonly dependencies: Dependencies) { - makeObservable(this); - - observe(this.dependencies.extensionInstances, change => { - switch (change.type) { - case "add": - if (this.instancesByName.has(change.newValue.name)) { - throw new TypeError("Extension names must be unique"); - } - - this.instancesByName.set(change.newValue.name, change.newValue); - break; - case "delete": - this.instancesByName.delete(change.oldValue.name); - break; - case "update": - throw new Error("Extension instances shouldn't be updated"); - } - }); - } - - @computed get userExtensions(): Map { - const extensions = this.toJSON(); - - extensions.forEach((ext, extId) => { - if (ext.isBundled) { - extensions.delete(extId); - } - }); - - return extensions; - } + readonly userExtensions = computed(() => new Map(( + this.extensions.toJSON() + .filter(([, extension]) => !extension.isBundled) + ))); /** * Get the extension instance by its manifest name @@ -120,19 +90,18 @@ export class ExtensionLoader { return null; } - return this.instancesByName.get(name); + return this.instancesByName.get().get(name); } // Transform userExtensions to a state object for storing into ExtensionsStore - @computed get storeState() { - return Array.from(this.userExtensions) - .map(([extId, extension]) => [extId, { - enabled: extension.isEnabled, - name: extension.manifest.name, - }] as const); - } + readonly storeState = computed(() => Array.from( + this.userExtensions.get(), + ([extId, extension]) => [extId, { + enabled: extension.isEnabled, + name: extension.manifest.name, + }] as const, + )); - @action async init() { if (ipcMain) { await this.initMain(); @@ -140,7 +109,7 @@ export class ExtensionLoader { await this.initRenderer(); } - await Promise.all([this.whenLoaded]); + await when(() => this.isLoaded.get()); // broadcasting extensions between main/renderer processes reaction(() => this.toJSON(), () => this.broadcastExtensions(), { @@ -148,8 +117,7 @@ export class ExtensionLoader { }); reaction( - () => this.storeState, - + () => this.storeState.get(), (state) => { this.dependencies.updateExtensionsState(state); }, @@ -199,18 +167,20 @@ export class ExtensionLoader { setIsEnabled(lensExtensionId: LensExtensionId, isEnabled: boolean) { const extension = this.extensions.get(lensExtensionId); - assert(extension, `Must register extension ${lensExtensionId} with before enabling it`); + assert(extension, `Extension "${lensExtensionId}" must be registered before it can be enabled.`); + assert(!extension.isBundled, `Cannot change the enabled state of a bundled extension`); extension.isEnabled = isEnabled; } protected async initMain() { - this.isLoaded = true; + runInAction(() => { + this.isLoaded.set(true); + }); + await this.autoInitExtensions(); - ipcMainHandle(extensionLoaderFromMainChannel, () => { - return Array.from(this.toJSON()); - }); + ipcMainHandle(extensionLoaderFromMainChannel, () => [...this.toJSON()]); ipcMainOn(extensionLoaderFromRendererChannel, (event, extensions: [LensExtensionId, InstalledExtension][]) => { this.syncExtensions(extensions); @@ -219,7 +189,9 @@ export class ExtensionLoader { protected async initRenderer() { const extensionListHandler = (extensions: [LensExtensionId, InstalledExtension][]) => { - this.isLoaded = true; + runInAction(() => { + this.isLoaded.set(true); + }); this.syncExtensions(extensions); const receivedExtensionIds = extensions.map(([lensExtensionId]) => lensExtensionId); @@ -255,10 +227,10 @@ export class ExtensionLoader { } protected async loadBundledExtensions() { - return this.dependencies.bundledExtensions - .map(extension => { + const bundledExtensions = await Promise.all((this.dependencies.bundledExtensions + .map(async extension => { try { - const LensExtensionClass = extension[this.dependencies.extensionEntryPointName](); + const LensExtensionClass = await extension[this.dependencies.extensionEntryPointName](); if (!LensExtensionClass) { return null; @@ -291,7 +263,9 @@ export class ExtensionLoader { return null; } }) - .filter(isDefined); + )); + + return bundledExtensions.filter(isDefined); } protected async loadExtensions(extensions: ExtensionBeingActivated[]): Promise { @@ -332,6 +306,7 @@ export class ExtensionLoader { // 4. Return ExtensionLoading[] return [...installedExtensions.entries()] + .filter((entry): entry is [string, ExternalInstalledExtension] => !entry[1].isBundled) .map(([extId, extension]) => { const alreadyInit = this.dependencies.extensionInstances.has(extId) || this.nonInstancesByName.has(extension.manifest.name); @@ -391,7 +366,7 @@ export class ExtensionLoader { return loadedExtensions; } - protected requireExtension(extension: InstalledExtension): LensExtensionConstructor | null { + protected requireExtension(extension: ExternalInstalledExtension): LensExtensionConstructor | null { const extRelativePath = extension.manifest[this.dependencies.extensionEntryPointName]; if (!extRelativePath) { @@ -411,7 +386,7 @@ export class ExtensionLoader { return null; } - getExtension(extId: LensExtensionId) { + getExtensionById(extId: LensExtensionId) { return this.extensions.get(extId); } diff --git a/packages/core/src/extensions/lens-extension.ts b/packages/core/src/extensions/lens-extension.ts index ec5eca0cd9..ea451ca56e 100644 --- a/packages/core/src/extensions/lens-extension.ts +++ b/packages/core/src/extensions/lens-extension.ts @@ -9,7 +9,6 @@ import type { LensExtensionDependencies } from "./lens-extension-set-dependencie import type { ProtocolHandlerRegistration } from "../common/protocol-handler/registration"; import type { InstalledExtension, LegacyLensExtension, LensExtensionId, LensExtensionManifest } from "@k8slens/legacy-extensions"; - export const lensExtensionDependencies = Symbol("lens-extension-dependencies"); export const Disposers = Symbol("disposers"); @@ -42,14 +41,12 @@ export class LensExtension< [Disposers] = disposer(); constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) { - makeObservable(this); - // id is the name of the manifest this.id = id; - - this.manifest = manifest; + this.manifest = manifest as LensExtensionManifest; this.manifestPath = manifestPath; this.isBundled = !!isBundled; + makeObservable(this); } get name() { diff --git a/packages/core/src/features/certificate-authorities/common/request-system-cas-token.ts b/packages/core/src/features/certificate-authorities/common/request-system-cas-token.ts index c69b0bd8b0..5c5f17ff64 100644 --- a/packages/core/src/features/certificate-authorities/common/request-system-cas-token.ts +++ b/packages/core/src/features/certificate-authorities/common/request-system-cas-token.ts @@ -4,7 +4,14 @@ */ import { getInjectionToken } from "@ogre-tools/injectable"; +import type { PlatformSpecific } from "../../../common/utils/platform-specific-version.injectable"; -export const requestSystemCAsInjectionToken = getInjectionToken<() => Promise>({ +export type RequestSystemCAs = () => Promise; + +export const platformSpecificRequestSystemCAsInjectionToken = getInjectionToken>({ + id: "platform-specific-request-system-cas-token", +}); + +export const requestSystemCAsInjectionToken = getInjectionToken({ id: "request-system-cas-token", }); diff --git a/packages/core/src/features/certificate-authorities/main/darwin-request-system-cas.injectable.ts b/packages/core/src/features/certificate-authorities/main/darwin-request-system-cas.injectable.ts new file mode 100644 index 0000000000..9a798123e7 --- /dev/null +++ b/packages/core/src/features/certificate-authorities/main/darwin-request-system-cas.injectable.ts @@ -0,0 +1,60 @@ +/** + * 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 execFileInjectable from "../../../common/fs/exec-file.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { AsyncResult } from "@k8slens/utilities"; +import { platformSpecificRequestSystemCAsInjectionToken } from "../common/request-system-cas-token"; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions +const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; + +const darwinRequestSystemCAsInjectable = getInjectable({ + id: "darwin-request-system-cas", + instantiate: (di) => ({ + platform: "darwin" as const, + instantiate: () => { + const execFile = di.inject(execFileInjectable); + const logger = di.inject(loggerInjectable); + + const execSecurity = async (...args: string[]): AsyncResult => { + const result = await execFile("/usr/bin/security", args); + + if (!result.callWasSuccessful) { + return { + callWasSuccessful: false, + error: result.error.stderr || result.error.message, + }; + } + + return { + callWasSuccessful: true, + response: result.response.split(certSplitPattern), + }; + }; + + return async () => { + const [trustedResult, rootCAResult] = await Promise.all([ + execSecurity("find-certificate", "-a", "-p"), + execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"), + ]); + + if (!trustedResult.callWasSuccessful) { + logger.warn(`[INJECT-CAS]: Error retrieving trusted CAs: ${trustedResult.error}`); + } else if (!rootCAResult.callWasSuccessful) { + logger.warn(`[INJECT-CAS]: Error retrieving root CAs: ${rootCAResult.error}`); + } else { + return [...new Set([...trustedResult.response, ...rootCAResult.response])]; + } + + return []; + }; + }, + }), + causesSideEffects: true, + injectionToken: platformSpecificRequestSystemCAsInjectionToken, +}); + +export default darwinRequestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/linux-request-system-cas.injectable.ts b/packages/core/src/features/certificate-authorities/main/linux-request-system-cas.injectable.ts new file mode 100644 index 0000000000..520830fdf8 --- /dev/null +++ b/packages/core/src/features/certificate-authorities/main/linux-request-system-cas.injectable.ts @@ -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 { platformSpecificRequestSystemCAsInjectionToken } from "../common/request-system-cas-token"; + +const linuxRequestSystemCAsInjectable = getInjectable({ + id: "linux-request-system-cas", + instantiate: () => ({ + platform: "linux" as const, + instantiate: () => async () => [], + }), + injectionToken: platformSpecificRequestSystemCAsInjectionToken, +}); + +export default linuxRequestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/request-system-cas.global-override-for-injectable.ts b/packages/core/src/features/certificate-authorities/main/request-system-cas.global-override-for-injectable.ts new file mode 100644 index 0000000000..670f150b81 --- /dev/null +++ b/packages/core/src/features/certificate-authorities/main/request-system-cas.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverride } from "@k8slens/test-utils"; +import requestSystemCAsInjectable from "./request-system-cas.injectable"; + +export default getGlobalOverride(requestSystemCAsInjectable, () => async () => []); diff --git a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.darwin.ts b/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.darwin.ts deleted file mode 100644 index 66c24a1235..0000000000 --- a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.darwin.ts +++ /dev/null @@ -1,57 +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 execFileInjectable from "../../../common/fs/exec-file.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import type { AsyncResult } from "@k8slens/utilities"; -import { requestSystemCAsInjectionToken } from "../common/request-system-cas-token"; - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet#other_assertions -const certSplitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: (di) => { - const execFile = di.inject(execFileInjectable); - const logger = di.inject(loggerInjectable); - - const execSecurity = async (...args: string[]): AsyncResult => { - const result = await execFile("/usr/bin/security", args); - - if (!result.callWasSuccessful) { - return { - callWasSuccessful: false, - error: result.error.stderr || result.error.message, - }; - } - - return { - callWasSuccessful: true, - response: result.response.split(certSplitPattern), - }; - }; - - return async () => { - const [trustedResult, rootCAResult] = await Promise.all([ - execSecurity("find-certificate", "-a", "-p"), - execSecurity("find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain"), - ]); - - if (!trustedResult.callWasSuccessful) { - logger.warn(`[INJECT-CAS]: Error retreiving trusted CAs: ${trustedResult.error}`); - } else if (!rootCAResult.callWasSuccessful) { - logger.warn(`[INJECT-CAS]: Error retreiving root CAs: ${rootCAResult.error}`); - } else { - return [...new Set([...trustedResult.response, ...rootCAResult.response])]; - } - - return []; - }; - }, - causesSideEffects: true, - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.linux.ts b/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.linux.ts deleted file mode 100644 index cffd0d172a..0000000000 --- a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.linux.ts +++ /dev/null @@ -1,14 +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 { requestSystemCAsInjectionToken } from "../common/request-system-cas-token"; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: () => async () => [], - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.testing-env.ts b/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.testing-env.ts deleted file mode 100644 index cffd0d172a..0000000000 --- a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.testing-env.ts +++ /dev/null @@ -1,14 +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 { requestSystemCAsInjectionToken } from "../common/request-system-cas-token"; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: () => async () => [], - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.ts b/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.ts new file mode 100644 index 0000000000..f17f538529 --- /dev/null +++ b/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.ts @@ -0,0 +1,20 @@ +/** + * 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 platformSpecificVersionInjectable from "../../../common/utils/platform-specific-version.injectable"; +import { platformSpecificRequestSystemCAsInjectionToken, requestSystemCAsInjectionToken } from "../common/request-system-cas-token"; + +const requestSystemCAsInjectable = getInjectable({ + id: "request-system-cas", + instantiate: (di) => { + const platformSpecificVersion = di.inject(platformSpecificVersionInjectable); + const platformSpecificRequestSystemCAs = platformSpecificVersion(platformSpecificRequestSystemCAsInjectionToken); + + return platformSpecificRequestSystemCAs ?? (async () => []); + }, + injectionToken: requestSystemCAsInjectionToken, +}); + +export default requestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.win32.ts b/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.win32.ts deleted file mode 100644 index 9e5d5d8caf..0000000000 --- a/packages/core/src/features/certificate-authorities/main/request-system-cas.injectable.win32.ts +++ /dev/null @@ -1,56 +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 execFileInjectable from "../../../common/fs/exec-file.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import { requestSystemCAsInjectionToken } from "../common/request-system-cas-token"; - -const pemEncoding = (hexEncodedCert: String) => { - const certData = Buffer.from(hexEncodedCert, "hex").toString("base64"); - const lines = ["-----BEGIN CERTIFICATE-----"]; - - for (let i = 0; i < certData.length; i += 64) { - lines.push(certData.substring(i, i + 64)); - } - - lines.push("-----END CERTIFICATE-----", ""); - - return lines.join("\r\n"); -}; - -const requestSystemCAsInjectable = getInjectable({ - id: "request-system-cas", - instantiate: (di) => { - const wincaRootsExePath: string = __non_webpack_require__.resolve("win-ca/lib/roots.exe"); - const execFile = di.inject(execFileInjectable); - const logger = di.inject(loggerInjectable); - - return async () => { - /** - * This needs to be done manually because for some reason calling the api from "win-ca" - * directly fails to load "child_process" correctly on renderer - */ - const result = await execFile(wincaRootsExePath, { - maxBuffer: 128 * 1024 * 1024, // 128 MiB - }); - - if (!result.callWasSuccessful) { - logger.warn(`[INJECT-CAS]: Error retreiving CAs`, result.error); - - return []; - } - - return result - .response - .split("\r\n") - .filter(Boolean) - .map(pemEncoding); - }; - }, - causesSideEffects: true, - injectionToken: requestSystemCAsInjectionToken, -}); - -export default requestSystemCAsInjectable; diff --git a/packages/core/src/features/certificate-authorities/main/win32-request-system-cas.injectable.ts b/packages/core/src/features/certificate-authorities/main/win32-request-system-cas.injectable.ts new file mode 100644 index 0000000000..2c4cc6f906 --- /dev/null +++ b/packages/core/src/features/certificate-authorities/main/win32-request-system-cas.injectable.ts @@ -0,0 +1,59 @@ +/** + * 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 execFileInjectable from "../../../common/fs/exec-file.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import { platformSpecificRequestSystemCAsInjectionToken } from "../common/request-system-cas-token"; + +const pemEncoding = (hexEncodedCert: String) => { + const certData = Buffer.from(hexEncodedCert, "hex").toString("base64"); + const lines = ["-----BEGIN CERTIFICATE-----"]; + + for (let i = 0; i < certData.length; i += 64) { + lines.push(certData.substring(i, i + 64)); + } + + lines.push("-----END CERTIFICATE-----", ""); + + return lines.join("\r\n"); +}; + +const win32RequestSystemCAsInjectable = getInjectable({ + id: "win32-request-system-cas", + instantiate: (di) => ({ + platform: "win32" as const, + instantiate: () => { + const winCARootsExePath: string = __non_webpack_require__.resolve("win-ca/lib/roots.exe"); + const execFile = di.inject(execFileInjectable); + const logger = di.inject(loggerInjectable); + + return async () => { + /** + * This needs to be done manually because for some reason calling the api from "win-ca" + * directly fails to load "child_process" correctly on renderer + */ + const result = await execFile(winCARootsExePath, { + maxBuffer: 128 * 1024 * 1024, // 128 MiB + }); + + if (!result.callWasSuccessful) { + logger.warn(`[INJECT-CAS]: Error retrieving CAs`, result.error); + + return []; + } + + return result + .response + .split("\r\n") + .filter(Boolean) + .map(pemEncoding); + }; + }, + }), + causesSideEffects: true, + injectionToken: platformSpecificRequestSystemCAsInjectionToken, +}); + +export default win32RequestSystemCAsInjectable; diff --git a/packages/core/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap b/packages/core/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap index 832ee400c8..266fef88c4 100644 --- a/packages/core/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap +++ b/packages/core/src/features/cluster/namespaces/__snapshots__/edit-namespace-from-new-tab.test.tsx.snap @@ -7469,6 +7469,8 @@ metadata: selfLink: /apis/some-api-version/namespaces/some-uid somePropertyToBeRemoved: some-value somePropertyToBeChanged: some-old-value + labels: + k8slens-edit-resource-version: some-api-version diff --git a/packages/core/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx b/packages/core/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx index 96ced30199..8480da45e3 100644 --- a/packages/core/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx +++ b/packages/core/src/features/cluster/namespaces/edit-namespace-from-new-tab.test.tsx @@ -12,8 +12,6 @@ import createEditResourceTabInjectable from "../../../renderer/components/dock/e import getRandomIdForEditResourceTabInjectable from "../../../renderer/components/dock/edit-resource/get-random-id-for-edit-resource-tab.injectable"; import type { AsyncFnMock } from "@async-fn/jest"; import asyncFn from "@async-fn/jest"; -import type { CallForResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable"; -import callForResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable"; import type { CallForPatchResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable"; import callForPatchResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-patch-resource/call-for-patch-resource.injectable"; import dockStoreInjectable from "../../../renderer/components/dock/dock/store.injectable"; @@ -23,6 +21,8 @@ import showErrorNotificationInjectable from "../../../renderer/components/notifi import readJsonFileInjectable from "../../../common/fs/read-json-file.injectable"; import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; import hostedClusterIdInjectable from "../../../renderer/cluster-frame-context/hosted-cluster-id.injectable"; +import type { CallForResource } from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable"; +import callForResourceInjectable from "../../../renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable"; describe("cluster/namespaces - edit namespace from new tab", () => { let builder: ApplicationBuilder; @@ -225,10 +225,16 @@ metadata: expect(rendered.baseElement).toMatchSnapshot(); }); - it("calls for save with empty values", () => { + it("calls for save with just the adding version label", () => { expect(callForPatchResourceMock).toHaveBeenCalledWith( someNamespace, - [], + [{ + op: "add", + path: "/metadata/labels", + value: { + "k8slens-edit-resource-version": "some-api-version", + }, + }], ); }); @@ -532,6 +538,13 @@ metadata: path: "/metadata/someAddedProperty", value: "some-new-value", }, + { + op: "add", + path: "/metadata/labels", + value: { + "k8slens-edit-resource-version": "some-api-version", + }, + }, { op: "replace", path: "/metadata/somePropertyToBeChanged", @@ -759,7 +772,7 @@ metadata: `); }); - it("when selecting to save, calls for save of second namespace", () => { + it("when selecting to save, calls for save of second namespace with just the add edit version label", () => { callForPatchResourceMock.mockClear(); const saveButton = rendered.getByTestId( @@ -770,7 +783,13 @@ metadata: expect(callForPatchResourceMock).toHaveBeenCalledWith( someOtherNamespace, - [], + [{ + op: "add", + path: "/metadata/labels", + value: { + "k8slens-edit-resource-version": "some-api-version", + }, + }], ); }); @@ -826,7 +845,7 @@ metadata: `); }); - it("when selecting to save, calls for save of first namespace", () => { + it("when selecting to save, calls for save of first namespace with just the new edit version label", () => { callForPatchResourceMock.mockClear(); const saveButton = rendered.getByTestId( @@ -837,7 +856,13 @@ metadata: expect(callForPatchResourceMock).toHaveBeenCalledWith( someNamespace, - [], + [ { + op: "add", + path: "/metadata/labels", + value: { + "k8slens-edit-resource-version": "some-api-version", + }, + }], ); }); }); diff --git a/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap b/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap index dbaf12b978..4fb639ff2b 100644 --- a/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -376,7 +376,7 @@ exports[`extensions - navigation using application menu when navigating to exten

Add new features via Lens Extensions. Check out the diff --git a/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts b/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts index bb7f531cb3..f2f6114a1f 100644 --- a/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts +++ b/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts @@ -5,19 +5,14 @@ import { getInjectable } from "@ogre-tools/injectable"; import enabledExtensionsStateInjectable from "./state.injectable"; -export interface IsEnabledExtensionDescriptor { - readonly id: string; - readonly isBundled: boolean; -} - -export type IsExtensionEnabled = (desc: IsEnabledExtensionDescriptor) => boolean; +export type IsExtensionEnabled = (id: string) => boolean; const isExtensionEnabledInjectable = getInjectable({ id: "is-extension-enabled", instantiate: (di): IsExtensionEnabled => { const state = di.inject(enabledExtensionsStateInjectable); - return ({ id, isBundled }) => isBundled || (state.get(id)?.enabled ?? false); + return (id) => (state.get(id)?.enabled ?? false); }, }); diff --git a/packages/core/src/jest.setup.ts b/packages/core/src/jest.setup.ts index 4e9b86ba22..96a4b816d4 100644 --- a/packages/core/src/jest.setup.ts +++ b/packages/core/src/jest.setup.ts @@ -58,7 +58,7 @@ jest.mock("./renderer/components/tooltip/withTooltip"); jest.mock("monaco-editor"); const getInjectables = (environment: "renderer" | "main", filePathGlob: string) => [ - ...glob.sync(`./{common,extensions,${environment}}/**/${filePathGlob}`, { + ...glob.sync(`./{common,extensions,${environment},test-env}/**/${filePathGlob}`, { cwd: __dirname, }), @@ -70,10 +70,10 @@ const getInjectables = (environment: "renderer" | "main", filePathGlob: string) global.injectablePaths = { renderer: { globalOverridePaths: getInjectables("renderer", "*.global-override-for-injectable.{ts,tsx}"), - paths: getInjectables("renderer", "*.{injectable,injectable.testing-env}.{ts,tsx}"), + paths: getInjectables("renderer", "*.injectable.{ts,tsx}"), }, main: { globalOverridePaths: getInjectables("main", "*.global-override-for-injectable.{ts,tsx}"), - paths: getInjectables("main", "*.{injectable,injectable.testing-env}.{ts,tsx}"), + paths: getInjectables("main", "*.injectable.{ts,tsx}"), }, }; diff --git a/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts b/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts index c7b53d8973..cecf52e4c1 100644 --- a/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts +++ b/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts @@ -98,11 +98,19 @@ export class KubeconfigSyncManager { @action protected stopOldSync(filePath: string): void { - if (!this.sources.delete(filePath)) { - // already stopped + const source = this.sources.get(filePath); + + // already stopped + if (!source) { return this.dependencies.logger.debug(`no syncing file/folder to stop`, { filePath }); } + const [, disposer] = source; + + disposer(); + + this.sources.delete(filePath); + this.dependencies.logger.info(`stopping sync of file/folder`, { filePath }); this.dependencies.logger.debug(`${this.sources.size} files/folders watched`, { files: Array.from(this.sources.keys()) }); } diff --git a/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts b/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts index cd20ab7ede..d0fe1e25c3 100644 --- a/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts +++ b/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts @@ -24,7 +24,7 @@ const createExtensionInstanceInjectable = getInjectable({ }; return (ExtensionClass, extension) => { - const instance = new ExtensionClass(extension) as LensMainExtension; + const instance = new ExtensionClass(extension as any) as LensMainExtension; (instance as Writable)[lensExtensionDependencies] = deps; diff --git a/packages/core/src/main/protocol-handler/__test__/router.test.ts b/packages/core/src/main/protocol-handler/__test__/router.test.ts index e3eddb6b34..11e49dc113 100644 --- a/packages/core/src/main/protocol-handler/__test__/router.test.ts +++ b/packages/core/src/main/protocol-handler/__test__/router.test.ts @@ -10,16 +10,12 @@ import { noop } from "@k8slens/utilities"; import type { LensProtocolRouterMain } from "../lens-protocol-router-main/lens-protocol-router-main"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import lensProtocolRouterMainInjectable from "../lens-protocol-router-main/lens-protocol-router-main.injectable"; -import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import { LensExtension } from "../../../extensions/lens-extension"; import type { ObservableMap } from "mobx"; +import { runInAction } from "mobx"; import extensionInstancesInjectable from "../../../extensions/extension-loader/extension-instances.injectable"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable"; -import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable"; -import pathExistsInjectable from "../../../common/fs/path-exists.injectable"; -import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; -import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable"; import type { LensExtensionId } from "@k8slens/legacy-extensions"; import type { LensExtensionState } from "../../../features/extensions/enabled/common/state.injectable"; import enabledExtensionsStateInjectable from "../../../features/extensions/enabled/common/state.injectable"; @@ -39,16 +35,8 @@ describe("protocol router tests", () => { beforeEach(async () => { const di = getDiForUnitTesting(); - di.override(pathExistsInjectable, () => () => { throw new Error("tried call pathExists without override"); }); - di.override(pathExistsSyncInjectable, () => () => { throw new Error("tried call pathExistsSync without override"); }); - di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); }); - di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); }); - enabledExtensions = di.inject(enabledExtensionsStateInjectable); - - di.permitSideEffects(getConfigurationFileModelInjectable); - - di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); broadcastMessageMock = jest.fn(); di.override(broadcastMessageInjectable, () => broadcastMessageMock); @@ -56,7 +44,9 @@ describe("protocol router tests", () => { extensionInstances = di.inject(extensionInstancesInjectable); lpr = di.inject(lensProtocolRouterMainInjectable); - lpr.rendererLoaded = true; + runInAction(() => { + lpr.rendererLoaded.set(true); + }); }); it("should broadcast invalid protocol on non-lens URLs", async () => { @@ -69,7 +59,19 @@ describe("protocol router tests", () => { expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerInvalid, "invalid host", "lens://foobar"); }); - it("should not throw when has valid host", async () => { + it("should broadcast internal route when called with valid host", async () => { + lpr.addInternalHandler("/", noop); + + try { + expect(await lpr.route("lens://app")).toBeUndefined(); + } catch (error) { + expect(throwIfDefined(error)).not.toThrow(); + } + + expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched"); + }); + + it("should broadcast external route when called with valid host", async () => { const extId = uuid.v4(); const ext = new LensExtension({ id: extId, diff --git a/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts b/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts index 8185ca4e68..1018420b10 100644 --- a/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts +++ b/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts @@ -6,7 +6,7 @@ import * as proto from "../../../common/protocol-handler"; import URLParse from "url-parse"; import type { LensExtension } from "../../../extensions/lens-extension"; -import { observable, when, makeObservable } from "mobx"; +import { observable, when } from "mobx"; import type { LensProtocolRouterDependencies, RouteAttempt } from "../../../common/protocol-handler"; import { ProtocolHandlerInvalid } from "../../../common/protocol-handler"; import { disposer, noop } from "@k8slens/utilities"; @@ -39,17 +39,15 @@ export interface LensProtocolRouterMainDependencies extends LensProtocolRouterDe } export class LensProtocolRouterMain extends proto.LensProtocolRouter { - private missingExtensionHandlers: FallbackHandler[] = []; + private readonly missingExtensionHandlers: FallbackHandler[] = []; // TODO: This is used to solve out-of-place temporal dependency. Remove, and solve otherwise. - @observable rendererLoaded = false; + readonly rendererLoaded = observable.box(false); - protected disposers = disposer(); + protected readonly disposers = disposer(); constructor(protected readonly dependencies: LensProtocolRouterMainDependencies) { super(dependencies); - - makeObservable(this); } public cleanup() { @@ -118,13 +116,12 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter { protected _routeToInternal(url: URLParse>): RouteAttempt { const rawUrl = url.toString(); // for sending to renderer const attempt = super._routeToInternal(url); + const broadcastToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerInternal, rawUrl, attempt); - const sendRoutingToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerInternal, rawUrl, attempt); - - if (this.rendererLoaded) { - sendRoutingToRenderer(); + if (this.rendererLoaded.get()) { + broadcastToRenderer(); } else { - this.disposers.push(when(() => this.rendererLoaded, sendRoutingToRenderer)); + this.disposers.push(when(() => this.rendererLoaded.get(), broadcastToRenderer)); } return attempt; @@ -141,13 +138,12 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter { * argument. */ const attempt = await super._routeToExtension(new URLParse(url.toString(), true)); + const broadcastToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt); - const sendRoutingToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt); - - if (this.rendererLoaded) { - sendRoutingToRenderer(); + if (this.rendererLoaded.get()) { + broadcastToRenderer(); } else { - this.disposers.push(when(() => this.rendererLoaded, sendRoutingToRenderer)); + this.disposers.push(when(() => this.rendererLoaded.get(), broadcastToRenderer)); } return attempt; diff --git a/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-loaded.injectable.ts b/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-loaded.injectable.ts index 2fe71ed7d5..3096c5587f 100644 --- a/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-loaded.injectable.ts +++ b/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-loaded.injectable.ts @@ -16,7 +16,7 @@ const flagRendererAsLoadedInjectable = getInjectable({ runInAction(() => { // Todo: remove this kludge which enables out-of-place temporal dependency. - lensProtocolRouterMain.rendererLoaded = true; + lensProtocolRouterMain.rendererLoaded.set(true); }); }, }), diff --git a/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-not-loaded.injectable.ts b/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-not-loaded.injectable.ts index 899b234f39..078cc7def0 100644 --- a/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-not-loaded.injectable.ts +++ b/packages/core/src/main/start-main-application/runnables/flag-renderer/flag-renderer-as-not-loaded.injectable.ts @@ -16,7 +16,7 @@ const flagRendererAsNotLoadedInjectable = getInjectable({ runInAction(() => { // Todo: remove this kludge which enables out-of-place temporal dependency. - lensProtocolRouterMain.rendererLoaded = false; + lensProtocolRouterMain.rendererLoaded.set(false); }); return undefined; diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/__snapshots__/hpa-details.test.tsx.snap b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/__snapshots__/details.test.tsx.snap similarity index 100% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/__snapshots__/hpa-details.test.tsx.snap rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/__snapshots__/details.test.tsx.snap diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.scss b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.scss similarity index 99% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.scss rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.scss index 571d988b11..d422451583 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.scss +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.scss @@ -16,6 +16,7 @@ .TableCell { word-break: break-word; + &:first-child { margin-left: $margin * 2; } diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.test.tsx similarity index 92% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.test.tsx index 3190be6edd..79f96c28cb 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.test.tsx @@ -13,7 +13,7 @@ import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable"; import type { DiRender } from "../test-utils/renderFor"; import { renderFor } from "../test-utils/renderFor"; -import { HpaDetails } from "./hpa-details"; +import { HorizontalPodAutoscalerDetails } from "./details"; jest.mock("react-router-dom", () => ({ Link: ({ children }: { children: React.ReactNode }) => children, @@ -62,7 +62,7 @@ describe("", () => { const hpa = new HorizontalPodAutoscaler(hpaV2); result = render( - , + , ); expect(result.baseElement).toMatchSnapshot(); @@ -72,7 +72,7 @@ describe("", () => { const hpa = new HorizontalPodAutoscaler(hpaV2); result = render( - , + , ); expect(result.queryByTestId("hpa-metrics")).toBeNull(); @@ -101,7 +101,7 @@ describe("", () => { }); result = render( - , + , ); expect(result.getByText("CPU Utilization percentage")).toBeInTheDocument(); @@ -131,7 +131,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument(); @@ -160,7 +160,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("Resource cpu on Pods")).toBeInTheDocument(); @@ -191,7 +191,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument(); @@ -216,7 +216,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("packets-per-second on Pods")).toBeInTheDocument(); @@ -252,7 +252,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText(/requests-per-second/)).toHaveTextContent("requests-per-second onService/nginx"); @@ -277,7 +277,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("requests-per-second")).toBeInTheDocument(); @@ -311,7 +311,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument(); @@ -339,7 +339,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.getByText("queue_messages_ready on {\"matchLabels\":{\"queue\":\"worker_tasks\"}}")).toBeInTheDocument(); @@ -368,7 +368,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.baseElement).toMatchSnapshot(); @@ -398,7 +398,7 @@ describe("", () => { ); result = render( - , + , ); expect(result.baseElement).toMatchSnapshot(); diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.tsx b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.tsx similarity index 93% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.tsx rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.tsx index d57c8a3323..f4d675fa3d 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.tsx +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/details.tsx @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./hpa-details.scss"; +import "./details.scss"; import React from "react"; import { observer } from "mobx-react"; @@ -22,8 +22,8 @@ import { withInjectables } from "@ogre-tools/injectable-react"; import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable"; import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable"; import loggerInjectable from "../../../common/logger.injectable"; -import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; -import { getMetricName } from "./get-hpa-metric-name"; +import getHorizontalPodAutoscalerMetrics from "./get-metrics.injectable"; +import { getMetricName } from "./get-metric-name"; export interface HpaDetailsProps extends KubeObjectDetailsProps { } @@ -36,7 +36,7 @@ interface Dependencies { } @observer -class NonInjectedHpaDetails extends React.Component { +class NonInjectedHorizontalPodAutoscalerDetails extends React.Component { private renderTargetLink(target: HorizontalPodAutoscalerMetricTarget | undefined) { if (!target) { return null; @@ -177,7 +177,7 @@ class NonInjectedHpaDetails extends React.Component(NonInjectedHpaDetails, { +export const HorizontalPodAutoscalerDetails = withInjectables(NonInjectedHorizontalPodAutoscalerDetails, { getProps: (di, props) => ({ ...props, apiManager: di.inject(apiManagerInjectable), diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-hpa-metric-name.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-metric-name.ts similarity index 100% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-hpa-metric-name.ts rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-metric-name.ts diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-hpa-metrics.injectable.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-metrics.injectable.ts similarity index 93% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-hpa-metrics.injectable.ts rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-metrics.injectable.ts index ac524f0cd5..8872fbf5fd 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-hpa-metrics.injectable.ts +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/get-metrics.injectable.ts @@ -5,9 +5,9 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { HorizontalPodAutoscaler, HorizontalPodAutoscalerMetricSpec, HorizontalPodAutoscalerMetricStatus } from "../../../common/k8s-api/endpoints"; import { HpaMetricType } from "../../../common/k8s-api/endpoints"; -import { getMetricName } from "./get-hpa-metric-name"; -import { HorizontalPodAutoscalerV1MetricParser } from "./hpa-v1-metric-parser"; -import { HorizontalPodAutoscalerV2MetricParser } from "./hpa-v2-metric-parser"; +import { getMetricName } from "./get-metric-name"; +import { HorizontalPodAutoscalerV1MetricParser } from "./metric-parser-v1"; +import { HorizontalPodAutoscalerV2MetricParser } from "./metric-parser-v2"; type Parser = HorizontalPodAutoscalerV1MetricParser | HorizontalPodAutoscalerV2MetricParser; diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/index.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/index.ts index a0b5231b2e..73bb54532e 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/index.ts +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/index.ts @@ -3,5 +3,5 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -export * from "./hpa"; -export * from "./hpa-details"; +export * from "./list-view"; +export * from "./details"; diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa.scss b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/list-view.scss similarity index 100% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa.scss rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/list-view.scss diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa.tsx b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/list-view.tsx similarity index 97% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa.tsx rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/list-view.tsx index 68ee6671d7..ae536410e3 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa.tsx +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/list-view.tsx @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import "./hpa.scss"; +import "./list-view.scss"; import React from "react"; import { observer } from "mobx-react"; @@ -17,7 +17,7 @@ import { KubeObjectAge } from "../kube-object/age"; import type { HorizontalPodAutoscalerStore } from "./store"; import { withInjectables } from "@ogre-tools/injectable-react"; import horizontalPodAutoscalerStoreInjectable from "./store.injectable"; -import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; +import getHorizontalPodAutoscalerMetrics from "./get-metrics.injectable"; import { NamespaceSelectBadge } from "../+namespaces/namespace-select-badge"; enum columnId { diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-v1-metric-parser.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser-v1.ts similarity index 100% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-v1-metric-parser.ts rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser-v1.ts diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-v2-metric-parser.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser-v2.ts similarity index 100% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-v2-metric-parser.ts rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser-v2.ts diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-autoscaler-metrics.test.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser.test.ts similarity index 99% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-autoscaler-metrics.test.ts rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser.test.ts index 5f1c1d7183..2168da52ad 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-autoscaler-metrics.test.ts +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/metric-parser.test.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import type { DiContainer } from "@ogre-tools/injectable"; -import getHorizontalPodAutoscalerMetrics from "./get-hpa-metrics.injectable"; +import getHorizontalPodAutoscalerMetrics from "./get-metrics.injectable"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import { HorizontalPodAutoscaler, HpaMetricType } from "../../../common/k8s-api/endpoints"; @@ -658,10 +658,10 @@ describe("getHorizontalPodAutoscalerMetrics", () => { ], }, }); - + expect(getMetrics(hpa)[0]).toEqual("10% / 50%"); }); - + it("should return correct resource metrics with current value", () => { const hpa = new HorizontalPodAutoscaler({ ...hpaV2Beta1, @@ -691,7 +691,7 @@ describe("getHorizontalPodAutoscalerMetrics", () => { ], }, }); - + expect(getMetrics(hpa)[0]).toEqual("500m / 100m"); }); @@ -787,7 +787,7 @@ describe("getHorizontalPodAutoscalerMetrics", () => { type: HpaMetricType.Pods, pods: { metricName: "packets-per-second", - + targetAverageValue: "1k", }, }, @@ -1038,7 +1038,7 @@ describe("getHorizontalPodAutoscalerMetrics", () => { ], }, }); - + expect(getMetrics(hpa)[0]).toEqual("unknown / 50%"); }); }); diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-autoscalers-route-component.injectable.ts b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/route-component.injectable.ts similarity index 94% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-autoscalers-route-component.injectable.ts rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/route-component.injectable.ts index 991f6cab1b..a8b81ae0b1 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-autoscalers-route-component.injectable.ts +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/route-component.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { routeSpecificComponentInjectionToken } from "../../routes/route-specific-component-injection-token"; import horizontalPodAutoscalersRouteInjectable from "../../../common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable"; -import { HorizontalPodAutoscalers } from "./hpa"; +import { HorizontalPodAutoscalers } from "./list-view"; const horizontalPodAutoscalersRouteComponentInjectable = getInjectable({ id: "horizontal-pod-autoscalers-route-component", diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-auto-scalers-sidebar-items.injectable.tsx b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/sidebar-items.injectable.tsx similarity index 100% rename from packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/horizontal-pod-auto-scalers-sidebar-items.injectable.tsx rename to packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/sidebar-items.injectable.tsx diff --git a/packages/core/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx b/packages/core/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx index 2f4f36bc34..053892d7ab 100644 --- a/packages/core/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx +++ b/packages/core/src/renderer/components/+extensions/attempt-install/attempt-install.injectable.tsx @@ -86,7 +86,7 @@ const attemptInstall = ({ } const extensionFolder = getExtensionDestFolder(name); - const installedExtension = extensionLoader.getExtension(validatedRequest.id); + const installedExtension = extensionLoader.getExtensionById(validatedRequest.id); if (installedExtension) { const { version: oldVersion } = installedExtension.manifest; diff --git a/packages/core/src/renderer/components/+extensions/attempt-install/unpack-extension.injectable.tsx b/packages/core/src/renderer/components/+extensions/attempt-install/unpack-extension.injectable.tsx index 5317fbfc48..47dd5e02cf 100644 --- a/packages/core/src/renderer/components/+extensions/attempt-install/unpack-extension.injectable.tsx +++ b/packages/core/src/renderer/components/+extensions/attempt-install/unpack-extension.injectable.tsx @@ -73,7 +73,7 @@ const unpackExtensionInjectable = getInjectable({ await fse.move(unpackedRootFolder, extensionFolder, { overwrite: true }); // wait for the loader has actually install it - await when(() => extensionLoader.userExtensions.has(id)); + await when(() => extensionLoader.userExtensions.get().has(id)); // Enable installed extensions by default. extensionLoader.setIsEnabled(id, true); diff --git a/packages/core/src/renderer/components/+extensions/disable-extension.injectable.ts b/packages/core/src/renderer/components/+extensions/disable-extension.injectable.ts new file mode 100644 index 0000000000..b2bf76df3e --- /dev/null +++ b/packages/core/src/renderer/components/+extensions/disable-extension.injectable.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { LensExtensionId } from "@k8slens/legacy-extensions"; +import { getInjectable } from "@ogre-tools/injectable"; +import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; + +export type DisableExtension = (extId: LensExtensionId) => void; + +const disableExtensionInjectable = getInjectable({ + id: "disable-extension", + + instantiate: (di): DisableExtension => { + const extensionLoader = di.inject(extensionLoaderInjectable); + + return (extId) => { + const ext = extensionLoader.getExtensionById(extId); + + if (ext && !ext.isBundled) { + ext.isEnabled = false; + } + }; + }, +}); + +export default disableExtensionInjectable; diff --git a/packages/core/src/renderer/components/+extensions/disable-extension/disable-extension.injectable.ts b/packages/core/src/renderer/components/+extensions/disable-extension/disable-extension.injectable.ts deleted file mode 100644 index 274679c331..0000000000 --- a/packages/core/src/renderer/components/+extensions/disable-extension/disable-extension.injectable.ts +++ /dev/null @@ -1,18 +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 extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable"; -import { disableExtension } from "./disable-extension"; - -const disableExtensionInjectable = getInjectable({ - id: "disable-extension", - - instantiate: (di) => - disableExtension({ - extensionLoader: di.inject(extensionLoaderInjectable), - }), -}); - -export default disableExtensionInjectable; diff --git a/packages/core/src/renderer/components/+extensions/disable-extension/disable-extension.ts b/packages/core/src/renderer/components/+extensions/disable-extension/disable-extension.ts deleted file mode 100644 index 834577a31c..0000000000 --- a/packages/core/src/renderer/components/+extensions/disable-extension/disable-extension.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { LensExtensionId } from "@k8slens/legacy-extensions"; -import type { ExtensionLoader } from "../../../../extensions/extension-loader"; - -interface Dependencies { - extensionLoader: ExtensionLoader; -} - -export const disableExtension = - ({ extensionLoader }: Dependencies) => - (id: LensExtensionId) => { - const extension = extensionLoader.getExtension(id); - - if (extension) { - extension.isEnabled = false; - } - }; diff --git a/packages/core/src/renderer/components/+extensions/enable-extension.injectable.ts b/packages/core/src/renderer/components/+extensions/enable-extension.injectable.ts new file mode 100644 index 0000000000..b9372f4a33 --- /dev/null +++ b/packages/core/src/renderer/components/+extensions/enable-extension.injectable.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { LensExtensionId } from "@k8slens/legacy-extensions"; +import { getInjectable } from "@ogre-tools/injectable"; +import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; + +export type EnableExtension = (extId: LensExtensionId) => void; + +const enableExtensionInjectable = getInjectable({ + id: "enable-extension", + + instantiate: (di): EnableExtension => { + const extensionLoader = di.inject(extensionLoaderInjectable); + + return (extId) => { + const ext = extensionLoader.getExtensionById(extId); + + if (ext && !ext.isBundled) { + ext.isEnabled = true; + } + }; + }, +}); + +export default enableExtensionInjectable; diff --git a/packages/core/src/renderer/components/+extensions/enable-extension/enable-extension.injectable.ts b/packages/core/src/renderer/components/+extensions/enable-extension/enable-extension.injectable.ts deleted file mode 100644 index 19e5e83233..0000000000 --- a/packages/core/src/renderer/components/+extensions/enable-extension/enable-extension.injectable.ts +++ /dev/null @@ -1,18 +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 extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable"; -import { enableExtension } from "./enable-extension"; - -const enableExtensionInjectable = getInjectable({ - id: "enable-extension", - - instantiate: (di) => - enableExtension({ - extensionLoader: di.inject(extensionLoaderInjectable), - }), -}); - -export default enableExtensionInjectable; diff --git a/packages/core/src/renderer/components/+extensions/enable-extension/enable-extension.ts b/packages/core/src/renderer/components/+extensions/enable-extension/enable-extension.ts deleted file mode 100644 index 5692e9efdc..0000000000 --- a/packages/core/src/renderer/components/+extensions/enable-extension/enable-extension.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { LensExtensionId } from "@k8slens/legacy-extensions"; -import type { ExtensionLoader } from "../../../../extensions/extension-loader"; - -interface Dependencies { - extensionLoader: ExtensionLoader; -} - -export const enableExtension = - ({ extensionLoader }: Dependencies) => - (id: LensExtensionId) => { - const extension = extensionLoader.getExtension(id); - - if (extension) { - extension.isEnabled = true; - } - }; diff --git a/packages/core/src/renderer/components/+extensions/extensions.tsx b/packages/core/src/renderer/components/+extensions/extensions.tsx index ab8049fe92..dcb6a4b70f 100644 --- a/packages/core/src/renderer/components/+extensions/extensions.tsx +++ b/packages/core/src/renderer/components/+extensions/extensions.tsx @@ -4,138 +4,66 @@ */ import styles from "./extensions.module.scss"; -import type { IComputedValue } from "mobx"; -import { - makeObservable, - observable, - reaction, - when, -} from "mobx"; -import { disposeOnUnmount, observer } from "mobx-react"; import React from "react"; import { DropFileInput } from "../input"; -import { Install } from "./install"; +import { ExtensionInstall } from "./install"; import { InstalledExtensions } from "./installed-extensions"; import { Notice } from "./notice"; import { SettingLayout } from "../layout/setting-layout"; import { docsUrl } from "../../../common/vars"; import { withInjectables } from "@ogre-tools/injectable-react"; - -import userExtensionsInjectable from "./user-extensions/user-extensions.injectable"; -import enableExtensionInjectable from "./enable-extension/enable-extension.injectable"; -import disableExtensionInjectable from "./disable-extension/disable-extension.injectable"; -import type { ConfirmUninstallExtension } from "./confirm-uninstall-extension.injectable"; -import confirmUninstallExtensionInjectable from "./confirm-uninstall-extension.injectable"; -import type { InstallExtensionFromInput } from "./install-extension-from-input.injectable"; -import installExtensionFromInputInjectable from "./install-extension-from-input.injectable"; -import installFromSelectFileDialogInjectable from "./install-from-select-file-dialog.injectable"; import type { InstallOnDrop } from "./install-on-drop.injectable"; import installOnDropInjectable from "./install-on-drop.injectable"; -import { supportedExtensionFormats } from "./supported-extension-formats"; -import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; -import type { ExtensionInstallationStateStore } from "../../../extensions/extension-installation-state-store/extension-installation-state-store"; import Gutter from "../gutter/gutter"; -import type { InstalledExtension, LensExtensionId } from "@k8slens/legacy-extensions"; + +const ExtensionsNotice = () => ( + +

+ {"Add new features via Lens Extensions. Check out the "} + + docs + + {" and list of "} + + available extensions + + . +

+ +); interface Dependencies { - userExtensions: IComputedValue; - enableExtension: (id: LensExtensionId) => void; - disableExtension: (id: LensExtensionId) => void; - confirmUninstallExtension: ConfirmUninstallExtension; - installExtensionFromInput: InstallExtensionFromInput; - installFromSelectFileDialog: () => Promise; installOnDrop: InstallOnDrop; - extensionInstallationStateStore: ExtensionInstallationStateStore; } -@observer -class NonInjectedExtensions extends React.Component { - @observable installPath = ""; - - constructor(props: Dependencies) { - super(props); - makeObservable(this); - } - - componentDidMount() { - disposeOnUnmount(this, [ - reaction(() => this.props.userExtensions.get().length, (curSize, prevSize) => { - if (curSize > prevSize) { - disposeOnUnmount(this, [ - when(() => !this.props.extensionInstallationStateStore.anyInstalling, () => this.installPath = ""), - ]); - } - }), - ]); - } - - render() { - const userExtensions = this.props.userExtensions.get(); - - return ( - - -
-

Extensions

- - -

- {"Add new features via Lens Extensions. Check out the "} - - docs - - {" and list of "} - - available extensions - - . -

-
- - (this.installPath = value)} - installFromInput={() => this.props.installExtensionFromInput(this.installPath)} - installFromSelectFileDialog={this.props.installFromSelectFileDialog} - installPath={this.installPath} - /> - - - - -
-
-
- ); - } -} +const NonInjectedExtensions = ({ installOnDrop }: Dependencies) => ( + + +
+

Extensions

+ + + + +
+
+
+); export const Extensions = withInjectables(NonInjectedExtensions, { getProps: (di) => ({ - userExtensions: di.inject(userExtensionsInjectable), - enableExtension: di.inject(enableExtensionInjectable), - disableExtension: di.inject(disableExtensionInjectable), - confirmUninstallExtension: di.inject(confirmUninstallExtensionInjectable), - installExtensionFromInput: di.inject(installExtensionFromInputInjectable), installOnDrop: di.inject(installOnDropInjectable), - installFromSelectFileDialog: di.inject(installFromSelectFileDialogInjectable), - extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable), }), }); diff --git a/packages/core/src/renderer/components/+extensions/install.tsx b/packages/core/src/renderer/components/+extensions/install.tsx index 63c09f8b18..ffbdc3ea2b 100644 --- a/packages/core/src/renderer/components/+extensions/install.tsx +++ b/packages/core/src/renderer/components/+extensions/install.tsx @@ -4,7 +4,7 @@ */ import styles from "./install.module.scss"; -import React from "react"; +import React, { useEffect, useRef, useState } from "react"; import { prevDefault } from "@k8slens/utilities"; import { Button } from "../button"; import { Icon } from "../icon"; @@ -16,17 +16,16 @@ import type { ExtensionInstallationStateStore } from "../../../extensions/extens import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import { unionInputValidatorsAsync } from "../input/input_validators"; - -export interface InstallProps { - installPath: string; - supportedFormats: string[]; - onChange: (path: string) => void; - installFromInput: () => void; - installFromSelectFileDialog: () => void; -} +import { supportedExtensionFormats } from "./supported-extension-formats"; +import type { InstallExtensionFromInput } from "./install-extension-from-input.injectable"; +import type { InstallFromSelectFileDialog } from "./install-from-select-file-dialog.injectable"; +import installExtensionFromInputInjectable from "./install-extension-from-input.injectable"; +import installFromSelectFileDialogInjectable from "./install-from-select-file-dialog.injectable"; interface Dependencies { - extensionInstallationStateStore: ExtensionInstallationStateStore; + installState: ExtensionInstallationStateStore; + installExtensionFromInput: InstallExtensionFromInput; + installFromSelectFileDialog: InstallFromSelectFileDialog; } const installInputValidator = unionInputValidatorsAsync( @@ -38,71 +37,75 @@ const installInputValidator = unionInputValidatorsAsync( InputValidators.isPath, ); -const NonInjectedInstall: React.FC = ({ - installPath, - supportedFormats, - onChange, - installFromInput, +const installTitle = `Name or file path or URL to an extension package (${supportedExtensionFormats.join(", ")})`; + +const NonInjectedInstall = observer(({ + installExtensionFromInput, installFromSelectFileDialog, - extensionInstallationStateStore, -}) => ( -
- -
-
- - )} - /> -
-
-
-
- - Pro-Tip - : you can drag and drop a tarball file to this area - -
-); + installState, +}: Dependencies) => { + const [installPath, setInstallPath] = useState(""); + const prevAnyInstalling = useRef(installState.anyInstalling); -export const Install = withInjectables( - observer(NonInjectedInstall), - { - getProps: (di, props) => ({ - extensionInstallationStateStore: di.inject( - extensionInstallationStateStoreInjectable, - ), + useEffect(() => { + const currentlyInstalling = installState.anyInstalling; + const previouslyInstalling = prevAnyInstalling.current; - ...props, - }), - }, -); + if (!currentlyInstalling && previouslyInstalling) { + prevAnyInstalling.current = false; + setInstallPath(""); + } + }, [installState.anyInstalling]); + + return ( +
+ +
+
+ installExtensionFromInput(installPath)} + iconRight={( + + )} + /> +
+
+
+
+ + Pro-Tip + : you can drag and drop a tarball file to this area + +
+ ); +}); + +export const ExtensionInstall = withInjectables(NonInjectedInstall, { + getProps: (di, props) => ({ + ...props, + installState: di.inject(extensionInstallationStateStoreInjectable), + installExtensionFromInput: di.inject(installExtensionFromInputInjectable), + installFromSelectFileDialog: di.inject(installFromSelectFileDialogInjectable), + }), +}); diff --git a/packages/core/src/renderer/components/+extensions/installed-extensions.tsx b/packages/core/src/renderer/components/+extensions/installed-extensions.tsx index f420b63f59..62c40b253a 100644 --- a/packages/core/src/renderer/components/+extensions/installed-extensions.tsx +++ b/packages/core/src/renderer/components/+extensions/installed-extensions.tsx @@ -4,8 +4,7 @@ */ import styles from "./installed-extensions.module.scss"; -import React, { useMemo } from "react"; -import type { ExtensionDiscovery } from "../../../extensions/extension-discovery/extension-discovery"; +import React from "react"; import { Icon } from "../icon"; import { List } from "../list/list"; import { MenuActions, MenuItem } from "../menu"; @@ -17,18 +16,27 @@ import extensionDiscoveryInjectable from "../../../extensions/extension-discover import { withInjectables } from "@ogre-tools/injectable-react"; import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import type { ExtensionInstallationStateStore } from "../../../extensions/extension-installation-state-store/extension-installation-state-store"; -import type { InstalledExtension, LensExtensionId } from "@k8slens/legacy-extensions"; +import type { InstalledExtension } from "@k8slens/legacy-extensions"; +import type { IComputedValue } from "mobx"; +import type { ConfirmUninstallExtension } from "./confirm-uninstall-extension.injectable"; +import confirmUninstallExtensionInjectable from "./confirm-uninstall-extension.injectable"; +import type { DisableExtension } from "./disable-extension.injectable"; +import disableExtensionInjectable from "./disable-extension.injectable"; +import type { EnableExtension } from "./enable-extension.injectable"; +import enableExtensionInjectable from "./enable-extension.injectable"; +import userExtensionsInjectable from "./user-extensions/user-extensions.injectable"; +import type { ExtensionDiscovery } from "../../../extensions/extension-discovery/extension-discovery"; export interface InstalledExtensionsProps { - extensions: InstalledExtension[]; - enable: (id: LensExtensionId) => void; - disable: (id: LensExtensionId) => void; - uninstall: (extension: InstalledExtension) => void; } interface Dependencies { extensionDiscovery: ExtensionDiscovery; extensionInstallationStateStore: ExtensionInstallationStateStore; + userExtensions: IComputedValue; + enableExtension: EnableExtension; + disableExtension: DisableExtension; + confirmUninstallExtension: ConfirmUninstallExtension; } function getStatus(extension: InstalledExtension) { @@ -39,106 +47,20 @@ function getStatus(extension: InstalledExtension) { return extension.isEnabled ? "Enabled" : "Disabled"; } -const NonInjectedInstalledExtensions = observer(({ extensionDiscovery, extensionInstallationStateStore, extensions, uninstall, enable, disable }: Dependencies & InstalledExtensionsProps) => { - const columns = useMemo( - () => [ - { - Header: "Name", - accessor: "extension", - width: 200, - sortType: (rowA: Row, rowB: Row) => { // Custom sorting for extension name - const nameA = extensions[rowA.index].manifest.name; - const nameB = extensions[rowB.index].manifest.name; - - if (nameA > nameB) return -1; - if (nameB > nameA) return 1; - - return 0; - }, - }, - { - Header: "Version", - accessor: "version", - }, - { - Header: "Status", - accessor: "status", - }, - { - Header: "", - accessor: "actions", - disableSortBy: true, - width: 20, - className: "actions", - }, - ], [], - ); - - const data = useMemo( - () => extensions.map(extension => { - const { id, isEnabled, isCompatible, manifest } = extension; - const { name, description, version } = manifest; - const isUninstalling = extensionInstallationStateStore.isExtensionUninstalling(id); - - return { - extension: ( -
-
-
{name}
-
{description}
-
-
- ), - version, - status: ( -
- {getStatus(extension)} -
- ), - actions: ( - - {isCompatible && ( - <> - {isEnabled ? ( - disable(id)} - > - - Disable - - ) : ( - enable(id)} - > - - Enable - - )} - - )} - - uninstall(extension)} - > - - Uninstall - - - ), - }; - }), [extensions, extensionInstallationStateStore.anyUninstalling], - ); - +const NonInjectedInstalledExtensions = observer(({ + extensionDiscovery, + extensionInstallationStateStore, + userExtensions, + confirmUninstallExtension, + enableExtension, + disableExtension, +}: Dependencies & InstalledExtensionsProps) => { if (!extensionDiscovery.isLoaded) { return
; } + const extensions = userExtensions.get(); + if (extensions.length == 0) { return (
@@ -151,13 +73,96 @@ const NonInjectedInstalledExtensions = observer(({ extensionDiscovery, extension ); } + const toggleExtensionWith = (enabled: boolean) => ( + enabled + ? disableExtension + : enableExtension + ); + return (
Installed extensions} - columns={columns} - data={data} - items={extensions} + columns={[ + { + Header: "Name", + accessor: "extension", + width: 200, + sortType: (rowA: Row, rowB: Row) => { // Custom sorting for extension name + const nameA = extensions[rowA.index].manifest.name; + const nameB = extensions[rowB.index].manifest.name; + + if (nameA > nameB) return -1; + if (nameB > nameA) return 1; + + return 0; + }, + }, + { + Header: "Version", + accessor: "version", + }, + { + Header: "Status", + accessor: "status", + }, + { + Header: "", + accessor: "actions", + disableSortBy: true, + width: 20, + }, + ]} + data={extensions.map(extension => { + const { id, isEnabled, isCompatible, manifest } = extension; + const { name, description, version } = manifest; + const isUninstalling = extensionInstallationStateStore.isExtensionUninstalling(id); + const toggleExtension = toggleExtensionWith(isEnabled); + + return { + extension: ( +
+
+
{name}
+
{description}
+
+
+ ), + version, + status: ( +
+ {getStatus(extension)} +
+ ), + actions: ( + + {isCompatible && ( + toggleExtension(id)} + > + + + {isEnabled ? "Disable" : "Enabled"} + + + )} + + confirmUninstallExtension(extension)} + > + + Uninstall + + + ), + }; + })} + items={userExtensions.get()} filters={[ (extension) => extension.manifest.name, (extension) => getStatus(extension), @@ -170,8 +175,12 @@ const NonInjectedInstalledExtensions = observer(({ extensionDiscovery, extension export const InstalledExtensions = withInjectables(NonInjectedInstalledExtensions, { getProps: (di, props) => ({ + ...props, extensionDiscovery: di.inject(extensionDiscoveryInjectable), extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable), - ...props, + userExtensions: di.inject(userExtensionsInjectable), + enableExtension: di.inject(enableExtensionInjectable), + disableExtension: di.inject(disableExtensionInjectable), + confirmUninstallExtension: di.inject(confirmUninstallExtensionInjectable), }), }); diff --git a/packages/core/src/renderer/components/+extensions/uninstall-extension.injectable.tsx b/packages/core/src/renderer/components/+extensions/uninstall-extension.injectable.tsx index 6a68107ad5..d70d56b5f9 100644 --- a/packages/core/src/renderer/components/+extensions/uninstall-extension.injectable.tsx +++ b/packages/core/src/renderer/components/+extensions/uninstall-extension.injectable.tsx @@ -27,7 +27,7 @@ const uninstallExtensionInjectable = getInjectable({ const showErrorNotification = di.inject(showErrorNotificationInjectable); return async (extensionId: LensExtensionId): Promise => { - const ext = extensionLoader.getExtension(extensionId); + const ext = extensionLoader.getExtensionById(extensionId); if (!ext) { logger.debug(`[EXTENSIONS]: cannot uninstall ${extensionId}, was not installed`); @@ -45,7 +45,7 @@ const uninstallExtensionInjectable = getInjectable({ await extensionDiscovery.uninstallExtension(extensionId); // wait for the ExtensionLoader to actually uninstall the extension - await when(() => !extensionLoader.userExtensions.has(extensionId)); + await when(() => !extensionLoader.userExtensions.get().has(extensionId)); showSuccessNotification(

diff --git a/packages/core/src/renderer/components/+extensions/user-extensions/user-extensions.injectable.ts b/packages/core/src/renderer/components/+extensions/user-extensions/user-extensions.injectable.ts index 63d5c7a5fc..9c3d8fc3eb 100644 --- a/packages/core/src/renderer/components/+extensions/user-extensions/user-extensions.injectable.ts +++ b/packages/core/src/renderer/components/+extensions/user-extensions/user-extensions.injectable.ts @@ -12,7 +12,7 @@ const userExtensionsInjectable = getInjectable({ instantiate: (di) => { const extensionLoader = di.inject(extensionLoaderInjectable); - return computed(() => [...extensionLoader.userExtensions.values()]); + return computed(() => [...extensionLoader.userExtensions.get().values()]); }, }); diff --git a/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts b/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts index af9fd15a26..42ffa28946 100644 --- a/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts +++ b/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/call-for-resource/call-for-resource.injectable.ts @@ -3,44 +3,35 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import type { KubeObject } from "../../../../../../common/k8s-api/kube-object"; +import { KubeObject } from "../../../../../../common/k8s-api/kube-object"; import { parseKubeApi } from "../../../../../../common/k8s-api/kube-api-parse"; import type { AsyncResult } from "@k8slens/utilities"; import { getErrorMessage } from "../../../../../../common/utils/get-error-message"; -import apiManagerInjectable from "../../../../../../common/k8s-api/api-manager/manager.injectable"; -import { waitUntilDefined } from "@k8slens/utilities"; +import apiKubeInjectable from "../../../../../k8s/api-kube.injectable"; -export type CallForResource = ( - selfLink: string -) => AsyncResult; +export type CallForResource = (selfLink: string) => AsyncResult; const callForResourceInjectable = getInjectable({ id: "call-for-resource", instantiate: (di): CallForResource => { - const apiManager = di.inject(apiManagerInjectable); + const apiKube = di.inject(apiKubeInjectable); return async (apiPath: string) => { - const api = await waitUntilDefined(() => apiManager.getApi(apiPath)); - const parsed = parseKubeApi(apiPath); - if (!api || !parsed.name) { + if (!parsed.name) { return { callWasSuccessful: false, error: "Invalid API path" }; } - let resource: KubeObject | null; - try { - resource = await api.get({ - name: parsed.name, - namespace: parsed.namespace, - }); + return { + callWasSuccessful: true, + response: new KubeObject(await apiKube.get(apiPath)), + }; } catch (e) { return { callWasSuccessful: false, error: getErrorMessage(e) }; } - - return { callWasSuccessful: true, response: resource || undefined }; }; }, diff --git a/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx b/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx index 7b2386e189..e1f8eca988 100644 --- a/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx +++ b/packages/core/src/renderer/components/dock/edit-resource/edit-resource-model/edit-resource-model.injectable.tsx @@ -7,9 +7,9 @@ import type { CallForResource } from "./call-for-resource/call-for-resource.inje import callForResourceInjectable from "./call-for-resource/call-for-resource.injectable"; import { waitUntilDefined } from "@k8slens/utilities"; import editResourceTabStoreInjectable from "../store.injectable"; -import type { EditResourceTabStore } from "../store"; -import { action, computed, makeObservable, observable, runInAction } from "mobx"; -import type { KubeObject } from "../../../../../common/k8s-api/kube-object"; +import type { EditingResource, EditResourceTabStore } from "../store"; +import { action, computed, observable, runInAction } from "mobx"; +import type { KubeObject, RawKubeObject } from "../../../../../common/k8s-api/kube-object"; import yaml from "js-yaml"; import assert from "assert"; import type { CallForPatchResource } from "./call-for-patch-resource/call-for-patch-resource.injectable"; @@ -19,18 +19,22 @@ import type { ShowNotification } from "../../../notifications"; import showSuccessNotificationInjectable from "../../../notifications/show-success-notification.injectable"; import React from "react"; import showErrorNotificationInjectable from "../../../notifications/show-error-notification.injectable"; +import { createKubeApiURL, parseKubeApi } from "../../../../../common/k8s-api/kube-api-parse"; const editResourceModelInjectable = getInjectable({ id: "edit-resource-model", instantiate: async (di, tabId: string) => { + const store = di.inject(editResourceTabStoreInjectable); + const model = new EditResourceModel({ callForResource: di.inject(callForResourceInjectable), callForPatchResource: di.inject(callForPatchResourceInjectable), showSuccessNotification: di.inject(showSuccessNotificationInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable), - store: di.inject(editResourceTabStoreInjectable), + store, tabId, + waitForEditingResource: () => waitUntilDefined(() => store.getData(tabId)), }); await model.load(); @@ -48,19 +52,42 @@ export default editResourceModelInjectable; interface Dependencies { callForResource: CallForResource; callForPatchResource: CallForPatchResource; + waitForEditingResource: () => Promise; showSuccessNotification: ShowNotification; showErrorNotification: ShowNotification; readonly store: EditResourceTabStore; readonly tabId: string; } -export class EditResourceModel { - constructor(private readonly dependencies: Dependencies) { - makeObservable(this); +function getEditSelfLinkFor(object: RawKubeObject): string { + const lensVersionLabel = object.metadata.labels?.[EditResourceLabelName]; + + if (lensVersionLabel) { + const { apiVersionWithGroup, ...parsedApi } = parseKubeApi(object.metadata.selfLink); + + parsedApi.apiVersion = lensVersionLabel; + + return createKubeApiURL({ + ...parsedApi, + apiVersion: `${parsedApi.apiGroup}/${parsedApi.apiVersion}`, + }); } + return object.metadata.selfLink; +} + +/** + * The label name that Lens uses to receive the desired api version + */ +export const EditResourceLabelName = "k8slens-edit-resource-version"; + +export class EditResourceModel { + constructor(protected readonly dependencies: Dependencies) {} + readonly configuration = { - value: computed(() => this.editingResource.draft || this.editingResource.firstDraft || ""), + value: computed( + () => this.editingResource.draft || this.editingResource.firstDraft || "", + ), onChange: action((value: string) => { this.editingResource.draft = value; @@ -100,27 +127,39 @@ export class EditResourceModel { return this.editingResource.resource; } - load = async () => { - await waitUntilDefined(() => this.dependencies.store.getData(this.dependencies.tabId)); + load = async (): Promise => { + await this.dependencies.waitForEditingResource(); - const result = await this.dependencies.callForResource(this.selfLink); + let result = await this.dependencies.callForResource(this.selfLink); if (!result.callWasSuccessful) { - this.dependencies.showErrorNotification( - `Loading resource failed: ${result.error}`, - ); + return void this.dependencies.showErrorNotification(`Loading resource failed: ${result.error}`); + } + if (result?.response?.metadata.labels?.[EditResourceLabelName]) { + const parsed = parseKubeApi(this.selfLink); + + parsed.apiVersion = result.response.metadata.labels[EditResourceLabelName]; + + result = await this.dependencies.callForResource(createKubeApiURL(parsed)); + } + + if (!result.callWasSuccessful) { + return void this.dependencies.showErrorNotification(`Loading resource failed: ${result.error}`); + } + + const resource = result.response; + + runInAction(() => { + this._resource = resource; + }); + + if (!resource) { return; } runInAction(() => { - this._resource = result.response; - - if (this._resource) { - this.editingResource.firstDraft = yaml.dump( - this._resource.toPlainObject(), - ); - } + this.editingResource.firstDraft = yaml.dump(resource.toPlainObject()); }); }; @@ -138,16 +177,16 @@ export class EditResourceModel { save = async () => { const currentValue = this.configuration.value.get(); - const currentVersion = yaml.load(currentValue); - const firstVersion = yaml.load( - this.editingResource.firstDraft ?? currentValue, - ); - const patches = createPatch(firstVersion, currentVersion); + const currentVersion = yaml.load(currentValue) as RawKubeObject; + const firstVersion = yaml.load(this.editingResource.firstDraft ?? currentValue); - const result = await this.dependencies.callForPatchResource( - this.resource, - patches, - ); + // Make sure we save this label so that we can use it in the future + currentVersion.metadata.labels ??= {}; + currentVersion.metadata.labels[EditResourceLabelName] = currentVersion.apiVersion.split("/").pop(); + + const patches = createPatch(firstVersion, currentVersion); + const selfLink = getEditSelfLinkFor(currentVersion); + const result = await this.dependencies.callForPatchResource(this.resource, patches); if (!result.callWasSuccessful) { this.dependencies.showErrorNotification(( @@ -158,23 +197,26 @@ export class EditResourceModel {

)); - return; + return null; } const { kind, name } = result.response; - this.dependencies.showSuccessNotification(( + this.dependencies.showSuccessNotification(

- {`${kind} `} + {kind} + {" "} {name} {" updated."} -

- )); +

, + ); runInAction(() => { - this.editingResource.firstDraft = currentValue; + this.editingResource.firstDraft = yaml.dump(currentVersion); + this.editingResource.resource = selfLink; }); - return result.response.toString(); + // NOTE: This is required for `saveAndClose` to work correctly + return []; }; } diff --git a/packages/core/src/renderer/components/dock/edit-resource/view.tsx b/packages/core/src/renderer/components/dock/edit-resource/view.tsx index 0465f80c93..107728e81d 100644 --- a/packages/core/src/renderer/components/dock/edit-resource/view.tsx +++ b/packages/core/src/renderer/components/dock/edit-resource/view.tsx @@ -56,12 +56,14 @@ const NonInjectedEditResource = observer(({ Namespace:
- )} /> + )} + /> + onError={model.configuration.error.onChange} + /> ) } diff --git a/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts b/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts index 8fe4b7da38..449feac7ea 100644 --- a/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts +++ b/packages/core/src/renderer/components/kube-object-details/kube-object-detail-items/implementations/horizontal-pod-autoscaler-detail-item.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { kubeObjectDetailItemInjectionToken } from "../kube-object-detail-item-injection-token"; -import { HpaDetails } from "../../../+config-horizontal-pod-autoscalers"; +import { HorizontalPodAutoscalerDetails } from "../../../+config-horizontal-pod-autoscalers"; import { computed } from "mobx"; import { kubeObjectMatchesToKindAndApiVersion } from "../kube-object-matches-to-kind-and-api-version"; import currentKubeObjectInDetailsInjectable from "../../current-kube-object-in-details.injectable"; @@ -16,7 +16,7 @@ const horizontalPodAutoscalerDetailItemInjectable = getInjectable({ const kubeObject = di.inject(currentKubeObjectInDetailsInjectable); return { - Component: HpaDetails, + Component: HorizontalPodAutoscalerDetails, enabled: computed(() => isHorizontalPodAutoscaler(kubeObject.value.get()?.object)), orderNumber: 10, }; diff --git a/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx b/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx index 7e36639c41..592d288e13 100644 --- a/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx +++ b/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx @@ -246,12 +246,10 @@ class NonInjectedMonacoEditor extends React.Component this.model, this.onModelChange), - reaction(() => this.theme, theme => { - if (theme) { - editor.setTheme(theme); - } + reaction(() => this.theme, editor.setTheme), + reaction(() => this.props.value, value => this.setValue(value), { + fireImmediately: true, }), - reaction(() => this.props.value, value => this.setValue(value)), reaction(() => this.options, opts => this.editor.updateOptions(opts)), () => onDidLayoutChangeDisposer.dispose(), diff --git a/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts b/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts index ebc424641a..fda96f9b7a 100644 --- a/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts +++ b/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts @@ -30,7 +30,7 @@ const createExtensionInstanceInjectable = getInjectable({ }; return (ExtensionClass, extension) => { - const instance = new ExtensionClass(extension) as LensRendererExtension; + const instance = new ExtensionClass(extension as any) as LensRendererExtension; (instance as Writable)[lensExtensionDependencies] = deps; diff --git a/packages/core/src/common/vars/application-information-fake.injectable.testing-env.ts b/packages/core/src/test-env/application-information-fake.injectable.ts similarity index 87% rename from packages/core/src/common/vars/application-information-fake.injectable.testing-env.ts rename to packages/core/src/test-env/application-information-fake.injectable.ts index 1fd988b817..c5d9bfa8d2 100644 --- a/packages/core/src/common/vars/application-information-fake.injectable.testing-env.ts +++ b/packages/core/src/test-env/application-information-fake.injectable.ts @@ -18,9 +18,7 @@ export const applicationInformationFakeInjectable = getInjectable({ bundledKubectlVersion: "1.23.3", bundledHelmVersion: "3.7.2", sentryDsn: "", - contentSecurityPolicy: - "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", - + contentSecurityPolicy: "script-src 'unsafe-eval' 'self'; frame-src http://*.localhost:*/; img-src * data:", welcomeRoute: "/welcome", copyright: "some-copyright-information", description: "some-descriptive-text", diff --git a/packages/core/src/common/vars/node-env.injectable.testing-env.ts b/packages/core/src/test-env/node-env.injectable.ts similarity index 59% rename from packages/core/src/common/vars/node-env.injectable.testing-env.ts rename to packages/core/src/test-env/node-env.injectable.ts index 20d041330f..b08639d94c 100644 --- a/packages/core/src/common/vars/node-env.injectable.testing-env.ts +++ b/packages/core/src/test-env/node-env.injectable.ts @@ -3,12 +3,12 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { nodeEnvInjectionToken } from "./node-env-injection-token"; +import { nodeEnvInjectionToken } from "../main/library"; -const nodeEnvFakeInjectable = getInjectable({ - id: "node-env-fake", +const nodeEnvForTestingEnvInjectable = getInjectable({ + id: "node-env-for-testing-env", instantiate: () => "production", injectionToken: nodeEnvInjectionToken, }); -export default nodeEnvFakeInjectable; +export default nodeEnvForTestingEnvInjectable; diff --git a/packages/core/webpack/main.ts b/packages/core/webpack/main.ts index 2460ac3c8d..3e8c3ed4d7 100755 --- a/packages/core/webpack/main.ts +++ b/packages/core/webpack/main.ts @@ -10,7 +10,6 @@ import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"; import { iconsAndImagesWebpackRules } from "./renderer"; import { DefinePlugin } from "webpack"; import { buildDir, isDevelopment } from "./vars"; -import { platform } from "process"; const webpackLensMain = (): webpack.Configuration => { return { @@ -67,8 +66,8 @@ const webpackLensMain = (): webpack.Configuration => { }, plugins: [ new DefinePlugin({ - CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, - CONTEXT_MATCHER_FOR_FEATURES: `/\\/(main|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, + CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable\\.tsx?$/`, + CONTEXT_MATCHER_FOR_FEATURES: `/\\/(main|common)\\/.+\\.injectable\\.tsx?$/`, }), new ForkTsCheckerPlugin({ typescript: { diff --git a/packages/core/webpack/renderer.ts b/packages/core/webpack/renderer.ts index 3b133f8f50..a64fc2d3c3 100755 --- a/packages/core/webpack/renderer.ts +++ b/packages/core/webpack/renderer.ts @@ -12,7 +12,6 @@ import type { WebpackPluginInstance } from "webpack"; import { optimize, DefinePlugin } from "webpack"; import nodeExternals from "webpack-node-externals"; import { isDevelopment, buildDir, sassCommonVars } from "./vars"; -import { platform } from "process"; export function webpackLensRenderer(): webpack.Configuration { return { @@ -84,8 +83,8 @@ export function webpackLensRenderer(): webpack.Configuration { plugins: [ new DefinePlugin({ - CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, - CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, + CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable\\.tsx?$/`, + CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable\\.tsx?$/`, }), new ForkTsCheckerPlugin({}), diff --git a/packages/infrastructure/eslint-config/prettier-config.json b/packages/infrastructure/eslint-config/prettier-config.json index c9f2606a59..7c1cbfc0a5 100644 --- a/packages/infrastructure/eslint-config/prettier-config.json +++ b/packages/infrastructure/eslint-config/prettier-config.json @@ -1,5 +1,5 @@ { - "printWidth": 100, + "printWidth": 120, "tabWidth": 2, "useTabs": false, "semi": true, diff --git a/packages/open-lens/src/common/build-environment.injectable.ts b/packages/open-lens/src/common/build-environment.injectable.ts new file mode 100644 index 0000000000..790b4e3fcb --- /dev/null +++ b/packages/open-lens/src/common/build-environment.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { lensBuildEnvironmentInjectionToken } from "@k8slens/application"; +import { getInjectable } from "@ogre-tools/injectable"; + +const lensBuildEnvironmentInjectable = getInjectable({ + id: "lens-build-environment", + instantiate: () => "unknown", + injectionToken: lensBuildEnvironmentInjectionToken, +}); + +export default lensBuildEnvironmentInjectable; diff --git a/packages/open-lens/webpack/main.ts b/packages/open-lens/webpack/main.ts index a94f45a794..277fc1b785 100644 --- a/packages/open-lens/webpack/main.ts +++ b/packages/open-lens/webpack/main.ts @@ -50,8 +50,8 @@ const main: webpack.Configuration = ({ }, plugins: [ new DefinePlugin({ - CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, - CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, + CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable\\.tsx?$/`, + CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable\\.tsx?$/`, }), ], }); diff --git a/packages/open-lens/webpack/renderer.ts b/packages/open-lens/webpack/renderer.ts index e46487c6de..a17511db30 100644 --- a/packages/open-lens/webpack/renderer.ts +++ b/packages/open-lens/webpack/renderer.ts @@ -81,8 +81,8 @@ plugins: [ new DefinePlugin({ - CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, - CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, + CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable\\.tsx?$/`, + CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable\\.tsx?$/`, }), new ForkTsCheckerPlugin(), diff --git a/packages/technical-features/application/agnostic/index.ts b/packages/technical-features/application/agnostic/index.ts index 2e5595a816..fd8c0b4800 100644 --- a/packages/technical-features/application/agnostic/index.ts +++ b/packages/technical-features/application/agnostic/index.ts @@ -7,3 +7,5 @@ export { startApplicationInjectionToken } from "./src/start-application/start-ap export { applicationInformationToken } from "./src/application-information-token.no-coverage"; export type { ApplicationInformation } from "./src/application-information-token.no-coverage"; + +export { lensBuildEnvironmentInjectionToken } from "./src/environment-token"; diff --git a/packages/technical-features/application/agnostic/src/environment-token.test.ts b/packages/technical-features/application/agnostic/src/environment-token.test.ts new file mode 100644 index 0000000000..bece4e0ac7 --- /dev/null +++ b/packages/technical-features/application/agnostic/src/environment-token.test.ts @@ -0,0 +1,22 @@ +import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable"; +import { lensBuildEnvironmentInjectionToken } from "./environment-token"; + +describe("environment-token coverage tests", () => { + let di: DiContainer; + + beforeEach(() => { + di = createContainer("irrelevant"); + }); + + it("should be able to specify a build environment", () => { + di.register( + getInjectable({ + id: "some-id", + instantiate: () => "some-value", + injectionToken: lensBuildEnvironmentInjectionToken, + }), + ); + + expect(di.inject(lensBuildEnvironmentInjectionToken)).toBe("some-value"); + }); +}); diff --git a/packages/technical-features/application/agnostic/src/environment-token.ts b/packages/technical-features/application/agnostic/src/environment-token.ts new file mode 100644 index 0000000000..111c45d526 --- /dev/null +++ b/packages/technical-features/application/agnostic/src/environment-token.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectionToken } from "@ogre-tools/injectable"; + +export const lensBuildEnvironmentInjectionToken = getInjectionToken({ + id: "lens-build-environment-token", +}); diff --git a/packages/technical-features/application/agnostic/src/start-application/start-application.injectable.ts b/packages/technical-features/application/agnostic/src/start-application/start-application.injectable.ts index 87f6d1f7db..ea2f4f8a5b 100644 --- a/packages/technical-features/application/agnostic/src/start-application/start-application.injectable.ts +++ b/packages/technical-features/application/agnostic/src/start-application/start-application.injectable.ts @@ -13,9 +13,7 @@ const startApplicationInjectable = getInjectable({ instantiate: (di): StartApplication => { const runManyAsync = runManyFor(di); - const beforeApplicationIsLoading = runManyAsync( - timeSlots.beforeApplicationIsLoadingInjectionToken, - ); + const beforeApplicationIsLoading = runManyAsync(timeSlots.beforeApplicationIsLoadingInjectionToken); const onLoadOfApplication = runManyAsync(timeSlots.onLoadOfApplicationInjectionToken); const afterApplicationIsLoaded = runManyAsync(timeSlots.afterApplicationIsLoadedInjectionToken); diff --git a/packages/technical-features/application/electron-main/src/start-application/start-electron-application.injectable.ts b/packages/technical-features/application/electron-main/src/start-application/start-electron-application.injectable.ts index 495f75ec27..bd17d85991 100644 --- a/packages/technical-features/application/electron-main/src/start-application/start-electron-application.injectable.ts +++ b/packages/technical-features/application/electron-main/src/start-application/start-electron-application.injectable.ts @@ -1,9 +1,4 @@ -import { - DiContainer, - getInjectable, - instantiationDecoratorToken, - lifecycleEnum, -} from "@ogre-tools/injectable"; +import { DiContainer, getInjectable, instantiationDecoratorToken, lifecycleEnum } from "@ogre-tools/injectable"; import { startApplicationInjectionToken } from "@k8slens/application"; import whenAppIsReadyInjectable from "./when-app-is-ready.injectable"; import { beforeAnythingInjectionToken, beforeElectronIsReadyInjectionToken } from "./time-slots"; diff --git a/packages/technical-features/application/electron-main/src/starting-of-electron-main-application.test.ts b/packages/technical-features/application/electron-main/src/starting-of-electron-main-application.test.ts index d64cec3bc5..ec4ae1a836 100644 --- a/packages/technical-features/application/electron-main/src/starting-of-electron-main-application.test.ts +++ b/packages/technical-features/application/electron-main/src/starting-of-electron-main-application.test.ts @@ -1,10 +1,7 @@ import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable"; import { registerFeature } from "@k8slens/feature-core"; import { applicationFeatureForElectronMain } from "./feature"; -import { - beforeApplicationIsLoadingInjectionToken, - startApplicationInjectionToken, -} from "@k8slens/application"; +import { beforeApplicationIsLoadingInjectionToken, startApplicationInjectionToken } from "@k8slens/application"; import asyncFn, { AsyncFnMock } from "@async-fn/jest"; import whenAppIsReadyInjectable from "./start-application/when-app-is-ready.injectable"; import * as timeSlots from "./start-application/time-slots"; diff --git a/packages/technical-features/application/legacy-extensions/src/bundled-extension.ts b/packages/technical-features/application/legacy-extensions/src/bundled-extension.ts index f800ee0d37..e743d9dbed 100644 --- a/packages/technical-features/application/legacy-extensions/src/bundled-extension.ts +++ b/packages/technical-features/application/legacy-extensions/src/bundled-extension.ts @@ -1,13 +1,13 @@ import { getInjectionToken } from "@ogre-tools/injectable"; import type { - LensExtensionConstructor, - LensExtensionManifest, + BundledLensExtensionConstructor, + BundledLensExtensionManifest, } from "./lens-extension"; export interface BundledExtension { - readonly manifest: LensExtensionManifest; - main: () => LensExtensionConstructor | null; - renderer: () => LensExtensionConstructor | null; + readonly manifest: BundledLensExtensionManifest; + main: () => Promise; + renderer: () => Promise; } export const bundledExtensionInjectionToken = diff --git a/packages/technical-features/application/legacy-extensions/src/lens-extension.ts b/packages/technical-features/application/legacy-extensions/src/lens-extension.ts index 5c8222113d..dfde441d85 100644 --- a/packages/technical-features/application/legacy-extensions/src/lens-extension.ts +++ b/packages/technical-features/application/legacy-extensions/src/lens-extension.ts @@ -1,26 +1,39 @@ export type LensExtensionId = string; + export type LensExtensionConstructor = new ( ext: InstalledExtension ) => LegacyLensExtension; +export type BundledLensExtensionConstructor = new ( + ext: BundledInstalledExtension +) => LegacyLensExtension; -export interface InstalledExtension { - id: LensExtensionId; - - readonly manifest: LensExtensionManifest; - +export interface BaseInstalledExtension { + readonly id: LensExtensionId; // Absolute path to the non-symlinked source folder, // e.g. "/Users/user/.k8slens/extensions/helloworld" readonly absolutePath: string; - - /** - * Absolute to the symlinked package.json file - */ + // Absolute to the symlinked package.json file readonly manifestPath: string; - readonly isBundled: boolean; +} + +export interface BundledInstalledExtension extends BaseInstalledExtension { + readonly manifest: BundledLensExtensionManifest; + readonly isBundled: true; + readonly isCompatible: true; + readonly isEnabled: true; +} + +export interface ExternalInstalledExtension extends BaseInstalledExtension { + readonly manifest: LensExtensionManifest; + readonly isBundled: false; readonly isCompatible: boolean; isEnabled: boolean; } +export type InstalledExtension = + | BundledInstalledExtension + | ExternalInstalledExtension; + export interface LegacyLensExtension { readonly id: LensExtensionId; readonly manifest: LensExtensionManifest; @@ -38,22 +51,11 @@ export interface LegacyLensExtension { activate(): Promise; } -export interface LensExtensionManifest { +export interface BundledLensExtensionManifest { name: string; version: string; description?: string; - - main?: string; // path to %ext/dist/main.js - renderer?: string; // path to %ext/dist/renderer.js - /** - * Supported Lens version engine by extension could be defined in `manifest.engines.lens` - * Only MAJOR.MINOR version is taken in consideration. - */ - engines: { - lens: string; // "semver"-package format - npm?: string; - node?: string; - }; + publishConfig?: Partial>; /** * Specify extension name used for persisting data. @@ -61,3 +63,17 @@ export interface LensExtensionManifest { */ storeName?: string; } + +export interface LensExtensionManifest extends BundledLensExtensionManifest { + main?: string; // path to %ext/dist/main.js + renderer?: string; // path to %ext/dist/renderer.js + + /** + * Supported Lens version engine by extension could be defined in `manifest.engines.lens` + * Only MAJOR.MINOR version is taken in consideration. + */ + engines: { + lens: string; // "semver"-package format + [x: string]: string | undefined; + }; +} diff --git a/packages/technical-features/feature-core/src/deregister-feature.ts b/packages/technical-features/feature-core/src/deregister-feature.ts index 4476120ab7..8bd08e9077 100644 --- a/packages/technical-features/feature-core/src/deregister-feature.ts +++ b/packages/technical-features/feature-core/src/deregister-feature.ts @@ -2,9 +2,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import type { Feature } from "./feature"; import { featureContextMapInjectable } from "./feature-context-map-injectable"; -const getDependingFeaturesFor = ( - featureContextMap: Map }>, -) => { +const getDependingFeaturesFor = (featureContextMap: Map }>) => { const getDependingFeaturesForRecursion = (feature: Feature, atRoot = true): string[] => { const context = featureContextMap.get(feature); @@ -36,11 +34,9 @@ const deregisterFeatureRecursed = (di: DiContainer, feature: Feature, dependedBy const dependingFeatures = getDependingFeatures(feature); if (!dependedBy && dependingFeatures.length) { - throw new Error( - `Tried to deregister Feature "${ - feature.id - }", but it is the dependency of Features "${dependingFeatures.join(", ")}"`, - ); + const names = dependingFeatures.join(", "); + + throw new Error(`Tried to deregister Feature "${feature.id}", but it is the dependency of Features "${names}"`); } if (dependedBy) { diff --git a/packages/technical-features/feature-core/src/feature-dependencies.test.ts b/packages/technical-features/feature-core/src/feature-dependencies.test.ts index 8ddf6e11b8..8fb69fe33f 100644 --- a/packages/technical-features/feature-core/src/feature-dependencies.test.ts +++ b/packages/technical-features/feature-core/src/feature-dependencies.test.ts @@ -59,9 +59,7 @@ describe("feature-dependencies", () => { expect(() => { deregisterFeature(di, someDependencyFeature); - }).toThrow( - 'Tried to deregister feature "some-dependency-feature", but it was not registered.', - ); + }).toThrow('Tried to deregister feature "some-dependency-feature", but it was not registered.'); }); it("given the parent Feature is deregistered, when injecting an injectable from the dependency Feature, throws", () => { @@ -104,9 +102,7 @@ describe("feature-dependencies", () => { it("when the first Feature is deregistered, throws", () => { expect(() => { deregisterFeature(di, someFeature1); - }).toThrow( - 'Tried to deregister Feature "some-feature-1", but it is the dependency of Features "some-feature-2"', - ); + }).toThrow('Tried to deregister Feature "some-feature-1", but it is the dependency of Features "some-feature-2"'); }); it("given the second Feature is deregistered, when injecting an injectable from the first Feature, still does so", () => { @@ -180,9 +176,7 @@ describe("feature-dependencies", () => { expect(() => { di.inject(someInjectableInDependencyFeature); - }).toThrow( - 'Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".', - ); + }).toThrow('Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".'); }); }); @@ -256,9 +250,7 @@ describe("feature-dependencies", () => { expect(() => { di.inject(someInjectableInDependencyFeature); - }).toThrow( - 'Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".', - ); + }).toThrow('Tried to inject non-registered injectable "irrelevant" -> "some-injectable-in-dependency-feature".'); }); }); }); diff --git a/packages/technical-features/feature-core/src/register-feature.ts b/packages/technical-features/feature-core/src/register-feature.ts index f02b050002..535a93bf18 100644 --- a/packages/technical-features/feature-core/src/register-feature.ts +++ b/packages/technical-features/feature-core/src/register-feature.ts @@ -1,10 +1,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import type { Feature } from "./feature"; -import { - featureContextMapInjectable, - featureContextMapInjectionToken, -} from "./feature-context-map-injectable"; +import { featureContextMapInjectable, featureContextMapInjectionToken } from "./feature-context-map-injectable"; const createFeatureContext = (feature: Feature, di: DiContainer) => { const featureContextInjectable = getInjectable({ @@ -58,10 +55,9 @@ const registerFeatureRecursed = (di: DiContainer, feature: Feature, dependedBy?: if (dependedBy) { const oldNumberOfDependents = featureContext.dependedBy.get(dependedBy) || 0; + const newNumberOfDependents = oldNumberOfDependents + 1; - const newNumberOfDependants = oldNumberOfDependents + 1; - - featureContext.dependedBy.set(dependedBy, newNumberOfDependants); + featureContext.dependedBy.set(dependedBy, newNumberOfDependents); } if (!existingFeatureContext) { diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/index.ts b/packages/technical-features/messaging/agnostic/src/features/actual/index.ts index f85542c954..f0acf5ee1c 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/index.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/index.ts @@ -21,15 +21,9 @@ export { getMessageChannelListenerInjectable, } from "./message/message-channel-listener-injection-token"; -export type { - RequestChannel, - RequestChannelHandler, -} from "./request/request-channel-listener-injection-token"; +export type { RequestChannel, RequestChannelHandler } from "./request/request-channel-listener-injection-token"; -export type { - RequestFromChannel, - ChannelRequester, -} from "./request/request-from-channel-injection-token"; +export type { RequestFromChannel, ChannelRequester } from "./request/request-from-channel-injection-token"; export type { EnlistMessageChannelListener } from "./message/enlist-message-channel-listener-injection-token"; export { enlistMessageChannelListenerInjectionToken } from "./message/enlist-message-channel-listener-injection-token"; diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts b/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts index 9040939f06..6d65621f12 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts @@ -21,9 +21,7 @@ export const listeningOfChannelsInjectionToken = getInjectionToken | RequestChannel }, ->( +const listening = | RequestChannel }>( channelListeners: IComputedValue, enlistChannelListener: (listener: T) => () => void, getId: (listener: T) => string, @@ -33,9 +31,7 @@ const listening = < const reactionDisposer = reaction( () => channelListeners.get(), (newValues, oldValues = []) => { - const addedListeners = newValues.filter( - (newValue) => !oldValues.some((oldValue) => oldValue.id === newValue.id), - ); + const addedListeners = newValues.filter((newValue) => !oldValues.some((oldValue) => oldValue.id === newValue.id)); const removedListeners = oldValues.filter( (oldValue) => !newValues.some((newValue) => newValue.id === oldValue.id), @@ -45,9 +41,7 @@ const listening = < const id = getId(listener); if (listenerDisposers.has(id)) { - throw new Error( - `Tried to add listener for channel "${listener.channel.id}" but listener already exists.`, - ); + throw new Error(`Tried to add listener for channel "${listener.channel.id}" but listener already exists.`); } const disposer = enlistChannelListener(listener); diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts index 1bb114ca09..19f4b32df6 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts @@ -1,16 +1,10 @@ import type { Disposer } from "@k8slens/utilities"; import { getInjectionToken } from "@ogre-tools/injectable"; -import type { - MessageChannel, - MessageChannelListener, -} from "./message-channel-listener-injection-token"; +import type { MessageChannel, MessageChannelListener } from "./message-channel-listener-injection-token"; -export type EnlistMessageChannelListener = ( - listener: MessageChannelListener>, -) => Disposer; +export type EnlistMessageChannelListener = (listener: MessageChannelListener>) => Disposer; -export const enlistMessageChannelListenerInjectionToken = - getInjectionToken({ - id: "listening-to-a-message-channel", - }); +export const enlistMessageChannelListenerInjectionToken = getInjectionToken({ + id: "listening-to-a-message-channel", +}); diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts index 0558bf6598..0e2ee20a64 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/message/message-channel-listener-injection-token.ts @@ -18,9 +18,7 @@ export interface MessageChannelListener { handler: MessageChannelHandler; } -export const messageChannelListenerInjectionToken = getInjectionToken< - MessageChannelListener> ->({ +export const messageChannelListenerInjectionToken = getInjectionToken>>({ id: "message-channel-listener", }); @@ -31,10 +29,7 @@ export interface GetMessageChannelListenerInfo, - Message, ->( +export const getMessageChannelListenerInjectable = , Message>( info: GetMessageChannelListenerInfo, ) => getInjectable({ diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts index 7f2a04a78d..8f2d6c2c81 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts @@ -1,16 +1,12 @@ import type { Disposer } from "@k8slens/utilities/index"; import { getInjectionToken } from "@ogre-tools/injectable"; -import type { - RequestChannel, - RequestChannelListener, -} from "./request-channel-listener-injection-token"; +import type { RequestChannel, RequestChannelListener } from "./request-channel-listener-injection-token"; export type EnlistRequestChannelListener = ( listener: RequestChannelListener>, ) => Disposer; -export const enlistRequestChannelListenerInjectionToken = - getInjectionToken({ - id: "listening-to-a-request-channel", - }); +export const enlistRequestChannelListenerInjectionToken = getInjectionToken({ + id: "listening-to-a-request-channel", +}); diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/request/get-request-channel.ts b/packages/technical-features/messaging/agnostic/src/features/actual/request/get-request-channel.ts index c0ee40bcf4..e89f8a8d0e 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/request/get-request-channel.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/request/get-request-channel.ts @@ -1,7 +1,5 @@ import type { RequestChannel } from "./request-channel-listener-injection-token"; -export const getRequestChannel = ( - id: string, -): RequestChannel => ({ +export const getRequestChannel = (id: string): RequestChannel => ({ id, }); diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/request/request-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/request/request-channel-listener-injection-token.ts index 2ec76ff546..4491b14fe1 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/request/request-channel-listener-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/request/request-channel-listener-injection-token.ts @@ -7,10 +7,7 @@ export interface RequestChannel { _responseSignature?: Response; } -export type RequestChannelHandler = Channel extends RequestChannel< - infer Request, - infer Response -> +export type RequestChannelHandler = Channel extends RequestChannel ? (req: Request) => Promise | Response : never; diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/request/request-from-channel-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/request/request-from-channel-injection-token.ts index 194091b588..f15dc97614 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/request/request-from-channel-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/request/request-from-channel-injection-token.ts @@ -2,17 +2,11 @@ import { getInjectionToken } from "@ogre-tools/injectable"; import type { RequestChannel } from "./request-channel-listener-injection-token"; export interface RequestFromChannel { - ( - channel: RequestChannel, - request: Request, - ): Promise; + (channel: RequestChannel, request: Request): Promise; (channel: RequestChannel): Promise; } -export type ChannelRequester = Channel extends RequestChannel< - infer Request, - infer Response -> +export type ChannelRequester = Channel extends RequestChannel ? (req: Request) => Promise> : never; diff --git a/packages/technical-features/messaging/agnostic/src/listening-of-requests.test.ts b/packages/technical-features/messaging/agnostic/src/listening-of-requests.test.ts index e708c983c8..6432ac1376 100644 --- a/packages/technical-features/messaging/agnostic/src/listening-of-requests.test.ts +++ b/packages/technical-features/messaging/agnostic/src/listening-of-requests.test.ts @@ -121,9 +121,7 @@ describe("listening-of-requests", () => { runInAction(() => { di.register(someConflictingListenerInjectable); }); - }).toThrow( - 'Tried to add listener for channel "some-channel-id" but listener already exists.', - ); + }).toThrow('Tried to add listener for channel "some-channel-id" but listener already exists.'); }); describe("when another listener gets registered", () => { diff --git a/packages/technical-features/messaging/computed-channel/index.ts b/packages/technical-features/messaging/computed-channel/index.ts index dd72ecf71b..ba98df8cca 100644 --- a/packages/technical-features/messaging/computed-channel/index.ts +++ b/packages/technical-features/messaging/computed-channel/index.ts @@ -3,7 +3,4 @@ export { computedChannelObserverInjectionToken, } from "./src/computed-channel/computed-channel.injectable"; -export type { - ChannelObserver, - ComputedChannelFactory, -} from "./src/computed-channel/computed-channel.injectable"; +export type { ChannelObserver, ComputedChannelFactory } from "./src/computed-channel/computed-channel.injectable"; diff --git a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts index b652a0c795..99d5d18fec 100644 --- a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts +++ b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts @@ -1,23 +1,13 @@ import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; -import { - computed, - IComputedValue, - observable, - onBecomeObserved, - onBecomeUnobserved, - runInAction, -} from "mobx"; +import { computed, IComputedValue, observable, onBecomeObserved, onBecomeUnobserved, runInAction } from "mobx"; import type { MessageChannel } from "@k8slens/messaging"; import { getMessageChannelListenerInjectable } from "@k8slens/messaging"; import { sendMessageToChannelInjectionToken } from "@k8slens/messaging"; import { computedChannelAdministrationChannel } from "./computed-channel-administration-channel.injectable"; -export type ComputedChannelFactory = ( - channel: MessageChannel, - pendingValue: T, -) => IComputedValue; +export type ComputedChannelFactory = (channel: MessageChannel, pendingValue: T) => IComputedValue; export const computedChannelInjectionToken = getInjectionToken({ id: "computed-channel-injection-token", diff --git a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx index 6bea2ca76d..abab064191 100644 --- a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx +++ b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx @@ -3,23 +3,13 @@ import { act } from "@testing-library/react"; import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable"; import { getMessageBridgeFake, MessageBridgeFake } from "@k8slens/messaging-fake-bridge"; import { startApplicationInjectionToken } from "@k8slens/application"; -import { - computed, - IComputedValue, - IObservableValue, - observable, - reaction, - runInAction, -} from "mobx"; +import { computed, IComputedValue, IObservableValue, observable, reaction, runInAction } from "mobx"; import type { MessageChannel } from "@k8slens/messaging"; import { getMessageChannelListenerInjectable } from "@k8slens/messaging"; import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import { registerFeature } from "@k8slens/feature-core"; import { testUtils } from "@k8slens/messaging"; -import { - computedChannelInjectionToken, - computedChannelObserverInjectionToken, -} from "./computed-channel.injectable"; +import { computedChannelInjectionToken, computedChannelObserverInjectionToken } from "./computed-channel.injectable"; import { runWithThrownMobxReactions, renderFor } from "@k8slens/test-utils"; import { observer } from "mobx-react"; import { @@ -36,9 +26,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue )); [{ scenarioIsAsync: true }, { scenarioIsAsync: false }].forEach(({ scenarioIsAsync }) => - describe(`computed-channel, given running message bridge fake as ${ - scenarioIsAsync ? "async" : "sync" - }`, () => { + describe(`computed-channel, given running message bridge fake as ${scenarioIsAsync ? "async" : "sync"}`, () => { describe("given multiple dis and a message channel and a channel observer and application has started", () => { let di1: DiContainer; let di2: DiContainer; @@ -87,10 +75,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue messageBridgeFake.setAsync(scenarioIsAsync); messageBridgeFake.involve(di1, di2); - await Promise.all([ - di1.inject(startApplicationInjectionToken)(), - di2.inject(startApplicationInjectionToken)(), - ]); + await Promise.all([di1.inject(startApplicationInjectionToken)(), di2.inject(startApplicationInjectionToken)()]); }); describe("given a channel observer and matching computed channel for the channel in di-2", () => { @@ -175,9 +160,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue expect(observedValue).toBe("some-pending-value"); }); - const scenarioName = scenarioIsAsync - ? "when admin messages are propagated" - : "immediately"; + const scenarioName = scenarioIsAsync ? "when admin messages are propagated" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioName, () => { @@ -196,9 +179,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue }); }); - const scenarioName = scenarioIsAsync - ? "when returning value-messages propagate" - : "immediately"; + const scenarioName = scenarioIsAsync ? "when returning value-messages propagate" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioName, () => { @@ -227,9 +208,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue }); }); - const scenarioName = scenarioIsAsync - ? "when value-messages propagate" - : "immediately"; + const scenarioName = scenarioIsAsync ? "when value-messages propagate" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioName, () => { @@ -258,9 +237,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue stopObserving(); }); - const scenarioName = scenarioIsAsync - ? "when admin-messages propagate" - : "immediately"; + const scenarioName = scenarioIsAsync ? "when admin-messages propagate" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioName, () => { @@ -317,9 +294,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue expect(observedValue).toBe("some-pending-value"); }); - const scenarioName = scenarioIsAsync - ? "when admin messages propagate" - : "immediately"; + const scenarioName = scenarioIsAsync ? "when admin messages propagate" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioName, () => { @@ -345,9 +320,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue expect(observedValue).toBe("some-pending-value"); }); - const scenarioTitle = scenarioIsAsync - ? "when value-messages propagate back" - : "immediately"; + const scenarioTitle = scenarioIsAsync ? "when value-messages propagate back" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioTitle, () => { @@ -466,9 +439,7 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue expect(nonReactiveValue).toBe("some-initial-value"); }); - const scenarioName = scenarioIsAsync - ? "when messages would be propagated" - : "immediately"; + const scenarioName = scenarioIsAsync ? "when messages would be propagated" : "immediately"; // eslint-disable-next-line jest/valid-title describe(scenarioName, () => { diff --git a/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.test.ts b/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.test.ts index c4385246cd..898efdf3f1 100644 --- a/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.test.ts +++ b/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.test.ts @@ -1,9 +1,6 @@ import ipcMainInjectable from "../ipc-main/ipc-main.injectable"; import type { IpcMain, IpcMainEvent } from "electron"; -import { - EnlistMessageChannelListener, - enlistMessageChannelListenerInjectionToken, -} from "@k8slens/messaging"; +import { EnlistMessageChannelListener, enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; import { createContainer } from "@ogre-tools/injectable"; import { registerFeature } from "@k8slens/feature-core"; import { messagingFeatureForMain } from "../feature"; diff --git a/packages/technical-features/messaging/electron/main/src/request-from-channel/request-from-channel.injectable.ts b/packages/technical-features/messaging/electron/main/src/request-from-channel/request-from-channel.injectable.ts index 220daf9fc7..94ce8c89be 100644 --- a/packages/technical-features/messaging/electron/main/src/request-from-channel/request-from-channel.injectable.ts +++ b/packages/technical-features/messaging/electron/main/src/request-from-channel/request-from-channel.injectable.ts @@ -1,19 +1,13 @@ /* c8 ignore start */ import { getInjectable } from "@ogre-tools/injectable"; -import { - RequestChannel, - RequestFromChannel, - requestFromChannelInjectionToken, -} from "@k8slens/messaging"; +import { RequestChannel, RequestFromChannel, requestFromChannelInjectionToken } from "@k8slens/messaging"; const requestFromChannelInjectable = getInjectable({ id: "request-from-channel", instantiate: () => ((channel: RequestChannel) => { - throw new Error( - `Tried to request from channel "${channel.id}" but requesting in "main" it's not supported yet`, - ); + throw new Error(`Tried to request from channel "${channel.id}" but requesting in "main" it's not supported yet`); }) as unknown as RequestFromChannel, injectionToken: requestFromChannelInjectionToken, diff --git a/packages/technical-features/messaging/electron/main/src/send-message-to-channel/allow-communication-listener.injectable.ts b/packages/technical-features/messaging/electron/main/src/send-message-to-channel/allow-communication-listener.injectable.ts index af74d7a810..7b80adabc0 100644 --- a/packages/technical-features/messaging/electron/main/src/send-message-to-channel/allow-communication-listener.injectable.ts +++ b/packages/technical-features/messaging/electron/main/src/send-message-to-channel/allow-communication-listener.injectable.ts @@ -1,9 +1,7 @@ import { getMessageChannel, getMessageChannelListenerInjectable } from "@k8slens/messaging"; import frameIdsInjectable from "./frameIds.injectable"; -const frameCommunicationAdminChannel = getMessageChannel( - "frame-communication-admin-channel", -); +const frameCommunicationAdminChannel = getMessageChannel("frame-communication-admin-channel"); const allowCommunicationListenerInjectable = getMessageChannelListenerInjectable({ id: "allow-communication", diff --git a/packages/technical-features/messaging/electron/renderer/src/allow-communication-to-iframe.injectable.ts b/packages/technical-features/messaging/electron/renderer/src/allow-communication-to-iframe.injectable.ts index 59abe7f931..fe8928caca 100644 --- a/packages/technical-features/messaging/electron/renderer/src/allow-communication-to-iframe.injectable.ts +++ b/packages/technical-features/messaging/electron/renderer/src/allow-communication-to-iframe.injectable.ts @@ -2,9 +2,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; import { getMessageChannel, sendMessageToChannelInjectionToken } from "@k8slens/messaging"; -export const frameCommunicationAdminChannel = getMessageChannel( - "frame-communication-admin-channel", -); +export const frameCommunicationAdminChannel = getMessageChannel("frame-communication-admin-channel"); const allowCommunicationToIframeInjectable = getInjectable({ id: "allow-communication-to-iframe-injectable", diff --git a/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.test.ts b/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.test.ts index dc89c7b3e7..a325821b91 100644 --- a/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.test.ts +++ b/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.test.ts @@ -1,9 +1,6 @@ import type { IpcRendererEvent, IpcRenderer } from "electron"; import ipcRendererInjectable from "../ipc/ipc-renderer.injectable"; -import { - EnlistMessageChannelListener, - enlistMessageChannelListenerInjectionToken, -} from "@k8slens/messaging"; +import { EnlistMessageChannelListener, enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; import { createContainer } from "@ogre-tools/injectable"; import { registerFeature } from "@k8slens/feature-core"; import { messagingFeatureForRenderer } from "../feature"; diff --git a/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.test.ts b/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.test.ts index e91109df10..a3a3c86ad3 100644 --- a/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.test.ts +++ b/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.test.ts @@ -32,9 +32,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = { }; [{ scenarioIsAsync: true }, { scenarioIsAsync: false }].forEach(({ scenarioIsAsync }) => - describe(`get-message-bridge-fake, given running as ${ - scenarioIsAsync ? "async" : "sync" - }`, () => { + describe(`get-message-bridge-fake, given running as ${scenarioIsAsync ? "async" : "sync"}`, () => { let messageBridgeFake: any; beforeEach(() => { @@ -135,9 +133,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = { describe("given a message is sent in di-1", () => { beforeEach(() => { - const sendMessageToChannelFromDi1 = someDi1.inject( - sendMessageToChannelInjectionToken, - ); + const sendMessageToChannelFromDi1 = someDi1.inject(sendMessageToChannelInjectionToken); sendMessageToChannelFromDi1(someMessageChannel, "some-message"); }); @@ -161,13 +157,10 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = { }); it("the response gets handled in di-1", () => { - expect(someHandler1MockInDi1).toHaveBeenCalledWith( - "some-response-to: some-message", - { - frameId: 42, - processId: 42, - }, - ); + expect(someHandler1MockInDi1).toHaveBeenCalledWith("some-response-to: some-message", { + frameId: 42, + processId: 42, + }); }); scenarioIsAsync && @@ -191,13 +184,10 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = { }); it("the response gets handled in di-1", () => { - expect(someHandler1MockInDi1).toHaveBeenCalledWith( - "some-response-to: some-message", - { - frameId: 42, - processId: 42, - }, - ); + expect(someHandler1MockInDi1).toHaveBeenCalledWith("some-response-to: some-message", { + frameId: 42, + processId: 42, + }); }); }); }); @@ -375,9 +365,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = { const requestFromChannelFromDi2 = someDi2.inject(requestFromChannelInjectionToken); - return expect(() => - requestFromChannelFromDi2(someRequestChannel, "irrelevant"), - ).rejects.toThrow( + return expect(() => requestFromChannelFromDi2(someRequestChannel, "irrelevant")).rejects.toThrow( 'Tried to make a request but multiple listeners were discovered for channel "some-request-channel" in multiple DIs.', ); }); @@ -385,9 +373,7 @@ const someRequestChannelWithoutListeners: SomeRequestChannel = { it("when requesting from channel without listener, throws", () => { const requestFromChannel = someDi1.inject(requestFromChannelInjectionToken); - return expect(() => - requestFromChannel(someRequestChannelWithoutListeners, "irrelevant"), - ).rejects.toThrow( + return expect(() => requestFromChannel(someRequestChannelWithoutListeners, "irrelevant")).rejects.toThrow( 'Tried to make a request but no listeners for channel "some-request-channel-without-listeners" was discovered in any DIs', ); }); diff --git a/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts b/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts index 8feb31dcd2..3e7efea5f9 100644 --- a/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts +++ b/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts @@ -22,7 +22,7 @@ import asyncFn, { AsyncFnMock } from "@async-fn/jest"; export type MessageBridgeFake = { involve: (...dis: DiContainer[]) => void; messagePropagation: () => Promise; - messagePropagationRecursive: (callback: any) => any; + messagePropagationRecursive: (callback: () => any) => any; setAsync: (value: boolean) => void; }; @@ -167,9 +167,7 @@ export const getMessageBridgeFake = (): MessageBridgeFake => { await Promise.all(oldMessages.map((x) => wrapper(x.resolve))); }; - const messagePropagationRecursive = async ( - wrapper: (callback: any) => any = (callback) => callback(), - ) => { + const messagePropagationRecursive = async (wrapper = (callback: () => any) => callback()) => { while (messagePropagationBuffer.size) { await messagePropagation(wrapper); } diff --git a/packages/technical-features/react-application/src/react-application/react-application.tsx b/packages/technical-features/react-application/src/react-application/react-application.tsx index 933d0ad389..982a49a3c1 100644 --- a/packages/technical-features/react-application/src/react-application/react-application.tsx +++ b/packages/technical-features/react-application/src/react-application/react-application.tsx @@ -27,9 +27,7 @@ const render = (components: ReactApplicationHigherOrderComponent[]) => { export const ReactApplication = observer(({ di }: ReactApplicationProps) => { const computedInjectMany = di.inject(computedInjectManyInjectable); - const higherOrderComponents = computedInjectMany( - reactApplicationHigherOrderComponentInjectionToken, - ); + const higherOrderComponents = computedInjectMany(reactApplicationHigherOrderComponentInjectionToken); const Components = [...higherOrderComponents.get(), ReactApplicationContent]; diff --git a/packages/utility-features/react-testing-library-discovery/index.ts b/packages/utility-features/react-testing-library-discovery/index.ts index 109c6af90f..92e817512c 100644 --- a/packages/utility-features/react-testing-library-discovery/index.ts +++ b/packages/utility-features/react-testing-library-discovery/index.ts @@ -5,9 +5,4 @@ export type { QuerySingleElement, } from "./src/discovery-of-html-elements"; -export { - discoverFor, - getSingleElement, - queryAllElements, - querySingleElement, -} from "./src/discovery-of-html-elements"; +export { discoverFor, getSingleElement, queryAllElements, querySingleElement } from "./src/discovery-of-html-elements"; diff --git a/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts b/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts index 0df7d9202d..0722c74732 100644 --- a/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts +++ b/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts @@ -26,8 +26,7 @@ export interface Discover { getSingleElement: GetSingleElement; } -const getBaseElement = (source: DiscoverySourceTypes) => - "baseElement" in source ? source.baseElement : source; +const getBaseElement = (source: DiscoverySourceTypes) => ("baseElement" in source ? source.baseElement : source); export function querySingleElement(getSource: () => DiscoverySourceTypes): QuerySingleElement { return (attributeName, attributeValue) => { @@ -35,9 +34,7 @@ export function querySingleElement(getSource: () => DiscoverySourceTypes): Query const dataAttribute = `data-${attributeName}-test`; - const selector = attributeValue - ? `[${dataAttribute}="${attributeValue}"]` - : `[${dataAttribute}]`; + const selector = attributeValue ? `[${dataAttribute}="${attributeValue}"]` : `[${dataAttribute}]`; const discovered = getBaseElement(source).querySelector(selector); @@ -78,10 +75,7 @@ export function getSingleElement(getSource: () => DiscoverySourceTypes): GetSing return (attributeName, attributeValue) => { const dataAttribute = `data-${attributeName}-test`; - const { discovered, ...nestedDiscover } = querySingleElement(getSource)( - attributeName, - attributeValue, - ); + const { discovered, ...nestedDiscover } = querySingleElement(getSource)(attributeName, attributeValue); if (!discovered) { // eslint-disable-next-line xss/no-mixed-html @@ -97,18 +91,14 @@ export function getSingleElement(getSource: () => DiscoverySourceTypes): GetSing ); } - throw new Error( - `Couldn't find HTML-element with attribute "${dataAttribute}"\n\nHTML is:\n\n${html}`, - ); + throw new Error(`Couldn't find HTML-element with attribute "${dataAttribute}"\n\nHTML is:\n\n${html}`); } const click = () => { if ("click" in discovered && typeof discovered.click === "function") { discovered.click(); } else { - throw new Error( - `Tried to click something that was not clickable:\n\n${prettyDom(discovered)}`, - ); + throw new Error(`Tried to click something that was not clickable:\n\n${prettyDom(discovered)}`); } };