Compare commits

...

7 Commits

Author SHA1 Message Date
Ryan McKinley
8506a89579 Merge remote-tracking branch 'origin/main' into add-history-to-dashboard-api 2025-12-02 17:22:50 +03:00
Ryan McKinley
f34890327f private 2025-12-02 16:34:57 +03:00
Ryan McKinley
cf696db273 duplicate the merge code 2025-12-02 16:33:55 +03:00
Ryan McKinley
8b4a040dc8 Merge remote-tracking branch 'origin/main' into add-history-to-dashboard-api 2025-12-02 16:29:14 +03:00
Ryan McKinley
18c587a460 wrapper 2025-12-02 15:00:14 +03:00
Ryan McKinley
36a7c10f67 add history 2025-12-02 13:00:37 +03:00
Ryan McKinley
de1405b1e3 add history 2025-12-02 12:55:16 +03:00
7 changed files with 79 additions and 0 deletions

View File

@@ -19,6 +19,7 @@ export interface RevisionsModel {
data: Dashboard;
}
// TODO: this should be removed entirely
export class HistorySrv {
getHistoryList(dashboardUID: string, options: HistoryListOpts) {
if (typeof dashboardUID !== 'string') {
@@ -36,6 +37,8 @@ export class HistorySrv {
return getBackendSrv().get(`api/dashboards/uid/${dashboardUID}/versions/${version}`);
}
// restore should not be needed -- each result has the full dashboard data,
// so just update the dashboard with the data from the version you want to restore
restoreDashboard(dashboardUID: string, version: number) {
if (typeof dashboardUID !== 'string') {
return Promise.resolve({});

View File

@@ -56,6 +56,24 @@ export class UnifiedDashboardAPI
return await this.v1Client.deleteDashboard(uid, showSuccessAlert);
}
async listDashboardHistory(uid: string) {
const v1Response = await this.v1Client.listDashboardHistory(uid);
const filteredV1Items = v1Response.items.filter((item) => !failedFromVersion(item, ['v2']));
if (filteredV1Items.length === v1Response.items.length) {
return v1Response;
}
const v2Response = await this.v2Client.listDashboardHistory(uid);
const filteredV2Items = v2Response.items.filter((item) => !failedFromVersion(item, ['v0', 'v1']));
return {
...v2Response,
// Make sure we display only valid resources
items: [...filteredV1Items, ...filteredV2Items].filter(isResource),
};
}
/**
* List deleted dashboards handling mixed v1/v2 versions or pure v2 dashboards.
*

View File

@@ -12,6 +12,22 @@ import { SaveDashboardCommand } from '../components/SaveDashboard/types';
import { DashboardAPI, ListDeletedDashboardsOptions } from './types';
interface HistoryResult {
continueToken?: string;
versions: RevisionsModel[];
}
interface RevisionsModel {
id: number;
checked: boolean;
uid: string;
parentVersion: number;
version: number;
created: Date;
createdBy: string;
message: string;
data: Dashboard;
}
export class LegacyDashboardAPI implements DashboardAPI<DashboardDTO, Dashboard> {
constructor() {}
@@ -53,6 +69,30 @@ export class LegacyDashboardAPI implements DashboardAPI<DashboardDTO, Dashboard>
return result;
}
async listDashboardHistory(uid: string): Promise<ResourceList<Dashboard, Dashboard, string>> {
const result = await getBackendSrv().get<HistoryResult>(`/api/dashboards/uid/${uid}/versions`);
return {
apiVersion: 'v0alpha1',
kind: 'DashboardList',
metadata: { resourceVersion: '0' },
items: result.versions.map((v) => ({
apiVersion: 'v0alpha1',
kind: 'Dashboard',
metadata: {
name: v.uid,
resourceVersion: v.version.toString(),
generation: v.version,
creationTimestamp: v.created ? v.created.toISOString() : new Date().toISOString(),
annotations: {
'grafana.app/updatedBy': v.createdBy,
'grafana.app/message': v.message,
},
},
spec: v.data,
})),
};
}
/**
* No-op for legacy API
*/

View File

@@ -15,6 +15,8 @@ export interface DashboardAPI<G, T> {
saveDashboard(options: SaveDashboardCommand<T>): Promise<SaveDashboardResponseDTO>;
/** Delete a dashboard */
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse>;
/** List all versions of a dashboard */
listDashboardHistory(uid: string): Promise<ResourceList<T>>;
/** List all deleted dashboards */
listDeletedDashboards(options: ListDeletedDashboardsOptions): Promise<ResourceList<T>>;
/** Restore a deleted dashboard by re-creating it */

View File

@@ -206,6 +206,13 @@ export class K8sDashboardAPI implements DashboardAPI<DashboardDTO, Dashboard> {
}
}
async listDashboardHistory(uid: string) {
return await this.client.list({
labelSelector: 'grafana.app/get-history=true',
fieldSelector: `metadata.name=${uid}`,
});
}
async listDeletedDashboards(options: ListDeletedDashboardsOptions) {
return await this.client.list({ ...options, labelSelector: 'grafana.app/get-trash=true' });
}

View File

@@ -181,6 +181,13 @@ export class K8sDashboardV2API
};
}
async listDashboardHistory(uid: string) {
return await this.client.list({
labelSelector: 'grafana.app/get-history=true',
fieldSelector: `metadata.name=${uid}`,
});
}
listDeletedDashboards(options: ListDeletedDashboardsOptions) {
return this.client.list({ ...options, labelSelector: 'grafana.app/get-trash=true' });
}

View File

@@ -49,6 +49,7 @@ describe('validateUid', () => {
saveDashboard: jest.fn(),
listDeletedDashboards: jest.fn(),
restoreDashboard: jest.fn(),
listDashboardHistory: jest.fn(),
},
v2: {
getDashboardDTO: jest.fn().mockResolvedValue(v2Dashboard),
@@ -56,6 +57,7 @@ describe('validateUid', () => {
saveDashboard: jest.fn(),
listDeletedDashboards: jest.fn(),
restoreDashboard: jest.fn(),
listDashboardHistory: jest.fn(),
},
});
});