K8s/Dashboards: Add frontend api to switch between implementations (#88632)

pull/88803/head
Ryan McKinley 2 years ago committed by GitHub
parent f28905f8c4
commit 41e0430f83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md
  2. 1
      packages/grafana-data/src/types/featureToggles.gen.ts
  3. 6
      pkg/registry/apis/dashboard/access/sql_dashboards.go
  4. 7
      pkg/services/featuremgmt/registry.go
  5. 1
      pkg/services/featuremgmt/toggles_gen.csv
  6. 4
      pkg/services/featuremgmt/toggles_gen.go
  7. 13
      pkg/services/featuremgmt/toggles_gen.json
  8. 3
      public/app/core/components/Select/DashboardPicker.tsx
  9. 29
      public/app/core/components/SharedPreferences/SharedPreferences.test.tsx
  10. 4
      public/app/core/services/__mocks__/backend_srv.ts
  11. 11
      public/app/core/services/backend_srv.ts
  12. 5
      public/app/core/utils/explore.test.ts
  13. 4
      public/app/features/apiserver/client.ts
  14. 1
      public/app/features/apiserver/types.ts
  15. 3
      public/app/features/browse-dashboards/api/browseDashboardsAPI.ts
  16. 84
      public/app/features/dashboard/api/dashboard_api.ts
  17. 6
      public/app/features/dashboard/containers/DashboardPageProxy.test.tsx
  18. 5
      public/app/features/dashboard/services/DashboardLoaderSrv.ts
  19. 4
      public/app/features/dashboard/services/DashboardSrv.ts
  20. 3
      public/app/features/dashboard/state/actions.ts
  21. 5
      public/app/features/explore/Logs/LiveLogs.test.tsx
  22. 16
      public/app/features/explore/extensions/AddToDashboard/addToDashboard.test.ts
  23. 4
      public/app/features/explore/extensions/AddToDashboard/addToDashboard.ts
  24. 5
      public/app/features/explore/hooks/useStateSync/migrators/v0.test.ts
  25. 5
      public/app/features/explore/hooks/useStateSync/migrators/v1.test.ts
  26. 18
      public/app/features/manage-dashboards/state/actions.ts
  27. 6
      public/app/features/org/OrgDetailsPage.test.tsx
  28. 6
      public/app/features/profile/UserProfileEditPage.test.tsx

@ -153,6 +153,7 @@ Experimental features might be changed or removed without prior notice.
| `idForwarding` | Generate signed id token for identity that can be forwarded to plugins and external services |
| `enableNativeHTTPHistogram` | Enables native HTTP Histograms |
| `kubernetesSnapshots` | Routes snapshot requests from /api to the /apis endpoint |
| `kubernetesDashboards` | Use the kubernetes API in the frontend for dashboards |
| `datasourceQueryTypes` | Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus) |
| `queryService` | Register /apis/query.grafana.app/ -- will eventually replace /api/ds/query |
| `queryServiceRewrite` | Rewrite requests targeting /ds/query to the query service |

@ -117,6 +117,7 @@ export interface FeatureToggles {
transformationsVariableSupport?: boolean;
kubernetesPlaylists?: boolean;
kubernetesSnapshots?: boolean;
kubernetesDashboards?: boolean;
datasourceQueryTypes?: boolean;
queryService?: boolean;
queryServiceRewrite?: boolean;

@ -9,6 +9,7 @@ import (
"time"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
dashboardsV0 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
"github.com/grafana/grafana/pkg/components/simplejson"
@ -177,8 +178,9 @@ func (a *dashboardSqlAccess) GetDashboards(ctx context.Context, query *Dashboard
func (a *dashboardSqlAccess) GetDashboard(ctx context.Context, orgId int64, uid string) (*dashboardsV0.Dashboard, error) {
r, err := a.GetDashboards(ctx, &DashboardQuery{
OrgID: orgId,
UID: uid,
OrgID: orgId,
UID: uid,
Labels: labels.Everything(),
})
if err != nil {
return nil, err

@ -740,6 +740,13 @@ var (
Owner: grafanaAppPlatformSquad,
RequiresRestart: true, // changes the API routing
},
{
Name: "kubernetesDashboards",
Description: "Use the kubernetes API in the frontend for dashboards",
Stage: FeatureStageExperimental,
Owner: grafanaAppPlatformSquad,
FrontendOnly: true,
},
{
Name: "datasourceQueryTypes",
Description: "Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus)",

@ -98,6 +98,7 @@ formatString,preview,@grafana/dataviz-squad,false,false,true
transformationsVariableSupport,preview,@grafana/dataviz-squad,false,false,true
kubernetesPlaylists,GA,@grafana/grafana-app-platform-squad,false,true,false
kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,false
kubernetesDashboards,experimental,@grafana/grafana-app-platform-squad,false,false,true
datasourceQueryTypes,experimental,@grafana/grafana-app-platform-squad,false,true,false
queryService,experimental,@grafana/grafana-app-platform-squad,false,true,false
queryServiceRewrite,experimental,@grafana/grafana-app-platform-squad,false,true,false

1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
98 transformationsVariableSupport preview @grafana/dataviz-squad false false true
99 kubernetesPlaylists GA @grafana/grafana-app-platform-squad false true false
100 kubernetesSnapshots experimental @grafana/grafana-app-platform-squad false true false
101 kubernetesDashboards experimental @grafana/grafana-app-platform-squad false false true
102 datasourceQueryTypes experimental @grafana/grafana-app-platform-squad false true false
103 queryService experimental @grafana/grafana-app-platform-squad false true false
104 queryServiceRewrite experimental @grafana/grafana-app-platform-squad false true false

@ -403,6 +403,10 @@ const (
// Routes snapshot requests from /api to the /apis endpoint
FlagKubernetesSnapshots = "kubernetesSnapshots"
// FlagKubernetesDashboards
// Use the kubernetes API in the frontend for dashboards
FlagKubernetesDashboards = "kubernetesDashboards"
// FlagDatasourceQueryTypes
// Show query type endpoints in datasource API servers (currently hardcoded for testdata, expressions, and prometheus)
FlagDatasourceQueryTypes = "datasourceQueryTypes"

@ -2224,6 +2224,19 @@
"stage": "GA",
"codeowner": "@grafana/plugins-platform-backend"
}
},
{
"metadata": {
"name": "kubernetesDashboards",
"resourceVersion": "1717593661635",
"creationTimestamp": "2024-06-05T13:21:01Z"
},
"spec": {
"description": "Use the kubernetes API in the frontend for dashboards",
"stage": "experimental",
"codeowner": "@grafana/grafana-app-platform-squad",
"frontend": true
}
}
]
}

@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import { SelectableValue } from '@grafana/data';
import { AsyncSelectProps, AsyncSelect } from '@grafana/ui';
import { backendSrv } from 'app/core/services/backend_srv';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { DashboardSearchItem } from 'app/features/search/types';
import { DashboardDTO } from 'app/types';
@ -54,7 +55,7 @@ export const DashboardPicker = ({
(async () => {
// value was manually changed from outside or we are rendering for the first time.
// We need to fetch dashboard information.
const res = await backendSrv.getDashboardByUid(value);
const res = await getDashboardAPI().getDashboardDTO(value);
if (res.dashboard) {
setCurrent({
value: {

@ -7,21 +7,26 @@ import { Preferences as UserPreferencesDTO } from '@grafana/schema/src/raw/prefe
import SharedPreferences from './SharedPreferences';
jest.mock('app/features/dashboard/api/dashboard_api', () => ({
getDashboardAPI: () => ({
getDashboardDTO: jest.fn().mockResolvedValue({
dashboard: {
id: 2,
title: 'My Dashboard',
uid: 'myDash',
templating: {
list: [],
},
panels: [],
},
meta: {},
}),
}),
}));
jest.mock('app/core/services/backend_srv', () => {
return {
backendSrv: {
getDashboardByUid: jest.fn().mockResolvedValue({
dashboard: {
id: 2,
title: 'My Dashboard',
uid: 'myDash',
templating: {
list: [],
},
panels: [],
},
meta: {},
}),
search: jest.fn().mockResolvedValue([
{
id: 2,

@ -28,13 +28,15 @@ function makePromResponse() {
export const backendSrv = {
get: jest.fn(),
getDashboardByUid: jest.fn(),
getFolderByUid: jest.fn(),
post: jest.fn(),
resolveCancelerIfExists: jest.fn(),
search: jest.fn(),
datasourceRequest: jest.fn(() => Promise.resolve(makePromResponse())),
/** @deprecated Use getDashboardAPI().getDashboardDTO(uid) */
getDashboardByUid: jest.fn(),
// Observable support
fetch: (options: BackendSrvRequest) => {
return of(makePromResponse() as FetchResponse);

@ -15,12 +15,13 @@ import {
} from 'rxjs/operators';
import { v4 as uuidv4 } from 'uuid';
import { AppEvents, DataQueryErrorType } from '@grafana/data';
import { AppEvents, DataQueryErrorType, deprecationWarning } from '@grafana/data';
import { BackendSrv as BackendService, BackendSrvRequest, config, FetchError, FetchResponse } from '@grafana/runtime';
import appEvents from 'app/core/app_events';
import { getConfig } from 'app/core/config';
import { getSessionExpiry, hasSessionExpiry } from 'app/core/utils/auth';
import { loadUrlToken } from 'app/core/utils/urlToken';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { DashboardModel } from 'app/features/dashboard/state';
import { DashboardSearchItem } from 'app/features/search/types';
import { TokenRevokedModal } from 'app/features/users/TokenRevokedModal';
@ -512,8 +513,12 @@ export class BackendSrv implements BackendService {
return this.get('/api/search', query);
}
/** @deprecated */
getDashboardByUid(uid: string): Promise<DashboardDTO> {
return this.get<DashboardDTO>(`/api/dashboards/uid/${uid}`);
// NOTE: When this is removed, we can also remove most instances of:
// jest.mock('app/features/live/dashboard/dashboardWatcher
deprecationWarning('backend_srv', 'getDashboardByUid(uid)', 'getDashboardAPI().getDashboardDTO(uid)');
return getDashboardAPI().getDashboardDTO(uid);
}
validateDashboard(dashboard: DashboardModel): Promise<ValidateDashboardResponse> {
@ -522,7 +527,7 @@ export class BackendSrv implements BackendService {
// config.featureToggles.showDashboardValidationWarnings
return Promise.resolve({
isValid: false,
message: 'dashboard validation is supported',
message: 'dashboard validation is not supported',
});
}

@ -62,6 +62,11 @@ jest.mock('@grafana/runtime', () => ({
getDataSourceSrv: () => getDataSourceSrvMock(),
}));
// Avoids errors caused by circular dependencies
jest.mock('app/features/live/dashboard/dashboardWatcher', () => ({
ignoreNextSave: jest.fn(),
}));
describe('state functions', () => {
describe('serializeStateToUrlParam', () => {
it('returns url parameter value for a state object', () => {

@ -37,6 +37,10 @@ export class ScopedResourceClient<T = object, K = string> implements ResourceCli
return getBackendSrv().get<Resource<T, K>>(`${this.url}/${name}`);
}
public async subresource<S>(name: string, path: string): Promise<S> {
return getBackendSrv().get<S>(`${this.url}/${name}/${path}`);
}
public async list(opts?: ListOptions | undefined): Promise<ResourceList<T, K>> {
const finalOpts = opts || {};
finalOpts.labelSelector = this.parseListOptionsSelector(finalOpts?.labelSelector);

@ -144,6 +144,7 @@ export interface MetaStatus {
export interface ResourceClient<T = object, K = string> {
create(obj: ResourceForCreate<T, K>): Promise<void>;
get(name: string): Promise<Resource<T, K>>;
subresource<S>(name: string, path: string): Promise<S>;
list(opts?: ListOptions): Promise<ResourceList<T, K>>;
update(obj: ResourceForCreate<T, K>): Promise<Resource<T, K>>;
delete(name: string): Promise<MetaStatus>;

@ -7,6 +7,7 @@ import { Dashboard } from '@grafana/schema';
import { notifyApp } from 'app/core/actions';
import { createSuccessNotification } from 'app/core/copy/appNotification';
import { contextSrv } from 'app/core/core';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import {
@ -244,7 +245,7 @@ export const browseDashboardsAPI = createApi({
// Move all the dashboards sequentially
// TODO error handling here
for (const dashboardUID of selectedDashboards) {
const fullDash: DashboardDTO = await getBackendSrv().get(`/api/dashboards/uid/${dashboardUID}`);
const fullDash: DashboardDTO = await getDashboardAPI().getDashboardDTO(dashboardUID);
const options = {
dashboard: fullDash.dashboard,

@ -0,0 +1,84 @@
import { config, getBackendSrv } from '@grafana/runtime';
import { ScopedResourceClient } from 'app/features/apiserver/client';
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';
export interface DashboardAPI {
/** Get a dashboard with the access control metadata */
getDashboardDTO(uid: string): Promise<DashboardDTO>;
/** Save dashboard */
saveDashboard(options: SaveDashboardCommand): Promise<unknown>;
/** Delete a dashboard */
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse>;
}
// Implemented using /api/dashboards/*
class LegacyDashboardAPI implements DashboardAPI {
constructor() {}
saveDashboard(options: SaveDashboardCommand): Promise<unknown> {
dashboardWatcher.ignoreNextSave();
return getBackendSrv().post('/api/dashboards/db/', {
dashboard: options.dashboard,
message: options.message ?? '',
overwrite: options.overwrite ?? false,
folderUid: options.folderUid,
});
}
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse> {
return getBackendSrv().delete<DeleteDashboardResponse>(`/api/dashboards/uid/${uid}`, { showSuccessAlert });
}
getDashboardDTO(uid: string): Promise<DashboardDTO> {
return getBackendSrv().get<DashboardDTO>(`/api/dashboards/uid/${uid}`);
}
}
// Implemented using /apis/dashboards.grafana.app/*
class K8sDashboardAPI implements DashboardAPI {
private client: ResourceClient<DashboardDataDTO>;
constructor(private legacy: DashboardAPI) {
this.client = new ScopedResourceClient<DashboardDataDTO>({
group: 'dashboard.grafana.app',
version: 'v0alpha1',
resource: 'dashboards',
});
}
saveDashboard(options: SaveDashboardCommand): Promise<unknown> {
return this.legacy.saveDashboard(options);
}
deleteDashboard(uid: string, showSuccessAlert: boolean): Promise<DeleteDashboardResponse> {
return this.legacy.deleteDashboard(uid, showSuccessAlert);
}
async getDashboardDTO(uid: string): Promise<DashboardDTO> {
const d = await this.client.get(uid);
const m = await this.client.subresource<object>(uid, 'access');
return {
meta: {
...m,
isNew: false,
isFolder: false,
uid: d.metadata.name,
},
dashboard: d.spec,
};
}
}
let instance: DashboardAPI | undefined = undefined;
export function getDashboardAPI() {
if (!instance) {
const legacy = new LegacyDashboardAPI();
instance = config.featureToggles.kubernetesDashboards ? new K8sDashboardAPI(legacy) : legacy;
}
return instance;
}

@ -78,6 +78,12 @@ jest.mock('react-virtualized-auto-sizer', () => {
});
});
jest.mock('app/features/dashboard/api/dashboard_api', () => ({
getDashboardAPI: () => ({
getDashboardDTO: jest.fn().mockResolvedValue(dashMock),
}),
}));
function setup(props: Partial<DashboardPageProxyProps>) {
const context = getGrafanaContextMock();
const store = configureStore({});

@ -12,6 +12,7 @@ import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
import { DashboardDTO } from 'app/types';
import { appEvents } from '../../../core/core';
import { getDashboardAPI } from '../api/dashboard_api';
import { getDashboardSrv } from './DashboardSrv';
import { getDashboardSnapshotSrv } from './SnapshotSrv';
@ -81,8 +82,8 @@ export class DashboardLoaderSrv {
return Promise.resolve(cachedDashboard);
}
promise = backendSrv
.getDashboardByUid(uid)
promise = getDashboardAPI()
.getDashboardDTO(uid)
.then((result) => {
if (result.meta.isFolder) {
appEvents.emit(AppEvents.alertError, ['Dashboard not found']);

@ -6,7 +6,7 @@ import { Dashboard } from '@grafana/schema';
import { appEvents } from 'app/core/app_events';
import { t } from 'app/core/internationalization';
import { getBackendSrv } from 'app/core/services/backend_srv';
import { saveDashboard } from 'app/features/manage-dashboards/state/actions';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { DashboardMeta } from 'app/types';
import { RemovePanelEvent } from '../../../types/events';
@ -65,7 +65,7 @@ export class DashboardSrv {
saveJSONDashboard(json: string) {
const parsedJson = JSON.parse(json);
return saveDashboard({
return getDashboardAPI().saveDashboard({
dashboard: parsedJson,
folderUid: this.dashboard?.meta.folderUid || parsedJson.folderUid,
});

@ -2,6 +2,7 @@ import { TimeZone } from '@grafana/data';
import { getBackendSrv } from '@grafana/runtime';
import { notifyApp } from 'app/core/actions';
import { createSuccessNotification } from 'app/core/copy/appNotification';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { removeAllPanels } from 'app/features/panel/state/reducers';
import { updateTimeZoneForSession, updateWeekStartForSession } from 'app/features/profile/state/reducers';
@ -24,7 +25,7 @@ export function importDashboard(data: any, dashboardTitle: string): ThunkResult<
export function removeDashboard(uid: string): ThunkResult<void> {
return async (dispatch) => {
await getBackendSrv().delete(`/api/dashboards/uid/${uid}`);
await getDashboardAPI().deleteDashboard(uid, false);
dispatch(loadPluginDashboards());
};
}

@ -7,6 +7,11 @@ import { makeLogs } from '../__mocks__/makeLogs';
import { LiveLogsWithTheme } from './LiveLogs';
// Avoids errors caused by circular dependencies
jest.mock('app/features/live/dashboard/dashboardWatcher', () => ({
ignoreNextSave: jest.fn(),
}));
const setup = (rows: LogRowModel[]) =>
render(
<LiveLogsWithTheme

@ -1,6 +1,5 @@
import { MutableDataFrame } from '@grafana/data';
import { DataQuery, defaultDashboard } from '@grafana/schema';
import { backendSrv } from 'app/core/services/backend_srv';
import * as api from 'app/features/dashboard/state/initDashboard';
import { ExplorePanelData } from 'app/types';
@ -8,6 +7,15 @@ import { createEmptyQueryResponse } from '../../state/utils';
import { setDashboardInLocalStorage } from './addToDashboard';
let mockDashboard = {} as unknown;
jest.mock('app/features/dashboard/api/dashboard_api', () => ({
getDashboardAPI: () => ({
getDashboardDTO: () => {
return Promise.resolve(mockDashboard);
},
}),
}));
describe('addPanelToDashboard', () => {
let spy: jest.SpyInstance;
beforeAll(() => {
@ -71,7 +79,9 @@ describe('addPanelToDashboard', () => {
it('Previous panels should not be removed', async () => {
const queries: DataQuery[] = [{ refId: 'A' }];
const existingPanel = { prop: 'this should be kept' };
jest.spyOn(backendSrv, 'getDashboardByUid').mockResolvedValue({
// Set the mocked dashboard
mockDashboard = {
dashboard: {
...defaultDashboard,
templating: { list: [] },
@ -80,7 +90,7 @@ describe('addPanelToDashboard', () => {
panels: [existingPanel],
},
meta: {},
});
};
await setDashboardInLocalStorage({
queries,

@ -1,7 +1,7 @@
import { DataFrame, ExplorePanelsState } from '@grafana/data';
import { Dashboard, DataQuery, DataSourceRef } from '@grafana/schema';
import { DataTransformerConfig } from '@grafana/schema/dist/esm/raw/dashboard/x/dashboard_types.gen';
import { backendSrv } from 'app/core/services/backend_srv';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { setDashboardToFetchFromLocalStorage } from 'app/features/dashboard/state/initDashboard';
import { buildNewDashboardSaveModel } from 'app/features/dashboard-scene/serialization/buildNewDashboardSaveModel';
import { DashboardDTO, ExplorePanelData } from 'app/types';
@ -80,7 +80,7 @@ export async function setDashboardInLocalStorage(options: AddPanelToDashboardOpt
if (options.dashboardUid) {
try {
dto = await backendSrv.getDashboardByUid(options.dashboardUid);
dto = await getDashboardAPI().getDashboardDTO(options.dashboardUid);
} catch (e) {
throw AddToDashboardError.FETCH_DASHBOARD;
}

@ -2,6 +2,11 @@ import { DEFAULT_RANGE } from 'app/features/explore/state/utils';
import { v0Migrator } from './v0';
// Avoids errors caused by circular dependencies
jest.mock('app/features/live/dashboard/dashboardWatcher', () => ({
ignoreNextSave: jest.fn(),
}));
describe('v0 migrator', () => {
describe('parse', () => {
it('returns default state on empty string', () => {

@ -2,6 +2,11 @@ import { DEFAULT_RANGE } from 'app/features/explore/state/utils';
import { v1Migrator } from './v1';
// Avoids errors caused by circular dependencies
jest.mock('app/features/live/dashboard/dashboardWatcher', () => ({
ignoreNextSave: jest.fn(),
}));
jest.mock('app/core/utils/explore', () => ({
...jest.requireActual('app/core/utils/explore'),
generateExploreId: () => 'ID',

@ -3,8 +3,7 @@ import { getBackendSrv, getDataSourceSrv, isFetchError } from '@grafana/runtime'
import { notifyApp } from 'app/core/actions';
import { createErrorNotification } from 'app/core/copy/appNotification';
import { browseDashboardsAPI, ImportInputs } from 'app/features/browse-dashboards/api/browseDashboardsAPI';
import { SaveDashboardCommand } from 'app/features/dashboard/components/SaveDashboard/types';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { getDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
import { FolderInfo, PermissionLevelString, SearchQueryType, ThunkResult } from 'app/types';
import {
@ -16,7 +15,7 @@ import {
import { getLibraryPanel } from '../../library-panels/state/api';
import { LibraryElementDTO, LibraryElementKind } from '../../library-panels/types';
import { DashboardSearchHit } from '../../search/types';
import { DashboardJson, DeleteDashboardResponse } from '../types';
import { DashboardJson } from '../types';
import {
clearDashboard,
@ -311,17 +310,6 @@ export function deleteFoldersAndDashboards(folderUids: string[], dashboardUids:
return executeInOrder(tasks);
}
export function saveDashboard(options: SaveDashboardCommand) {
dashboardWatcher.ignoreNextSave();
return getBackendSrv().post('/api/dashboards/db/', {
dashboard: options.dashboard,
message: options.message ?? '',
overwrite: options.overwrite ?? false,
folderUid: options.folderUid,
});
}
function deleteFolder(uid: string, showSuccessAlert: boolean) {
return getBackendSrv().delete(`/api/folders/${uid}?forceDeleteRules=false`, undefined, { showSuccessAlert });
}
@ -360,7 +348,7 @@ export function getFolderById(id: number): Promise<{ id: number; title: string }
}
export function deleteDashboard(uid: string, showSuccessAlert: boolean) {
return getBackendSrv().delete<DeleteDashboardResponse>(`/api/dashboards/uid/${uid}`, { showSuccessAlert });
return getDashboardAPI().deleteDashboard(uid, showSuccessAlert);
}
function executeInOrder(tasks: any[]): Promise<unknown> {

@ -53,6 +53,12 @@ const setup = (propOverrides?: object) => {
);
};
jest.mock('app/features/dashboard/api/dashboard_api', () => ({
getDashboardAPI: () => ({
getDashboardDTO: jest.fn().mockResolvedValue({}),
}),
}));
describe('Render', () => {
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});

@ -22,6 +22,12 @@ jest.mock('app/core/hooks/useQueryParams', () => ({
useQueryParams: () => [{}],
}));
jest.mock('app/features/dashboard/api/dashboard_api', () => ({
getDashboardAPI: () => ({
getDashboardDTO: jest.fn().mockResolvedValue({}),
}),
}));
const defaultProps: Props = {
...initialUserState,
user: {

Loading…
Cancel
Save