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

Make clear the seperation of extenal and internal stores and apis

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-01-03 10:32:14 -05:00
parent 9da08f4ac4
commit ae21c88e8c
3 changed files with 79 additions and 81 deletions

View File

@ -5,11 +5,12 @@
import type { KubeObjectStore } from "../kube-object.store"; import type { KubeObjectStore } from "../kube-object.store";
import { action, observable, makeObservable } from "mobx"; import type { IComputedValue } from "mobx";
import { autoBind, isDefined, iter } from "../../utils"; import { action, observable } from "mobx";
import type { KubeApi } from "../kube-api"; 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 { parseKubeApi, createKubeApiURL } from "../kube-api-parse";
import { chain } from "../../utils/iter";
export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any> export type RegisterableStore<Store> = Store extends KubeObjectStore<any, any, any>
? Store ? Store
@ -21,25 +22,35 @@ export type KubeObjectStoreFrom<Api> = Api extends KubeApi<infer KubeObj, infer
? KubeObjectStore<KubeObj, Api, ApiData> ? KubeObjectStore<KubeObj, Api, ApiData>
: never; : never;
export type FindApiCallback = (api: KubeApi<KubeObject>) => boolean;
interface Dependencies {
readonly apis: IComputedValue<KubeApi[]>;
readonly stores: IComputedValue<KubeObjectStore[]>;
}
export class ApiManager { export class ApiManager {
private readonly apis = observable.map<string, KubeApi>(); private readonly externalApis = observable.array<KubeApi>();
private readonly stores = observable.map<string, KubeObjectStore>(); private readonly externalStores = observable.array<KubeObjectStore>();
constructor() { constructor(private readonly dependencies: Dependencies) {}
makeObservable(this);
autoBind(this);
}
getApi(pathOrCallback: string | ((api: KubeApi<KubeObject>) => boolean)) { getApi(pathOrCallback: string | FindApiCallback) {
if (typeof pathOrCallback === "string") { const callback: FindApiCallback = typeof pathOrCallback === "function"
return this.apis.get(pathOrCallback) || this.apis.get(parseKubeApi(pathOrCallback).apiBase); ? 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) { 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>(api: RegisterableApi<Api>): void; registerApi<Api>(api: RegisterableApi<Api>): void;
@ -47,45 +58,23 @@ export class ApiManager {
* @deprecated Just register the `api` by itself * @deprecated Just register the `api` by itself
*/ */
registerApi<Api>(apiBase: string, api: RegisterableApi<Api>): void; registerApi<Api>(apiBase: string, api: RegisterableApi<Api>): void;
registerApi<Api>(apiBaseRaw: string | RegisterableApi<Api>, apiRaw?: RegisterableApi<Api>) { registerApi<Api>(...args: [RegisterableApi<Api>] | [string, RegisterableApi<Api>]) {
const api = typeof apiBaseRaw === "string" if (args.length === 1) {
? apiRaw this.externalApis.push(args[0]);
: apiBaseRaw; } else {
this.externalApis.push(args[1]);
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);
} }
} }
protected resolveApi(api: undefined | string | KubeApi): KubeApi | undefined { unregisterApi(apiOrBase: string | KubeApi<KubeObject>) {
if (!api) { if (typeof apiOrBase === "string") {
return undefined; const api = this.externalApis.find(api => api.apiBase === apiOrBase);
}
if (typeof api === "string") { if (api) {
return this.getApi(api); this.externalApis.remove(api);
} }
} else {
return api; this.unregisterApi(apiOrBase.apiBase);
}
unregisterApi(api: string | KubeApi<KubeObject>) {
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]);
} }
} }
@ -93,15 +82,11 @@ export class ApiManager {
/** /**
* @deprecated KubeObjectStore's should only every be about a single KubeApi type * @deprecated KubeObjectStore's should only every be about a single KubeApi type
*/ */
registerStore<KubeObj extends KubeObject>(store: KubeObjectStore<KubeObj, KubeApi<KubeObj>, KubeJsonApiDataFor<KubeObj>>, apis: KubeApi<KubeObj>[]): void; registerStore<KubeObj>(store: RegisterableStore<KubeObj>, apis: KubeApi<KubeObject>[]): void;
@action @action
registerStore<KubeObj extends KubeObject>(store: KubeObjectStore<KubeObj, KubeApi<KubeObj>, KubeJsonApiDataFor<KubeObj>>, apis: KubeApi<KubeObj>[] = [store.api]): void { registerStore<KubeObj>(store: RegisterableStore<KubeObj>): void {
for (const api of apis.filter(isDefined)) { this.externalStores.push(store);
if (api.apiBase) {
this.stores.set(api.apiBase, store as never);
}
}
} }
getStore(api: string | undefined): KubeObjectStore | undefined; 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 * @deprecated use an actual cast instead of hiding it with this unused type param
*/ */
getStore<Store extends KubeObjectStore>(api: string | KubeApi): Store | undefined ; getStore<Store extends KubeObjectStore>(api: string | KubeApi): Store | undefined ;
getStore(api: string | KubeApi | undefined): KubeObjectStore | undefined { getStore(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined {
const { apiBase } = this.resolveApi(api) ?? {}; if (!apiOrBase) {
return undefined;
if (apiBase) {
return this.stores.get(apiBase);
} }
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 { lookupApiLink(ref: ObjectReference, parentObject?: KubeObject): string {
@ -132,7 +121,7 @@ export class ApiManager {
const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion); const api = this.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion);
if (api) { if (api) {
return api.getUrl({ namespace, name }); return api.formatUrlForNotListing({ namespace, name });
} }
// lookup api by generated resource link // lookup api by generated resource link
@ -151,7 +140,7 @@ export class ApiManager {
const apiByKind = this.getApi(api => api.kind === kind); const apiByKind = this.getApi(api => api.kind === kind);
if (apiByKind) { if (apiByKind) {
return apiByKind.getUrl({ name, namespace }); return apiByKind.formatUrlForNotListing({ name, namespace });
} }
// otherwise generate link with default prefix // otherwise generate link with default prefix

View File

@ -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<KubeObjectStore<any, any, any>>({
id: "kube-object-store-token",
});

View File

@ -2,29 +2,28 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * 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 { storesAndApisCanBeCreatedInjectionToken } from "../stores-apis-can-be-created.token";
import type { KubeObjectStore } from "../kube-object.store";
import { ApiManager } from "./api-manager"; import { ApiManager } from "./api-manager";
import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx";
export const kubeObjectStoreInjectionToken = getInjectionToken<KubeObjectStore<any, any, any>>({ import { kubeObjectStoreInjectionToken } from "./kube-object-store-token";
id: "kube-object-store-token", import { kubeApiInjectionToken } from "../kube-api/kube-api-injection-token";
}); import { computed } from "mobx";
const apiManagerInjectable = getInjectable({ const apiManagerInjectable = getInjectable({
id: "api-manager", id: "api-manager",
instantiate: (di) => { instantiate: (di) => {
const apiManager = new ApiManager(); const computedInjectMany = di.inject(computedInjectManyInjectable);
const storesAndApisCanBeCreated = di.inject(storesAndApisCanBeCreatedInjectionToken);
if (di.inject(storesAndApisCanBeCreatedInjectionToken)) { return new ApiManager({
const stores = di.injectMany(kubeObjectStoreInjectionToken); apis: storesAndApisCanBeCreated
? computedInjectMany(kubeApiInjectionToken)
for (const store of stores) { : computed(() => []),
apiManager.registerStore(store); stores: storesAndApisCanBeCreated
} ? computedInjectMany(kubeObjectStoreInjectionToken)
} : computed(() => []),
});
return apiManager;
}, },
}); });