From 1bc837608ce90bccd2edb59187f041eeee777a9a Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 3 May 2023 10:06:46 -0400 Subject: [PATCH] chore: Fix overriding errors by improving abstraction usage Signed-off-by: Sebastian Malton --- .../request-config-map.injectable.ts | 20 ++++++ .../request-secret.injectable.ts | 20 ++++++ .../service-accounts/details.tsx | 6 +- .../__tests__/pod-container-env.test.tsx | 8 +-- .../workloads-pods/pod-container-env.tsx | 62 ++++++++++++------- .../workloads-pods/secret-key.test.tsx | 6 +- .../components/workloads-pods/secret-key.tsx | 12 ++-- 7 files changed, 97 insertions(+), 37 deletions(-) create mode 100644 packages/core/src/renderer/components/config-maps/request-config-map.injectable.ts create mode 100644 packages/core/src/renderer/components/config-secrets/request-secret.injectable.ts diff --git a/packages/core/src/renderer/components/config-maps/request-config-map.injectable.ts b/packages/core/src/renderer/components/config-maps/request-config-map.injectable.ts new file mode 100644 index 0000000000..d44b01198a --- /dev/null +++ b/packages/core/src/renderer/components/config-maps/request-config-map.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 type { ConfigMapStore } from "./store"; +import configMapStoreInjectable from "./store.injectable"; + +export type RequestConfigMap = ConfigMapStore["load"]; + +const requestConfigMapInjectable = getInjectable({ + id: "request-config-map", + instantiate: (di): RequestConfigMap => { + const configMapStore = di.inject(configMapStoreInjectable); + + return (ref) => configMapStore.load(ref); + }, +}); + +export default requestConfigMapInjectable; diff --git a/packages/core/src/renderer/components/config-secrets/request-secret.injectable.ts b/packages/core/src/renderer/components/config-secrets/request-secret.injectable.ts new file mode 100644 index 0000000000..53f9052b81 --- /dev/null +++ b/packages/core/src/renderer/components/config-secrets/request-secret.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 type { SecretStore } from "./store"; +import secretStoreInjectable from "./store.injectable"; + +export type RequestSecret = SecretStore["load"]; + +const requestSecretInjectable = getInjectable({ + id: "request-secret", + instantiate: (di): RequestSecret => { + const secretStore = di.inject(secretStoreInjectable); + + return (ref) => secretStore.load(ref); + }, +}); + +export default requestSecretInjectable; diff --git a/packages/core/src/renderer/components/user-management/service-accounts/details.tsx b/packages/core/src/renderer/components/user-management/service-accounts/details.tsx index 5c17a6f03d..153e1caf0d 100644 --- a/packages/core/src/renderer/components/user-management/service-accounts/details.tsx +++ b/packages/core/src/renderer/components/user-management/service-accounts/details.tsx @@ -22,9 +22,12 @@ import type { GetDetailsUrl } from "../../kube-detail-params/get-details-url.inj import { withInjectables } from "@ogre-tools/injectable-react"; import getDetailsUrlInjectable from "../../kube-detail-params/get-details-url.injectable"; import secretStoreInjectable from "../../config-secrets/store.injectable"; +import type { RequestSecret } from "../../config-secrets/request-secret.injectable"; +import requestSecretInjectable from "../../config-secrets/request-secret.injectable"; interface Dependencies { secretStore: SecretStore; + requestSecret: RequestSecret; getDetailsUrl: GetDetailsUrl; } @@ -35,7 +38,7 @@ class NonInjectedServiceAccountsDetails extends React.Component ( ({ name }: { name: string }) => ( - this.props.secretStore.load({ name, namespace }) + this.props.requestSecret({ name, namespace }) .catch(() => name) ) ); @@ -164,5 +167,6 @@ export const ServiceAccountsDetails = withInjectables", () => { let render: DiRender; @@ -36,8 +36,8 @@ describe("", () => { getByName: jest.fn(), }); - di.override(secretStoreInjectable, () => secretStore as jest.Mocked); - di.override(configMapStoreInjectable, () => configMapStore as jest.Mocked); + di.override(requestSecretInjectable, () => () => Promise.resolve({} as Secret)); + di.override(requestConfigMapInjectable, () => () => Promise.resolve({} as ConfigMap)); render = renderFor(di); }); diff --git a/packages/core/src/renderer/components/workloads-pods/pod-container-env.tsx b/packages/core/src/renderer/components/workloads-pods/pod-container-env.tsx index 436987f765..f9a4b27e3e 100644 --- a/packages/core/src/renderer/components/workloads-pods/pod-container-env.tsx +++ b/packages/core/src/renderer/components/workloads-pods/pod-container-env.tsx @@ -5,19 +5,20 @@ import "./pod-container-env.scss"; -import React, { useEffect } from "react"; +import React, { useEffect, useRef } from "react"; import { observer } from "mobx-react"; -import type { Container } from "@k8slens/kube-object"; +import type { ConfigMap, Container, Secret } from "@k8slens/kube-object"; import { DrawerItem } from "../drawer"; -import { autorun } from "mobx"; -import { object } from "@k8slens/utilities"; +import { autorun, observable } from "mobx"; +import { getOrInsertWith, object } from "@k8slens/utilities"; import _ from "lodash"; -import { withInjectables } from "@ogre-tools/injectable-react"; -import type { ConfigMapStore } from "../config-maps/store"; -import type { SecretStore } from "../config-secrets/store"; -import configMapStoreInjectable from "../config-maps/store.injectable"; -import secretStoreInjectable from "../config-secrets/store.injectable"; +import type { IAsyncComputed } from "@ogre-tools/injectable-react"; +import { asyncComputed, withInjectables } from "@ogre-tools/injectable-react"; import { SecretKey } from "./secret-key"; +import type { RequestSecret } from "../config-secrets/request-secret.injectable"; +import requestSecretInjectable from "../config-secrets/request-secret.injectable"; +import type { RequestConfigMap } from "../config-maps/request-config-map.injectable"; +import requestConfigMapInjectable from "../config-maps/request-config-map.injectable"; export interface ContainerEnvironmentProps { container: Container; @@ -25,32 +26,49 @@ export interface ContainerEnvironmentProps { } interface Dependencies { - configMapStore: ConfigMapStore; - secretStore: SecretStore; + requestConfigMap: RequestConfigMap; + requestSecret: RequestSecret; } const NonInjectedContainerEnvironment = observer((props: Dependencies & ContainerEnvironmentProps) => { const { container: { env, envFrom = [] }, namespace, - configMapStore, - secretStore, + requestConfigMap, + requestSecret, } = props; + const secrets = useRef(observable.map>()); + const configMaps = useRef(observable.map>()); + useEffect( () => autorun(() => { for (const { valueFrom } of env ?? []) { - if (valueFrom?.configMapKeyRef?.name) { - void configMapStore.load({ name: valueFrom.configMapKeyRef.name, namespace }); + const { configMapKeyRef: { name } = { name: undefined }} = valueFrom ?? {}; + + if (name) { + getOrInsertWith(configMaps.current, name, () => asyncComputed({ + betweenUpdates: "show-latest-value", + valueWhenPending: undefined, + getValueFromObservedPromise: () => requestConfigMap({ name, namespace }), + })); } } for (const { configMapRef, secretRef } of envFrom ?? []) { if (secretRef?.name) { - void secretStore.load({ name: secretRef.name, namespace }); + getOrInsertWith(secrets.current, secretRef.name, () => asyncComputed({ + betweenUpdates: "show-latest-value", + valueWhenPending: undefined, + getValueFromObservedPromise: () => requestSecret({ name: secretRef.name, namespace }), + })); } if (configMapRef?.name) { - void configMapStore.load({ name: configMapRef.name, namespace }); + getOrInsertWith(configMaps.current, configMapRef.name, () => asyncComputed({ + betweenUpdates: "show-latest-value", + valueWhenPending: undefined, + getValueFromObservedPromise: () => requestConfigMap({ name: configMapRef.name, namespace }), + })); } } }), []); @@ -83,7 +101,7 @@ const NonInjectedContainerEnvironment = observer((props: Dependencies & Containe ); } else if (configMapKeyRef?.name) { const { name, key } = configMapKeyRef; - const configMap = configMapStore.getByName(name, namespace); + const configMap = configMaps.current.get(name)?.value.get(); secretValue = configMap ? configMap.data[key] @@ -117,7 +135,7 @@ const NonInjectedContainerEnvironment = observer((props: Dependencies & Containe ); const renderEnvFromConfigMap = (configMapName: string, prefix: string | undefined) => { - const configMap = configMapStore.getByName(configMapName, namespace); + const configMap = configMaps.current.get(configMapName)?.value.get(); if (!configMap) return null; @@ -135,7 +153,7 @@ const NonInjectedContainerEnvironment = observer((props: Dependencies & Containe }; const renderEnvFromSecret = (secretName: string, prefix: string | undefined) => { - const secret = secretStore.getByName(secretName, namespace); + const secret = secrets.current.get(secretName)?.value.get(); if (!secret) return null; @@ -169,7 +187,7 @@ const NonInjectedContainerEnvironment = observer((props: Dependencies & Containe export const ContainerEnvironment = withInjectables(NonInjectedContainerEnvironment, { getProps: (di, props) => ({ ...props, - configMapStore: di.inject(configMapStoreInjectable), - secretStore: di.inject(secretStoreInjectable), + requestConfigMap: di.inject(requestConfigMapInjectable), + requestSecret: di.inject(requestSecretInjectable), }), }); diff --git a/packages/core/src/renderer/components/workloads-pods/secret-key.test.tsx b/packages/core/src/renderer/components/workloads-pods/secret-key.test.tsx index 1abdef621d..6188ffb7bc 100644 --- a/packages/core/src/renderer/components/workloads-pods/secret-key.test.tsx +++ b/packages/core/src/renderer/components/workloads-pods/secret-key.test.tsx @@ -10,11 +10,11 @@ import type { RenderResult } from "@testing-library/react"; import { act } from "@testing-library/react"; import React from "react"; import type { SecretStore } from "../config-secrets/store"; -import secretStoreInjectable from "../config-secrets/store.injectable"; import { Secret, SecretType } from "@k8slens/kube-object"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import { renderFor } from "../test-utils/renderFor"; import { SecretKey } from "./secret-key"; +import requestSecretInjectable from "../config-secrets/request-secret.injectable"; describe("SecretKey technical tests", () => { let loadSecretMock: AsyncFnMock; @@ -25,9 +25,7 @@ describe("SecretKey technical tests", () => { const render = renderFor(di); loadSecretMock = asyncFn(); - di.override(secretStoreInjectable, () => ({ - load: loadSecretMock, - } as Partial as SecretStore)); + di.override(requestSecretInjectable, () => loadSecretMock); result = render(( ; @@ -17,12 +17,12 @@ export interface SecretKeyProps { } interface Dependencies { - secretStore: SecretStore; + requestSecret: RequestSecret; } const NonInjectedSecretKey = (props: SecretKeyProps & Dependencies) => { const { - reference: { name, key }, namespace, secretStore, + reference: { name, key }, namespace, requestSecret, } = props; const [loading, setLoading] = useState(false); @@ -38,7 +38,7 @@ const NonInjectedSecretKey = (props: SecretKeyProps & Dependencies) => { void (async () => { try { - const secret = await secretStore.load({ name, namespace }); + const secret = await requestSecret({ name, namespace }); try { setSecretData(base64.decode(secret.data[key] ?? "")); @@ -82,6 +82,6 @@ const NonInjectedSecretKey = (props: SecretKeyProps & Dependencies) => { export const SecretKey = withInjectables(NonInjectedSecretKey, { getProps: (di, props) => ({ ...props, - secretStore: di.inject(secretStoreInjectable), + requestSecret: di.inject(requestSecretInjectable), }), });