From 322c7d954864d107927141ac439f1bffdee10afa Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 7 Jan 2025 09:26:00 +0300 Subject: [PATCH] K8s/Typescript: Support generic status for ScopedResourceClient (#98509) --- public/app/features/apiserver/client.ts | 80 +++++++++++----------- public/app/features/apiserver/types.ts | 17 ++--- public/app/features/scopes/internal/api.ts | 2 +- 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/public/app/features/apiserver/client.ts b/public/app/features/apiserver/client.ts index 76f0383665c..8ac5b531095 100644 --- a/public/app/features/apiserver/client.ts +++ b/public/app/features/apiserver/client.ts @@ -21,7 +21,7 @@ export interface GroupVersionResource { resource: string; } -export class ScopedResourceClient implements ResourceClient { +export class ScopedResourceClient implements ResourceClient { readonly url: string; constructor(gvr: GroupVersionResource, namespaced = true) { @@ -30,23 +30,23 @@ export class ScopedResourceClient implements ResourceCli this.url = `/apis/${gvr.group}/${gvr.version}/${ns}${gvr.resource}`; } - public async get(name: string): Promise> { - return getBackendSrv().get>(`${this.url}/${name}`); + public async get(name: string): Promise> { + return getBackendSrv().get>(`${this.url}/${name}`); } public async subresource(name: string, path: string): Promise { return getBackendSrv().get(`${this.url}/${name}/${path}`); } - public async list(opts?: ListOptions | undefined): Promise> { + public async list(opts?: ListOptions | undefined): Promise> { const finalOpts = opts || {}; finalOpts.labelSelector = this.parseListOptionsSelector(finalOpts?.labelSelector); finalOpts.fieldSelector = this.parseListOptionsSelector(finalOpts?.fieldSelector); - return getBackendSrv().get>(this.url, opts); + return getBackendSrv().get>(this.url, opts); } - public async create(obj: ResourceForCreate): Promise> { + public async create(obj: ResourceForCreate): Promise> { if (!obj.metadata.name && !obj.metadata.generateName) { const login = contextSrv.user.login; // GenerateName lets the apiserver create a new uid for the name @@ -57,47 +57,16 @@ export class ScopedResourceClient implements ResourceCli return getBackendSrv().post(this.url, obj); } - public async update(obj: Resource): Promise> { + public async update(obj: Resource): Promise> { setSavedFromUIAnnotation(obj.metadata); - return getBackendSrv().put>(`${this.url}/${obj.metadata.name}`, obj); + return getBackendSrv().put>(`${this.url}/${obj.metadata.name}`, obj); } public async delete(name: string): Promise { return getBackendSrv().delete(`${this.url}/${name}`); } - private parseListOptionsSelector( - selector: ListOptionsLabelSelector | ListOptionsFieldSelector | undefined - ): string | undefined { - if (!Array.isArray(selector)) { - return selector; - } - - return selector - .map((label) => { - const key = String(label.key); - const operator = label.operator; - - switch (operator) { - case '=': - case '!=': - return `${key}${operator}${label.value}`; - - case 'in': - case 'notin': - return `${key} ${operator} (${label.value.join(',')})`; - - case '': - case '!': - return `${operator}${key}`; - - default: - return null; - } - }) - .filter(Boolean) - .join(','); - } + private parseListOptionsSelector = parseListOptionsSelector; } // add the origin annotations so we know what was set from the UI @@ -138,3 +107,34 @@ export class DatasourceAPIVersions { return apiVersions[pluginID]; } } + +export const parseListOptionsSelector = (selector: ListOptionsLabelSelector | ListOptionsFieldSelector | undefined) => { + if (!Array.isArray(selector)) { + return selector; + } + + return selector + .map((label) => { + const key = String(label.key); + const operator = label.operator; + + switch (operator) { + case '=': + case '!=': + return `${key}${operator}${label.value}`; + + case 'in': + case 'notin': + return `${key} ${operator} (${label.value.join(',')})`; + + case '': + case '!': + return `${operator}${key}`; + + default: + return null; + } + }) + .filter(Boolean) + .join(','); +}; diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index a7760187f61..d90393f0bf1 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -84,9 +84,10 @@ type GrafanaClientAnnotations = { [AnnoKeyDashboardGnetId]?: string; }; -export interface Resource extends TypeMeta { +export interface Resource extends TypeMeta { metadata: ObjectMeta; spec: T; + status?: S; } export interface ResourceForCreate extends Partial> { @@ -103,9 +104,9 @@ export interface ListMeta { remainingItemCount?: number; } -export interface ResourceList extends TypeMeta { +export interface ResourceList extends TypeMeta { metadata: ListMeta; - items: Array>; + items: Array>; } export type ListOptionsLabelSelector = @@ -168,12 +169,12 @@ export interface MetaStatus { details?: object; } -export interface ResourceClient { - create(obj: ResourceForCreate): Promise>; - get(name: string): Promise>; +export interface ResourceClient { + create(obj: ResourceForCreate): Promise>; + get(name: string): Promise>; subresource(name: string, path: string): Promise; - list(opts?: ListOptions): Promise>; - update(obj: ResourceForCreate): Promise>; + list(opts?: ListOptions): Promise>; + update(obj: ResourceForCreate): Promise>; delete(name: string): Promise; } diff --git a/public/app/features/scopes/internal/api.ts b/public/app/features/scopes/internal/api.ts index 81eb52d48c9..b1f1ceaf869 100644 --- a/public/app/features/scopes/internal/api.ts +++ b/public/app/features/scopes/internal/api.ts @@ -12,7 +12,7 @@ const namespace = config.namespace ?? 'default'; const nodesEndpoint = `/apis/${group}/${version}/namespaces/${namespace}/find/scope_node_children`; const dashboardsEndpoint = `/apis/${group}/${version}/namespaces/${namespace}/find/scope_dashboard_bindings`; -const scopesClient = new ScopedResourceClient({ +const scopesClient = new ScopedResourceClient({ group, version, resource: 'scopes',