Dashlist: Use new nested folder picker (#74011)

* Add new folderUID property

* Add nested folder picker + migration to UID

* fix folderUID

* comment
This commit is contained in:
Josh Hunt
2023-09-07 10:57:31 +00:00
committed by GitHub
parent ebe13a53f7
commit 44e51ffe8b
10 changed files with 192 additions and 51 deletions

View File

@ -4496,7 +4496,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Unexpected any. Specify a different type.", "5"]
],
"public/app/plugins/panel/dashlist/module.tsx:5381": [
"public/app/plugins/panel/dashlist/migrations.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/debug/CursorView.tsx:5381": [

View File

@ -25,7 +25,7 @@ title: DashboardListPanelCfg kind
### Options
| Property | Type | Required | Default | Description |
|----------------------|----------|----------|---------|-------------|
|----------------------|----------|----------|---------|-----------------------------------------------------------------|
| `includeVars` | boolean | **Yes** | `false` | |
| `keepTime` | boolean | **Yes** | `false` | |
| `maxItems` | integer | **Yes** | `10` | |
@ -35,6 +35,7 @@ title: DashboardListPanelCfg kind
| `showSearch` | boolean | **Yes** | `false` | |
| `showStarred` | boolean | **Yes** | `true` | |
| `tags` | string[] | **Yes** | | |
| `folderId` | integer | No | | |
| `folderId` | integer | No | | folderId is deprecated, and migrated to folderUid on panel init |
| `folderUID` | string | No | | |

View File

@ -12,7 +12,11 @@
export const pluginVersion = "10.2.0-pre";
export interface Options {
/**
* folderId is deprecated, and migrated to folderUid on panel init
*/
folderId?: number;
folderUID?: string;
includeVars: boolean;
keepTime: boolean;
maxItems: number;

View File

@ -41,10 +41,11 @@ async function fetchDashboards(options: Options, replaceVars: InterpolateFunctio
let searchedDashboards: Promise<DashboardSearchItem[]> = Promise.resolve([]);
if (options.showSearch) {
const uid = options.folderUID === '' ? 'general' : options.folderUID;
const params = {
limit: options.maxItems,
query: replaceVars(options.query, {}, 'text'),
folderIds: options.folderId,
folderUIDs: uid,
tag: options.tags.map((tag: string) => replaceVars(tag, {}, 'text')),
type: 'dash-db',
};

View File

@ -0,0 +1,77 @@
import { wellFormedPanelModel } from 'test/fixtures/panelModel.fixture';
import { PanelModel } from '@grafana/data';
import { mockFolderDTO } from 'app/features/browse-dashboards/fixtures/folder.fixture';
import { dashlistMigrationHandler, AngularModel } from './migrations';
const getMock = jest.fn();
jest.mock('@grafana/runtime', () => ({
...jest.requireActual('@grafana/runtime'),
getBackendSrv: () => ({
get: getMock,
}),
}));
describe('dashlist migrations', () => {
it('migrates angular panel model to react model', async () => {
const basePanelModel = wellFormedPanelModel({});
basePanelModel.pluginVersion = '5.1';
const angularPanel: PanelModel<any> & AngularModel = {
...basePanelModel,
// pluginVersion: '5.1',
starred: true,
recent: true,
search: true,
headings: true,
limit: 7,
query: 'hello, query',
};
const newOptions = await dashlistMigrationHandler(angularPanel);
expect(newOptions).toEqual({
showStarred: true,
showRecentlyViewed: true,
showSearch: true,
showHeadings: true,
maxItems: 7,
query: 'hello, query',
includeVars: undefined,
keepTime: undefined,
});
expect(angularPanel).toStrictEqual(basePanelModel);
});
it('migrates folder id to folder UID', async () => {
const folderDTO = mockFolderDTO(1, {
id: 77,
uid: 'abc-124',
});
getMock.mockResolvedValue(folderDTO);
const basePanelOptions = {
showStarred: true,
showRecentlyViewed: true,
showSearch: true,
showHeadings: true,
maxItems: 7,
query: 'hello, query',
includeVars: false,
keepTime: false,
tags: [],
};
const panelModel = wellFormedPanelModel({
...basePanelOptions,
folderId: 77,
});
const newOptions = await dashlistMigrationHandler(panelModel);
expect(newOptions).toStrictEqual({
...basePanelOptions,
folderUID: 'abc-124',
});
});
});

View File

@ -0,0 +1,60 @@
import { PanelModel } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { FolderDTO } from 'app/types';
import { Options } from './panelcfg.gen';
function getFolderByID(folderID: number) {
return getBackendSrv().get<FolderDTO>(`/api/folders/id/${folderID}`);
}
export interface AngularModel {
/** @deprecated */
starred?: boolean;
/** @deprecated */
recent?: boolean;
/** @deprecated */
search?: boolean;
/** @deprecated */
headings?: boolean;
/** @deprecated */
limit?: number;
/** @deprecated */
query?: string;
/** @deprecated */
folderId?: number;
/** @deprecated */
tags?: string[];
}
export async function dashlistMigrationHandler(panel: PanelModel<Options> & AngularModel) {
// Convert old angular model to new react model
const newOptions: Options = {
...panel.options,
showStarred: panel.options.showStarred ?? panel.starred,
showRecentlyViewed: panel.options.showRecentlyViewed ?? panel.recent,
showSearch: panel.options.showSearch ?? panel.search,
showHeadings: panel.options.showHeadings ?? panel.headings,
maxItems: panel.options.maxItems ?? panel.limit,
query: panel.options.query ?? panel.query,
folderId: panel.options.folderId ?? panel.folderId,
tags: panel.options.tags ?? panel.tags,
};
// Delete old angular properties
const previousVersion = parseFloat(panel.pluginVersion || '6.1');
if (previousVersion < 6.3) {
const oldProps = ['starred', 'recent', 'search', 'headings', 'limit', 'query', 'folderId'] as const;
oldProps.forEach((prop) => delete panel[prop]);
}
// Convert the folderId to folderUID. Uses the API to do the conversion.
if (newOptions.folderId !== undefined) {
const folderId = newOptions.folderId;
const folderResp = await getFolderByID(folderId);
newOptions.folderUID = folderResp.uid;
delete newOptions.folderId;
}
return newOptions;
}

View File

@ -1,15 +1,11 @@
import React from 'react';
import { PanelModel, PanelPlugin } from '@grafana/data';
import { PanelPlugin } from '@grafana/data';
import { TagsInput } from '@grafana/ui';
import {
ALL_FOLDER,
GENERAL_FOLDER,
ReadonlyFolderPicker,
} from '../../../core/components/Select/ReadonlyFolderPicker/ReadonlyFolderPicker';
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
import { DashList } from './DashList';
import { dashlistMigrationHandler } from './migrations';
import { defaultOptions, Options } from './panelcfg.gen';
export const plugin = new PanelPlugin<Options>(DashList)
@ -56,18 +52,12 @@ export const plugin = new PanelPlugin<Options>(DashList)
defaultValue: defaultOptions.query,
})
.addCustomEditor({
path: 'folderId',
path: 'folderUid',
name: 'Folder',
id: 'folderId',
id: 'folderUid',
defaultValue: undefined,
editor: function RenderFolderPicker({ value, onChange }) {
return (
<ReadonlyFolderPicker
initialFolderId={value}
onChange={(folder) => onChange(folder?.id)}
extraFolders={[ALL_FOLDER, GENERAL_FOLDER]}
/>
);
return <FolderPicker value={value} onChange={(folderUID) => onChange(folderUID)} />;
},
})
.addCustomEditor({
@ -81,23 +71,4 @@ export const plugin = new PanelPlugin<Options>(DashList)
},
});
})
.setMigrationHandler((panel: PanelModel<Options> & Record<string, any>) => {
const newOptions = {
showStarred: panel.options.showStarred ?? panel.starred,
showRecentlyViewed: panel.options.showRecentlyViewed ?? panel.recent,
showSearch: panel.options.showSearch ?? panel.search,
showHeadings: panel.options.showHeadings ?? panel.headings,
maxItems: panel.options.maxItems ?? panel.limit,
query: panel.options.query ?? panel.query,
folderId: panel.options.folderId ?? panel.folderId,
tags: panel.options.tags ?? panel.tags,
};
const previousVersion = parseFloat(panel.pluginVersion || '6.1');
if (previousVersion < 6.3) {
const oldProps = ['starred', 'recent', 'search', 'headings', 'limit', 'query', 'folderId'];
oldProps.forEach((prop) => delete panel[prop]);
}
return newOptions;
});
.setMigrationHandler(dashlistMigrationHandler);

View File

@ -30,8 +30,10 @@ composableKinds: PanelCfg: {
showHeadings: bool | *true
maxItems: int | *10
query: string | *""
folderId?: int
tags: [...string] | *[]
// folderId is deprecated, and migrated to folderUid on panel init
folderId?: int
folderUID?: string
} @cuetsy(kind="interface")
}
}]

View File

@ -9,7 +9,11 @@
// Run 'make gen-cue' from repository root to regenerate.
export interface Options {
/**
* folderId is deprecated, and migrated to folderUid on panel init
*/
folderId?: number;
folderUID?: string;
includeVars: boolean;
keepTime: boolean;
maxItems: number;

View File

@ -0,0 +1,21 @@
import { Chance } from 'chance';
import { PanelModel } from '@grafana/data';
export function wellFormedPanelModel<T extends object>(panelOptions: T, seed = 1): PanelModel<T> {
const random = Chance(seed);
return {
id: random.integer(),
type: random.word(),
title: random.sentence({ words: 3 }),
description: random.sentence({ words: 10 }),
options: panelOptions,
fieldConfig: {
defaults: {},
overrides: [],
},
pluginVersion: '9.5.0',
targets: [],
};
}