1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Make sure that stores can still be retrieved

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-03-20 13:43:05 -04:00
parent 886c812679
commit 5856210aa9
6 changed files with 89 additions and 48 deletions

View File

@ -24,6 +24,7 @@ import { KubeApi as ExternalKubeApi } from "../../../extensions/common-api/k8s-a
import { Cluster } from "../../cluster/cluster";
import { runInAction } from "mobx";
import { customResourceDefinitionApiInjectionToken } from "../api-manager/crd-api-token";
import assert from "assert";
class TestApi extends KubeApi<KubeObject> {
protected async checkPreferredVersion() {
@ -156,6 +157,10 @@ describe("ApiManager", () => {
});
});
it("can have a default KubeObjectStore instance retrieved for it", () => {
expect(apiManager.getStore(apiBase)).toBeInstanceOf(KubeObjectStore);
});
describe("given that an extension registers an api with the same apibase", () => {
beforeEach(() => {
void Object.assign(new ExternalKubeApi({
@ -172,6 +177,34 @@ describe("ApiManager", () => {
myField: 2,
});
});
it("can have a default KubeObjectStore instance retrieved for it", () => {
expect(apiManager.getStore(apiBase)).toBeInstanceOf(KubeObjectStore);
});
describe("given that an extension registers a store for the same apibase", () => {
beforeEach(() => {
const api = apiManager.getApi(apiBase);
assert(api);
apiManager.registerStore(Object.assign(
new KubeObjectStore({
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
}, api),
{
someField: 2,
},
));
});
it("can gets the custom KubeObjectStore instance instead", () => {
expect(apiManager.getStore(apiBase)).toMatchObject({
someField: 2,
});
});
});
});
});
});

View File

@ -10,7 +10,8 @@ import { autorun, action, observable } from "mobx";
import type { KubeApi } from "../kube-api";
import type { KubeObject, ObjectReference } from "../kube-object";
import { parseKubeApi, createKubeApiURL } from "../kube-api-parse";
import { iter } from "@k8slens/utilities";
import { getOrInsertWith, iter } from "@k8slens/utilities";
import type { CreateCustomResourceStore } from "./create-custom-resource-store.injectable";
export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any>
? Store
@ -28,12 +29,13 @@ interface Dependencies {
readonly apis: IComputedValue<KubeApi[]>;
readonly crdApis: IComputedValue<KubeApi[]>;
readonly stores: IComputedValue<KubeObjectStore[]>;
createCustomResourceStore: CreateCustomResourceStore;
}
export class ApiManager {
private readonly externalApis = observable.array<KubeApi>();
private readonly externalStores = observable.array<KubeObjectStore>();
private readonly defaultCrdStores = observable.map<string, KubeObjectStore>();
private readonly apis = observable.map<string, KubeApi>();
constructor(private readonly dependencies: Dependencies) {
@ -117,6 +119,16 @@ export class ApiManager {
this.externalStores.push(store);
}
private apiIsDefaultCrdApi(api: KubeApi): boolean {
for (const crdApi of this.dependencies.crdApis.get()) {
if (crdApi.apiBase === api.apiBase) {
return true;
}
}
return false;
}
getStore(api: string | undefined): KubeObjectStore | undefined;
getStore<Api>(api: RegisterableApi<Api>): KubeObjectStoreFrom<Api> | undefined;
/**
@ -137,9 +149,19 @@ export class ApiManager {
return undefined;
}
return iter.chain(this.dependencies.stores.get().values())
const defaultResult = iter.chain(this.dependencies.stores.get().values())
.concat(this.externalStores.values())
.find(store => store.api.apiBase === api.apiBase);
if (defaultResult) {
return defaultResult;
}
if (this.apiIsDefaultCrdApi(api)) {
return getOrInsertWith(this.defaultCrdStores, api.apiBase, () => this.dependencies.createCustomResourceStore(api));
}
return undefined;
}
lookupApiLink(ref: ObjectReference, parentObject?: KubeObject): string {

View File

@ -0,0 +1,27 @@
/**
* 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 clusterFrameContextForNamespacedResourcesInjectable from "../../../renderer/cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../logger.injectable";
import type { KubeApi } from "../kube-api";
import type { KubeObject } from "../kube-object";
import type { KubeObjectStoreDependencies } from "../kube-object.store";
import { CustomResourceStore } from "./resource.store";
export type CreateCustomResourceStore = <K extends KubeObject>(api: KubeApi<K>) => CustomResourceStore<K>;
const createCustomResourceStoreInjectable = getInjectable({
id: "create-custom-resource-store",
instantiate: (di): CreateCustomResourceStore => {
const deps: KubeObjectStoreDependencies = {
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
};
return (api) => new CustomResourceStore(deps, api);
},
});
export default createCustomResourceStoreInjectable;

View File

@ -10,6 +10,7 @@ import { kubeObjectStoreInjectionToken } from "./kube-object-store-token";
import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
import { computed } from "mobx";
import { customResourceDefinitionApiInjectionToken } from "./crd-api-token";
import createCustomResourceStoreInjectable from "./create-custom-resource-store.injectable";
const apiManagerInjectable = getInjectable({
id: "api-manager",
@ -27,6 +28,7 @@ const apiManagerInjectable = getInjectable({
crdApis: storesAndApisCanBeCreated
? computedInjectMany(customResourceDefinitionApiInjectionToken)
: computed(() => []),
createCustomResourceStore: di.inject(createCustomResourceStoreInjectable),
});
},
});

View File

@ -88,7 +88,7 @@ export interface KubeObjectStoreDependencies {
readonly logger: Logger;
}
export abstract class KubeObjectStore<
export class KubeObjectStore<
K extends KubeObject = KubeObject,
A extends KubeApi<K, D> = KubeApi<K, KubeJsonApiDataFor<K>>,
D extends KubeJsonApiDataFor<K> = KubeApiDataFrom<K, A>,

View File

@ -5,59 +5,17 @@
import { getInjectable } from "@ogre-tools/injectable";
import autoRegistrationEmitterInjectable from "../../../common/k8s-api/api-manager/auto-registration-emitter.injectable";
import apiManagerInjectable from "../../../common/k8s-api/api-manager/manager.injectable";
import { CustomResourceStore } from "../../../common/k8s-api/api-manager/resource.store";
import type { CustomResourceDefinition } from "../../../common/k8s-api/endpoints";
import type { KubeApiDependencies } from "../../../common/k8s-api/kube-api";
import { KubeApi } from "../../../common/k8s-api/kube-api";
import { KubeObject } from "../../../common/k8s-api/kube-object";
import type { KubeApi } from "../../../common/k8s-api/kube-api";
import { beforeClusterFrameStartsSecondInjectionToken } from "../tokens";
import type { KubeObjectStoreDependencies } from "../../../common/k8s-api/kube-object.store";
import clusterFrameContextForNamespacedResourcesInjectable from "../../cluster-frame-context/for-namespaced-resources.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import maybeKubeApiInjectable from "../../../common/k8s-api/maybe-kube-api.injectable";
const setupAutoRegistrationInjectable = getInjectable({
id: "setup-auto-registration",
instantiate: (di) => ({
run: () => {
const autoRegistrationEmitter = di.inject(autoRegistrationEmitterInjectable);
const beforeApiManagerInitializationCrds: CustomResourceDefinition[] = [];
const beforeApiManagerInitializationApis: KubeApi[] = [];
const kubeApiDependencies: KubeApiDependencies = {
logger: di.inject(loggerInjectable),
maybeKubeApi: di.inject(maybeKubeApiInjectable),
};
const kubeObjectStoreDependencies: KubeObjectStoreDependencies = {
context: di.inject(clusterFrameContextForNamespacedResourcesInjectable),
logger: di.inject(loggerInjectable),
};
let initialized = false;
const autoInitCustomResourceStore = (crd: CustomResourceDefinition) => {
const objectConstructor = class extends KubeObject {
static readonly kind = crd.getResourceKind();
static readonly namespaced = crd.isNamespaced();
static readonly apiBase = crd.getResourceApiBase();
};
const api = (() => {
const rawApi = apiManager.getApi(objectConstructor.apiBase);
if (rawApi) {
return rawApi;
}
const api = new KubeApi(kubeApiDependencies, { objectConstructor });
apiManager.registerApi(api);
return api;
})();
if (!apiManager.getStore(api)) {
apiManager.registerStore(new CustomResourceStore(kubeObjectStoreDependencies, api));
}
};
const autoInitKubeApi = (api: KubeApi) => {
apiManager.registerApi(api);
};
@ -74,7 +32,6 @@ const setupAutoRegistrationInjectable = getInjectable({
// NOTE: this MUST happen after the event emitter listeners are registered
const apiManager = di.inject(apiManagerInjectable);
beforeApiManagerInitializationCrds.forEach(autoInitCustomResourceStore);
beforeApiManagerInitializationApis.forEach(autoInitKubeApi);
initialized = true;
},