From b4c0ca981afb28b4c40831de2240934726d7f410 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Tue, 7 Mar 2023 08:34:29 -0800 Subject: [PATCH] Fix not showing some non-core kinds (#7303) * Fix not showing some non-core kinds - Specifically if a Kind is not present within the preferredVersion of a group then we don't know that resource exists Signed-off-by: Sebastian Malton * Add technical tests Signed-off-by: Sebastian Malton --------- Signed-off-by: Sebastian Malton --- .../src/main/cluster/request-api-versions.ts | 7 +- ...equest-non-core-api-versions.injectable.ts | 6 +- .../request-non-core-api-versions.test.ts | 167 ++++++++++++++++++ .../core/src/main/k8s-request.injectable.ts | 7 +- 4 files changed, 180 insertions(+), 7 deletions(-) create mode 100644 packages/core/src/main/cluster/request-non-core-api-versions.test.ts diff --git a/packages/core/src/main/cluster/request-api-versions.ts b/packages/core/src/main/cluster/request-api-versions.ts index be9ec2c21a..ac5a08e675 100644 --- a/packages/core/src/main/cluster/request-api-versions.ts +++ b/packages/core/src/main/cluster/request-api-versions.ts @@ -4,7 +4,6 @@ */ import { getInjectionToken } from "@ogre-tools/injectable"; -import type { Cluster } from "../../common/cluster/cluster"; import type { AsyncResult } from "../../common/utils/async-result"; export interface KubeResourceListGroup { @@ -12,7 +11,11 @@ export interface KubeResourceListGroup { path: string; } -export type RequestApiVersions = (cluster: Cluster) => Promise>; +export interface ClusterData { + readonly id: string; +} + +export type RequestApiVersions = (cluster: ClusterData) => Promise>; export const requestApiVersionsInjectionToken = getInjectionToken({ id: "request-api-versions-token", diff --git a/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts b/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts index e5b241c75f..25f8676c2a 100644 --- a/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts +++ b/packages/core/src/main/cluster/request-non-core-api-versions.injectable.ts @@ -20,10 +20,10 @@ const requestNonCoreApiVersionsInjectable = getInjectable({ return { callWasSuccessful: true, response: chain(groups.values()) - .filterMap(group => group.preferredVersion?.groupVersion && ({ + .flatMap(group => group.versions.map(version => ({ group: group.name, - path: `/apis/${group.preferredVersion.groupVersion}`, - })) + path: `/apis/${version.groupVersion}`, + }))) .collect(v => [...v]), }; } catch (error) { diff --git a/packages/core/src/main/cluster/request-non-core-api-versions.test.ts b/packages/core/src/main/cluster/request-non-core-api-versions.test.ts new file mode 100644 index 0000000000..34c2492b4f --- /dev/null +++ b/packages/core/src/main/cluster/request-non-core-api-versions.test.ts @@ -0,0 +1,167 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { AsyncFnMock } from "@async-fn/jest"; +import asyncFn from "@async-fn/jest"; +import type { V1APIGroupList } from "@kubernetes/client-node"; +import type { DiContainer } from "@ogre-tools/injectable"; +import { getDiForUnitTesting } from "../getDiForUnitTesting"; +import type { K8sRequest } from "../k8s-request.injectable"; +import k8sRequestInjectable from "../k8s-request.injectable"; +import type { RequestApiVersions } from "./request-api-versions"; +import requestNonCoreApiVersionsInjectable from "./request-non-core-api-versions.injectable"; + +describe("requestNonCoreApiVersions", () => { + let di: DiContainer; + let k8sRequestMock: AsyncFnMock; + let requestNonCoreApiVersions: RequestApiVersions; + + beforeEach(() => { + di = getDiForUnitTesting({ doGeneralOverrides: true }); + + k8sRequestMock = asyncFn(); + di.override(k8sRequestInjectable, () => k8sRequestMock); + + requestNonCoreApiVersions = di.inject(requestNonCoreApiVersionsInjectable); + }); + + describe("when called", () => { + let versionsRequest: ReturnType; + + beforeEach(() => { + versionsRequest = requestNonCoreApiVersions({ id: "some-cluster-id" }); + }); + + it("should request all api groups", () => { + expect(k8sRequestMock).toBeCalledWith({ id: "some-cluster-id" }, "/apis"); + }); + + describe("when api groups request resolves to empty", () => { + beforeEach(async () => { + await k8sRequestMock.resolve({ groups: [] } as V1APIGroupList); + }); + + it("should return empty list", async () => { + expect(await versionsRequest).toEqual({ + callWasSuccessful: true, + response: [], + }); + }); + }); + + describe("when api groups request resolves to single group", () => { + beforeEach(async () => { + await k8sRequestMock.resolve({ groups: [{ + name: "some-name", + versions: [{ + groupVersion: "some-name/v1", + version: "v1", + }], + }] } as V1APIGroupList); + }); + + it("should return single entry in list", async () => { + expect(await versionsRequest).toEqual({ + callWasSuccessful: true, + response: [{ + group: "some-name", + path: "/apis/some-name/v1", + }], + }); + }); + }); + + describe("when api groups request resolves to single group with multiple versions", () => { + beforeEach(async () => { + await k8sRequestMock.resolve({ groups: [{ + name: "some-name", + versions: [ + { + groupVersion: "some-name/v1", + version: "v1", + }, + { + groupVersion: "some-name/v1beta1", + version: "v1beta1", + }, + ], + }] } as V1APIGroupList); + }); + + it("should return multiple entries in list", async () => { + expect(await versionsRequest).toEqual({ + callWasSuccessful: true, + response: [ + { + group: "some-name", + path: "/apis/some-name/v1", + }, + { + group: "some-name", + path: "/apis/some-name/v1beta1", + }, + ], + }); + }); + }); + + describe("when api groups request resolves to multiple groups with multiple versions", () => { + beforeEach(async () => { + await k8sRequestMock.resolve({ groups: [ + { + name: "some-name", + versions: [ + { + groupVersion: "some-name/v1", + version: "v1", + }, + { + groupVersion: "some-name/v1beta1", + version: "v1beta1", + }, + ], + }, + { + name: "some-other-name.foo.com", + versions: [ + { + groupVersion: "some-other-name.foo.com/v1", + version: "v1", + }, + { + groupVersion: "some-other-name.foo.com/v1beta1", + version: "v1beta1", + }, + ], + }, + ] } as V1APIGroupList); + }); + + it("should return multiple entries in list", async () => { + expect(await versionsRequest).toEqual({ + callWasSuccessful: true, + response: [ + { + group: "some-name", + path: "/apis/some-name/v1", + }, + { + group: "some-name", + path: "/apis/some-name/v1beta1", + }, + { + group: "some-other-name.foo.com", + path: "/apis/some-other-name.foo.com/v1", + }, + { + group: "some-other-name.foo.com", + path: "/apis/some-other-name.foo.com/v1beta1", + }, + ], + }); + }); + }); + }); +}); diff --git a/packages/core/src/main/k8s-request.injectable.ts b/packages/core/src/main/k8s-request.injectable.ts index b15056b50b..70ca3c3b6e 100644 --- a/packages/core/src/main/k8s-request.injectable.ts +++ b/packages/core/src/main/k8s-request.injectable.ts @@ -2,7 +2,6 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { Cluster } from "../common/cluster/cluster"; import { getInjectable } from "@ogre-tools/injectable"; import type { LensRequestInit } from "../common/fetch/lens-fetch.injectable"; import lensFetchInjectable from "../common/fetch/lens-fetch.injectable"; @@ -12,7 +11,11 @@ export interface K8sRequestInit extends LensRequestInit { timeout?: number; } -export type K8sRequest = (cluster: Cluster, pathnameAndQuery: string, init?: K8sRequestInit) => Promise; +export interface ClusterData { + readonly id: string; +} + +export type K8sRequest = (cluster: ClusterData, pathnameAndQuery: string, init?: K8sRequestInit) => Promise; const k8sRequestInjectable = getInjectable({ id: "k8s-request",