From 579bfa477eb83845606224e602662fae4279942b Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Mon, 24 Jun 2024 18:25:04 +0300 Subject: [PATCH] K8s: improve frontend resource client (#89621) --- public/app/features/apiserver/client.ts | 31 ++++++++++++++----- public/app/features/apiserver/types.ts | 7 +++-- .../features/dashboard/api/dashboard_api.ts | 11 ++++--- public/app/features/playlist/api.ts | 4 ++- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/public/app/features/apiserver/client.ts b/public/app/features/apiserver/client.ts index a98ed0e2431..ee784d4df4d 100644 --- a/public/app/features/apiserver/client.ts +++ b/public/app/features/apiserver/client.ts @@ -1,4 +1,5 @@ import { config, getBackendSrv } from '@grafana/runtime'; +import { contextSrv } from 'app/core/core'; import { ListOptions, @@ -9,6 +10,7 @@ import { ResourceForCreate, ResourceList, ResourceClient, + ObjectMeta, } from './types'; export interface GroupVersionResource { @@ -26,13 +28,6 @@ export class ScopedResourceClient implements ResourceCli this.url = `/apis/${gvr.group}/${gvr.version}/${ns}${gvr.resource}`; } - public async create(obj: ResourceForCreate): Promise { - if (!obj.metadata.name && !obj.metadata.generateName) { - obj.metadata.generateName = 'g'; // Triggers the server to create a unique value - } - return getBackendSrv().post(this.url, obj); - } - public async get(name: string): Promise> { return getBackendSrv().get>(`${this.url}/${name}`); } @@ -49,7 +44,19 @@ export class ScopedResourceClient implements ResourceCli return getBackendSrv().get>(this.url, opts); } + 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 + // THe passed in value is the suggested prefix + obj.metadata.generateName = login ? login.slice(0, 2) : 'g'; + } + setOriginAsUI(obj.metadata); + return getBackendSrv().post(this.url, obj); + } + public async update(obj: Resource): Promise> { + setOriginAsUI(obj.metadata); return getBackendSrv().put>(`${this.url}/${obj.metadata.name}`, obj); } @@ -90,3 +97,13 @@ export class ScopedResourceClient implements ResourceCli .join(','); } } + +// add the origin annotations so we know what was set from the UI +function setOriginAsUI(meta: Partial) { + if (!meta.annotations) { + meta.annotations = {}; + } + meta.annotations.AnnoKeyOriginName = 'UI'; + meta.annotations.AnnoKeyOriginPath = window.location.pathname; + meta.annotations.AnnoKeyOriginHash = config.buildInfo.versionString; +} diff --git a/public/app/features/apiserver/types.ts b/public/app/features/apiserver/types.ts index 467d23fd3c5..97fe0317f30 100644 --- a/public/app/features/apiserver/types.ts +++ b/public/app/features/apiserver/types.ts @@ -33,12 +33,13 @@ export const AnnoKeyCreatedBy = 'grafana.app/createdBy'; export const AnnoKeyUpdatedTimestamp = 'grafana.app/updatedTimestamp'; export const AnnoKeyUpdatedBy = 'grafana.app/updatedBy'; export const AnnoKeyFolder = 'grafana.app/folder'; +export const AnnoKeyMessage = 'grafana.app/message'; export const AnnoKeySlug = 'grafana.app/slug'; // Identify where values came from const AnnoKeyOriginName = 'grafana.app/originName'; const AnnoKeyOriginPath = 'grafana.app/originPath'; -const AnnoKeyOriginKey = 'grafana.app/originKey'; +const AnnoKeyOriginHash = 'grafana.app/originHash'; const AnnoKeyOriginTimestamp = 'grafana.app/originTimestamp'; type GrafanaAnnotations = { @@ -50,7 +51,7 @@ type GrafanaAnnotations = { [AnnoKeyOriginName]?: string; [AnnoKeyOriginPath]?: string; - [AnnoKeyOriginKey]?: string; + [AnnoKeyOriginHash]?: string; [AnnoKeyOriginTimestamp]?: string; // Any key value @@ -142,7 +143,7 @@ export interface MetaStatus { } export interface ResourceClient { - create(obj: ResourceForCreate): Promise; + create(obj: ResourceForCreate): Promise>; get(name: string): Promise>; subresource(name: string, path: string): Promise; list(opts?: ListOptions): Promise>; diff --git a/public/app/features/dashboard/api/dashboard_api.ts b/public/app/features/dashboard/api/dashboard_api.ts index f13a68102a3..6ea255ad5e4 100644 --- a/public/app/features/dashboard/api/dashboard_api.ts +++ b/public/app/features/dashboard/api/dashboard_api.ts @@ -4,7 +4,7 @@ import { ResourceClient } from 'app/features/apiserver/types'; import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types'; import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher'; import { DeleteDashboardResponse } from 'app/features/manage-dashboards/types'; -import { DashboardDTO, DashboardDataDTO } from 'app/types'; +import { DashboardDTO, DashboardDataDTO, SaveDashboardResponseDTO } from 'app/types'; import { getScopesFromUrl } from '../utils/getScopesFromUrl'; @@ -12,7 +12,7 @@ export interface DashboardAPI { /** Get a dashboard with the access control metadata */ getDashboardDTO(uid: string): Promise; /** Save dashboard */ - saveDashboard(options: SaveDashboardCommand): Promise; + saveDashboard(options: SaveDashboardCommand): Promise; /** Delete a dashboard */ deleteDashboard(uid: string, showSuccessAlert: boolean): Promise; } @@ -21,10 +21,10 @@ export interface DashboardAPI { class LegacyDashboardAPI implements DashboardAPI { constructor() {} - saveDashboard(options: SaveDashboardCommand): Promise { + saveDashboard(options: SaveDashboardCommand): Promise { dashboardWatcher.ignoreNextSave(); - return getBackendSrv().post('/api/dashboards/db/', { + return getBackendSrv().post('/api/dashboards/db/', { dashboard: options.dashboard, message: options.message ?? '', overwrite: options.overwrite ?? false, @@ -48,6 +48,7 @@ class LegacyDashboardAPI implements DashboardAPI { // Implemented using /apis/dashboards.grafana.app/* class K8sDashboardAPI implements DashboardAPI { private client: ResourceClient; + constructor(private legacy: DashboardAPI) { this.client = new ScopedResourceClient({ group: 'dashboard.grafana.app', @@ -56,7 +57,7 @@ class K8sDashboardAPI implements DashboardAPI { }); } - saveDashboard(options: SaveDashboardCommand): Promise { + saveDashboard(options: SaveDashboardCommand): Promise { return this.legacy.saveDashboard(options); } diff --git a/public/app/features/playlist/api.ts b/public/app/features/playlist/api.ts index 25033582d50..dd778f8903d 100644 --- a/public/app/features/playlist/api.ts +++ b/public/app/features/playlist/api.ts @@ -71,7 +71,9 @@ class K8sAPI implements PlaylistAPI { async createPlaylist(playlist: Playlist): Promise { const body = this.playlistAsK8sResource(playlist); - await withErrorHandling(() => this.server.create(body)); + await withErrorHandling(async () => { + await this.server.create(body); + }); } async updatePlaylist(playlist: Playlist): Promise {