diff --git a/src/common/k8s-api/api-manager/api-manager.ts b/src/common/k8s-api/api-manager/api-manager.ts index 080ccb671a..c2d52d8a75 100644 --- a/src/common/k8s-api/api-manager/api-manager.ts +++ b/src/common/k8s-api/api-manager/api-manager.ts @@ -5,11 +5,12 @@ import type { KubeObjectStore } from "../kube-object.store"; -import { action, observable, makeObservable } from "mobx"; -import { autoBind, isDefined, iter } from "../../utils"; +import type { IComputedValue } from "mobx"; +import { action, observable } from "mobx"; import type { KubeApi } from "../kube-api"; -import type { KubeJsonApiDataFor, KubeObject, ObjectReference } from "../kube-object"; +import type { KubeObject, ObjectReference } from "../kube-object"; import { parseKubeApi, createKubeApiURL } from "../kube-api-parse"; +import { chain } from "../../utils/iter"; export type RegisterableStore = Store extends KubeObjectStore ? Store @@ -21,25 +22,35 @@ export type KubeObjectStoreFrom = Api extends KubeApi : never; +export type FindApiCallback = (api: KubeApi) => boolean; + +interface Dependencies { + readonly apis: IComputedValue; + readonly stores: IComputedValue; +} + export class ApiManager { - private readonly apis = observable.map(); - private readonly stores = observable.map(); + private readonly externalApis = observable.array(); + private readonly externalStores = observable.array(); - constructor() { - makeObservable(this); - autoBind(this); - } + constructor(private readonly dependencies: Dependencies) {} - getApi(pathOrCallback: string | ((api: KubeApi) => boolean)) { - if (typeof pathOrCallback === "string") { - return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase); - } + getApi(pathOrCallback: string | FindApiCallback) { + const callback: FindApiCallback = typeof pathOrCallback === "function" + ? pathOrCallback + : (() => { + const { apiBase } = parseKubeApi(pathOrCallback); - return iter.find(this.apis.values(), pathOrCallback ?? (() => true)); + return api => api.apiBase === apiBase; + })(); + + return chain(this.dependencies.apis.get().values()) + .concat(this.externalApis.values()) + .find(callback); } getApiByKind(kind: string, apiVersion: string) { - return iter.find(this.apis.values(), api => api.kind === kind && api.apiVersionWithGroup === apiVersion); + return this.getApi(api => api.kind === kind && api.apiVersionWithGroup === apiVersion); } registerApi(api: RegisterableApi): void; @@ -47,45 +58,23 @@ export class ApiManager { * @deprecated Just register the `api` by itself */ registerApi(apiBase: string, api: RegisterableApi): void; - registerApi(apiBaseRaw: string | RegisterableApi, apiRaw?: RegisterableApi) { - const api = typeof apiBaseRaw === "string" - ? apiRaw - : apiBaseRaw; - - if (!api?.apiBase) { - return; - } - - if (!this.apis.has(api.apiBase)) { - this.stores.forEach((store) => { - if (store.api === api) { - this.stores.set(api.apiBase, store); - } - }); - - this.apis.set(api.apiBase, api); + registerApi(...args: [RegisterableApi] | [string, RegisterableApi]) { + if (args.length === 1) { + this.externalApis.push(args[0]); + } else { + this.externalApis.push(args[1]); } } - protected resolveApi(api: undefined | string | KubeApi): KubeApi | undefined { - if (!api) { - return undefined; - } + unregisterApi(apiOrBase: string | KubeApi) { + if (typeof apiOrBase === "string") { + const api = this.externalApis.find(api => api.apiBase === apiOrBase); - if (typeof api === "string") { - return this.getApi(api); - } - - return api; - } - - unregisterApi(api: string | KubeApi) { - if (typeof api === "string") this.apis.delete(api); - else { - const apis = Array.from(this.apis.entries()); - const entry = apis.find(entry => entry[1] === api); - - if (entry) this.unregisterApi(entry[0]); + if (api) { + this.externalApis.remove(api); + } + } else { + this.unregisterApi(apiOrBase.apiBase); } } @@ -93,15 +82,11 @@ export class ApiManager { /** * @deprecated KubeObjectStore's should only every be about a single KubeApi type */ - registerStore(store: KubeObjectStore, KubeJsonApiDataFor>, apis: KubeApi[]): void; + registerStore(store: RegisterableStore, apis: KubeApi[]): void; @action - registerStore(store: KubeObjectStore, KubeJsonApiDataFor>, apis: KubeApi[] = [store.api]): void { - for (const api of apis.filter(isDefined)) { - if (api.apiBase) { - this.stores.set(api.apiBase, store as never); - } - } + registerStore(store: RegisterableStore): void { + this.externalStores.push(store); } getStore(api: string | undefined): KubeObjectStore | undefined; @@ -110,14 +95,18 @@ export class ApiManager { * @deprecated use an actual cast instead of hiding it with this unused type param */ getStore(api: string | KubeApi): Store | undefined ; - getStore(api: string | KubeApi | undefined): KubeObjectStore | undefined { - const { apiBase } = this.resolveApi(api) ?? {}; - - if (apiBase) { - return this.stores.get(apiBase); + getStore(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined { + if (!apiOrBase) { + return undefined; } - return undefined; + const { apiBase } = typeof apiOrBase === "string" + ? parseKubeApi(apiOrBase) + : apiOrBase; + + return chain(this.dependencies.stores.get().values()) + .concat(this.externalStores.values()) + .find(store => store.api.apiBase === apiBase); } lookupApiLink(ref: ObjectReference, parentObject?: KubeObject): string { @@ -132,7 +121,7 @@ export class ApiManager { const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion); if (api) { - return api.getUrl({ namespace, name }); + return api.formatUrlForNotListing({ namespace, name }); } // lookup api by generated resource link @@ -151,7 +140,7 @@ export class ApiManager { const apiByKind = this.getApi(api => api.kind === kind); if (apiByKind) { - return apiByKind.getUrl({ name, namespace }); + return apiByKind.formatUrlForNotListing({ name, namespace }); } // otherwise generate link with default prefix diff --git a/src/common/k8s-api/api-manager/kube-object-store-token.ts b/src/common/k8s-api/api-manager/kube-object-store-token.ts new file mode 100644 index 0000000000..bbf272db24 --- /dev/null +++ b/src/common/k8s-api/api-manager/kube-object-store-token.ts @@ -0,0 +1,10 @@ +/** + * 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"; +import type { KubeObjectStore } from "../kube-object.store"; + +export const kubeObjectStoreInjectionToken = getInjectionToken>({ + id: "kube-object-store-token", +}); diff --git a/src/common/k8s-api/api-manager/manager.injectable.ts b/src/common/k8s-api/api-manager/manager.injectable.ts index 45188cec7a..f0b61c28b6 100644 --- a/src/common/k8s-api/api-manager/manager.injectable.ts +++ b/src/common/k8s-api/api-manager/manager.injectable.ts @@ -2,29 +2,28 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; +import { getInjectable } from "@ogre-tools/injectable"; import { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token"; -import type { KubeObjectStore } from "../kube-object.store"; import { ApiManager } from "./api-manager"; - -export const kubeObjectStoreInjectionToken = getInjectionToken>({ - id: "kube-object-store-token", -}); +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; +import { kubeObjectStoreInjectionToken } from "./kube-object-store-token"; +import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token"; +import { computed } from "mobx"; const apiManagerInjectable = getInjectable({ id: "api-manager", instantiate: (di) => { - const apiManager = new ApiManager(); + const computedInjectMany = di.inject(computedInjectManyInjectable); + const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken); - if (di.inject(storesAndApisCanBeCreatedInjectionToken)) { - const stores = di.injectMany(kubeObjectStoreInjectionToken); - - for (const store of stores) { - apiManager.registerStore(store); - } - } - - return apiManager; + return new ApiManager({ + apis: storesAndApisCanBeCreated + ? computedInjectMany(kubeApiInjectionToken) + : computed(() => []), + stores: storesAndApisCanBeCreated + ? computedInjectMany(kubeObjectStoreInjectionToken) + : computed(() => []), + }); }, });