From adcf87e93930960b8c8d0011e4d03bae7edd0a8c Mon Sep 17 00:00:00 2001 From: Jim Ehrismann <40840436+jim-docker@users.noreply.github.com> Date: Thu, 24 Mar 2022 19:02:31 -0400 Subject: [PATCH] Release/v5.4.4 (#5099) * Emit AppEvent when opening Catalog and changing Catalog Category (#5071) * Emit AppEvent when opening Catalog and changing Catalog Category. Signed-off-by: Panu Horsmalahti * Add getName method to CatalogCategory Signed-off-by: Panu Horsmalahti * Fix winston logger missing splat format (#5070) * Fix KubeJsonApi.forCluster throwing on renderer (#5034) * test relied on typing of newer version of ogre-tools Signed-off-by: Jim Ehrismann * release v5.4.4 Signed-off-by: Jim Ehrismann Co-authored-by: Panu Horsmalahti Co-authored-by: Jari Kolehmainen Co-authored-by: Sebastian Malton --- package.json | 2 +- src/common/__tests__/catalog-entity.test.ts | 30 ++++++++++ src/common/catalog/catalog-entity.ts | 7 +++ src/common/k8s-api/api-base.ts | 40 +++++++++++++ src/common/k8s-api/api-kube.ts | 20 +++++++ src/common/k8s-api/index.ts | 57 +------------------ src/common/k8s-api/kube-json-api.ts | 8 +-- src/common/logger.ts | 5 +- src/main/__test__/cluster.test.ts | 1 + src/main/__test__/kube-auth-proxy.test.ts | 1 + src/main/__test__/kubeconfig-manager.test.ts | 1 + src/main/app-updater.ts | 8 +-- .../components/+catalog/catalog.test.tsx | 47 +++++++++++++++ src/renderer/components/+catalog/catalog.tsx | 17 ++++++ 14 files changed, 179 insertions(+), 65 deletions(-) create mode 100644 src/common/__tests__/catalog-entity.test.ts create mode 100644 src/common/k8s-api/api-base.ts create mode 100644 src/common/k8s-api/api-kube.ts diff --git a/package.json b/package.json index 57682bc695..d987348eb1 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.4.3", + "version": "5.4.4", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", diff --git a/src/common/__tests__/catalog-entity.test.ts b/src/common/__tests__/catalog-entity.test.ts new file mode 100644 index 0000000000..3785ed1d49 --- /dev/null +++ b/src/common/__tests__/catalog-entity.test.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { CatalogCategory, CatalogCategorySpec } from "../catalog"; + +class TestCatalogCategory extends CatalogCategory { + public readonly apiVersion = "catalog.k8slens.dev/v1alpha1"; + public readonly kind = "CatalogCategory"; + public metadata = { + name: "Test Category", + icon: "", + }; + public spec: CatalogCategorySpec = { + group: "entity.k8slens.dev", + versions: [], + names: { + kind: "Test", + }, + }; +} + +describe("CatalogCategory", () => { + it("returns name", () => { + const category = new TestCatalogCategory(); + + expect(category.getName()).toEqual("Test Category"); + }); +}); diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts index daff2e6017..2704bed2e0 100644 --- a/src/common/catalog/catalog-entity.ts +++ b/src/common/catalog/catalog-entity.ts @@ -173,6 +173,13 @@ export abstract class CatalogCategory extends (EventEmitter as new () => TypedEm return `${this.spec.group}/${this.spec.names.kind}`; } + /** + * Get the name of this category + */ + public getName(): string { + return this.metadata.name; + } + /** * Add a filter for menu items of catalogAddMenu * @param fn The function that should return a truthy value if that menu item should be displayed diff --git a/src/common/k8s-api/api-base.ts b/src/common/k8s-api/api-base.ts new file mode 100644 index 0000000000..e511dd8454 --- /dev/null +++ b/src/common/k8s-api/api-base.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { JsonApi } from "./json-api"; +import { apiPrefix, isDebugging, isDevelopment } from "../vars"; +import { appEventBus } from "../app-event-bus/event-bus"; + +export let apiBase: JsonApi; + +if (typeof window === "undefined") { + appEventBus.addListener((event) => { + if (event.name !== "lens-proxy" && event.action !== "listen") return; + + const params = event.params as { port?: number }; + + if (!params.port) return; + + apiBase = new JsonApi({ + serverAddress: `http://127.0.0.1:${params.port}`, + apiBase: apiPrefix, + debug: isDevelopment || isDebugging, + }, { + headers: { + "Host": `localhost:${params.port}`, + }, + }); + }); +} else { + apiBase = new JsonApi({ + serverAddress: `http://127.0.0.1:${window.location.port}`, + apiBase: apiPrefix, + debug: isDevelopment || isDebugging, + }, { + headers: { + "Host": window.location.host, + }, + }); +} diff --git a/src/common/k8s-api/api-kube.ts b/src/common/k8s-api/api-kube.ts new file mode 100644 index 0000000000..5ff8478694 --- /dev/null +++ b/src/common/k8s-api/api-kube.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 { isClusterPageContext } from "../utils"; +import { KubeJsonApi } from "./kube-json-api"; +import { apiKubePrefix, isDevelopment } from "../vars"; + +export const apiKube = isClusterPageContext() + ? new KubeJsonApi({ + serverAddress: `http://127.0.0.1:${window.location.port}`, + apiBase: apiKubePrefix, + debug: isDevelopment, + }, { + headers: { + "Host": window.location.host, + }, + }) + : undefined; diff --git a/src/common/k8s-api/index.ts b/src/common/k8s-api/index.ts index 441cb98687..0d47a643f3 100644 --- a/src/common/k8s-api/index.ts +++ b/src/common/k8s-api/index.ts @@ -3,58 +3,5 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { JsonApi } from "./json-api"; -import { KubeJsonApi } from "./kube-json-api"; -import { apiKubePrefix, apiPrefix, isDebugging, isDevelopment } from "../../common/vars"; -import { isClusterPageContext } from "../utils/cluster-id-url-parsing"; -import { appEventBus } from "../app-event-bus/event-bus"; - -let apiBase: JsonApi; -let apiKube: KubeJsonApi; - -if (typeof window === "undefined") { - appEventBus.addListener((event) => { - if (event.name !== "lens-proxy" && event.action !== "listen") return; - - const params = event.params as { port?: number }; - - if (!params.port) return; - - apiBase = new JsonApi({ - serverAddress: `http://127.0.0.1:${params.port}`, - apiBase: apiPrefix, - debug: isDevelopment || isDebugging, - }, { - headers: { - "Host": `localhost:${params.port}`, - }, - }); - }); -} else { - apiBase = new JsonApi({ - serverAddress: `http://127.0.0.1:${window.location.port}`, - apiBase: apiPrefix, - debug: isDevelopment || isDebugging, - }, { - headers: { - "Host": window.location.host, - }, - }); -} - -if (isClusterPageContext()) { - apiKube = new KubeJsonApi({ - serverAddress: `http://127.0.0.1:${window.location.port}`, - apiBase: apiKubePrefix, - debug: isDevelopment, - }, { - headers: { - "Host": window.location.host, - }, - }); -} - -export { - apiBase, - apiKube, -}; +export { apiBase } from "./api-base"; +export { apiKube } from "./api-kube"; diff --git a/src/common/k8s-api/kube-json-api.ts b/src/common/k8s-api/kube-json-api.ts index bc578641c3..30131545fe 100644 --- a/src/common/k8s-api/kube-json-api.ts +++ b/src/common/k8s-api/kube-json-api.ts @@ -5,8 +5,8 @@ import { JsonApi, JsonApiData, JsonApiError } from "./json-api"; import type { Response } from "node-fetch"; -import { LensProxy } from "../../main/lens-proxy"; import { apiKubePrefix, isDebugging } from "../vars"; +import { apiBase } from "./api-base"; export interface KubeJsonApiListMetadata { resourceVersion: string; @@ -57,15 +57,15 @@ export interface KubeJsonApiError extends JsonApiError { export class KubeJsonApi extends JsonApi { static forCluster(clusterId: string): KubeJsonApi { - const port = LensProxy.getInstance().port; + const url = new URL(apiBase.config.serverAddress); return new this({ - serverAddress: `http://127.0.0.1:${port}`, + serverAddress: apiBase.config.serverAddress, apiBase: apiKubePrefix, debug: isDebugging, }, { headers: { - "Host": `${clusterId}.localhost:${port}`, + "Host": `${clusterId}.localhost:${url.port}`, }, }); } diff --git a/src/common/logger.ts b/src/common/logger.ts index 504b0e126d..f29008818b 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -65,6 +65,9 @@ if (ipcMain) { } export default winston.createLogger({ - format: format.simple(), + format: format.combine( + format.splat(), + format.simple(), + ), transports, }); diff --git a/src/main/__test__/cluster.test.ts b/src/main/__test__/cluster.test.ts index 4fa5f5cdd3..065c785df2 100644 --- a/src/main/__test__/cluster.test.ts +++ b/src/main/__test__/cluster.test.ts @@ -20,6 +20,7 @@ jest.mock("winston", () => ({ label: jest.fn(), timestamp: jest.fn(), printf: jest.fn(), + splat: jest.fn(), }, createLogger: jest.fn().mockReturnValue(logger), transports: { diff --git a/src/main/__test__/kube-auth-proxy.test.ts b/src/main/__test__/kube-auth-proxy.test.ts index 7a3f4ea082..b4e6a71f66 100644 --- a/src/main/__test__/kube-auth-proxy.test.ts +++ b/src/main/__test__/kube-auth-proxy.test.ts @@ -15,6 +15,7 @@ jest.mock("winston", () => ({ printf: jest.fn(), padLevels: jest.fn(), ms: jest.fn(), + splat: jest.fn(), }, createLogger: jest.fn().mockReturnValue({ silly: jest.fn(), diff --git a/src/main/__test__/kubeconfig-manager.test.ts b/src/main/__test__/kubeconfig-manager.test.ts index 53808c6ce9..0894a41cbe 100644 --- a/src/main/__test__/kubeconfig-manager.test.ts +++ b/src/main/__test__/kubeconfig-manager.test.ts @@ -21,6 +21,7 @@ jest.mock("winston", () => ({ padLevels: jest.fn(), ms: jest.fn(), printf: jest.fn(), + splat: jest.fn(), }, createLogger: jest.fn().mockReturnValue(logger), transports: { diff --git a/src/main/app-updater.ts b/src/main/app-updater.ts index e63da9453e..b30d799677 100644 --- a/src/main/app-updater.ts +++ b/src/main/app-updater.ts @@ -52,10 +52,10 @@ function handleAutoUpdateBackChannel(event: Electron.IpcMainEvent, ...[arg]: Upd } autoUpdater.logger = { - info: message => logger.info(`[AUTO-UPDATE]: electron-updater:`, message), - warn: message => logger.warn(`[AUTO-UPDATE]: electron-updater:`, message), - error: message => logger.error(`[AUTO-UPDATE]: electron-updater:`, message), - debug: message => logger.debug(`[AUTO-UPDATE]: electron-updater:`, message), + info: message => logger.info(`[AUTO-UPDATE]: electron-updater: %s`, message), + warn: message => logger.warn(`[AUTO-UPDATE]: electron-updater: %s`, message), + error: message => logger.error(`[AUTO-UPDATE]: electron-updater: %s`, message), + debug: message => logger.debug(`[AUTO-UPDATE]: electron-updater: %s`, message), }; /** diff --git a/src/renderer/components/+catalog/catalog.test.tsx b/src/renderer/components/+catalog/catalog.test.tsx index e0ae3d217e..54beb21c5c 100644 --- a/src/renderer/components/+catalog/catalog.test.tsx +++ b/src/renderer/components/+catalog/catalog.test.tsx @@ -25,6 +25,9 @@ import { UserStore } from "../../../common/user-store"; import mockFs from "mock-fs"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import type { AppEvent } from "../../../common/app-event-bus/event-bus"; +import appEventBusInjectable from "../../../common/app-event-bus/app-event-bus.injectable"; +import { EventEmitter } from "../../../common/event-emitter"; mockWindow(); jest.mock("electron", () => ({ @@ -98,6 +101,7 @@ describe("", () => { let di: DependencyInjectionContainer; let catalogEntityStore: CatalogEntityStore; let catalogEntityRegistry: CatalogEntityRegistry; + let emitEvent: (event: AppEvent) => void; let render: DiRender; beforeEach(async () => { @@ -121,6 +125,14 @@ describe("", () => { di.override(catalogEntityRegistryInjectable, () => catalogEntityRegistry); + emitEvent = jest.fn(); + + const source = new EventEmitter(); + + source.emit = emitEvent; + + di.override(appEventBusInjectable, () => source); + catalogEntityStore = di.inject(catalogEntityStoreInjectable); }); @@ -316,4 +328,39 @@ describe("", () => { userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); }); + + it("emits catalog open AppEvent", () => { + render( + , + ); + + expect(emitEvent).toHaveBeenCalledWith( { + action: "open", + name: "catalog", + }); + }); + + it("emits catalog change AppEvent when changing the category", () => { + render( + , + ); + + userEvent.click(screen.getByText("Web Links")); + + expect(emitEvent).toHaveBeenLastCalledWith({ + action: "change-category", + name: "catalog", + params: { + category: "Web Links", + }, + }); + }); }); diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 0287925e32..b3b7ec0083 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -36,6 +36,8 @@ import getCategoryColumnsInjectable from "./get-category-columns.injectable"; import type { RegisteredCustomCategoryViewDecl } from "./custom-views.injectable"; import customCategoryViewsInjectable from "./custom-views.injectable"; import type { CustomCategoryViewComponents } from "./custom-views"; +import type { AppEvent } from "../../../common/app-event-bus/event-bus"; +import appEventBusInjectable from "../../../common/app-event-bus/app-event-bus.injectable"; interface Props extends RouteComponentProps {} @@ -44,6 +46,7 @@ interface Dependencies { catalogEntityStore: CatalogEntityStore; getCategoryColumns: (params: GetCategoryColumnsParams) => CategoryColumns; customCategoryViews: IComputedValue>>; + emitEvent: (event: AppEvent) => void; } @observer @@ -104,6 +107,11 @@ class NonInjectedCatalog extends React.Component { }); } })); + + this.props.emitEvent({ + name: "catalog", + action: "open", + }); } addToHotbar(entity: CatalogEntity): void { @@ -146,6 +154,14 @@ class NonInjectedCatalog extends React.Component { onTabChange = action((tabId: string | null) => { const activeCategory = this.categories.find(category => category.getId() === tabId); + this.props.emitEvent({ + name: "catalog", + action: "change-category", + params: { + category: activeCategory ? activeCategory.getName() : "Browse", + }, + }); + if (activeCategory) { navigate(catalogURL({ params: { group: activeCategory.spec.group, kind: activeCategory.spec.names.kind }})); } else { @@ -311,6 +327,7 @@ export const Catalog = withInjectables( NonInjectedCatalog, catalogPreviousActiveTabStorage: di.inject(catalogPreviousActiveTabStorageInjectable), getCategoryColumns: di.inject(getCategoryColumnsInjectable), customCategoryViews: di.inject(customCategoryViewsInjectable), + emitEvent: di.inject(appEventBusInjectable).emit, ...props, }), });