Folders: Update folder using app platform APIs (#110449)
This commit is contained in:
@@ -11,6 +11,7 @@ const collator = new Intl.Collator();
|
||||
const mockAccessControl = {
|
||||
'dashboards.permissions:write': true,
|
||||
'dashboards:create': true,
|
||||
'folders:write': true,
|
||||
};
|
||||
const additionalProperties = {
|
||||
canAdmin: true,
|
||||
@@ -108,6 +109,19 @@ const createFolderHandler = () =>
|
||||
});
|
||||
});
|
||||
|
||||
const handlers = [listFoldersHandler(), getFolderHandler(), createFolderHandler()];
|
||||
const saveFolderHandler = () =>
|
||||
http.put<{ uid: string }, { title: string; version: number }>('/api/folders/:uid', async ({ params, request }) => {
|
||||
const { uid } = params;
|
||||
const body = await request.json();
|
||||
const folder = mockTree.find((v) => v.item.uid === uid);
|
||||
|
||||
if (!folder) {
|
||||
return HttpResponse.json({ message: 'folder not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json({ ...folder.item, title: body.title });
|
||||
});
|
||||
|
||||
const handlers = [listFoldersHandler(), getFolderHandler(), createFolderHandler(), saveFolderHandler()];
|
||||
|
||||
export default handlers;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/** Expected constant response from `/api/search/sorting` */
|
||||
export const SORT_OPTIONS = {
|
||||
sortOptions: [
|
||||
{
|
||||
description: 'Sort results in an alphabetically ascending order',
|
||||
displayName: 'Alphabetically (A–Z)',
|
||||
meta: '',
|
||||
name: 'alpha-asc',
|
||||
},
|
||||
{
|
||||
description: 'Sort results in an alphabetically descending order',
|
||||
displayName: 'Alphabetically (Z–A)',
|
||||
meta: '',
|
||||
name: 'alpha-desc',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -3,6 +3,8 @@ import { HttpResponse, http } from 'msw';
|
||||
|
||||
import { wellFormedTree } from '../../../fixtures/folders';
|
||||
|
||||
import { SORT_OPTIONS } from './constants';
|
||||
|
||||
const [mockTree] = wellFormedTree();
|
||||
|
||||
type FilterArray = Array<(v: (typeof mockTree)[number]) => boolean>;
|
||||
@@ -70,4 +72,6 @@ const getLegacySearchHandler = () =>
|
||||
return HttpResponse.json(response);
|
||||
});
|
||||
|
||||
export default [getLegacySearchHandler()];
|
||||
const getSearchSortingHandler = () => http.get('/api/search/sorting', () => HttpResponse.json(SORT_OPTIONS));
|
||||
|
||||
export default [getLegacySearchHandler(), getSearchSortingHandler()];
|
||||
|
||||
+19
-9
@@ -7,32 +7,42 @@ const [mockTree] = wellFormedTree();
|
||||
|
||||
type FilterArray = Array<(v: (typeof mockTree)[number]) => boolean>;
|
||||
|
||||
const typeMap: Record<string, string> = {
|
||||
folder: 'folders',
|
||||
dashboard: 'dashboards',
|
||||
};
|
||||
|
||||
const getSearchHandler = () =>
|
||||
http.get('/apis/dashboard.grafana.app/v0alpha1/namespaces/default/search', ({ request }) => {
|
||||
http.get('/apis/dashboard.grafana.app/v0alpha1/namespaces/:namespace/search', ({ request }) => {
|
||||
const folderFilter = new URL(request.url).searchParams.get('folder') || null;
|
||||
const typeFilter = new URL(request.url).searchParams.get('type') || null;
|
||||
const response = mockTree
|
||||
.filter((filterItem) => {
|
||||
const filters: FilterArray = [];
|
||||
if (folderFilter && folderFilter !== 'general') {
|
||||
filters.push(({ item }) => item.kind === 'folder' && item.parentUID === folderFilter);
|
||||
}
|
||||
|
||||
if (folderFilter === 'general') {
|
||||
filters.push(({ item }) => item.kind === 'folder' && item.parentUID === undefined);
|
||||
}
|
||||
|
||||
if (typeFilter) {
|
||||
filters.push(({ item }) => item.kind === typeFilter);
|
||||
}
|
||||
|
||||
if (folderFilter && folderFilter !== 'general') {
|
||||
filters.push(
|
||||
({ item }) => (item.kind === 'folder' || item.kind === 'dashboard') && item.parentUID === folderFilter
|
||||
);
|
||||
}
|
||||
|
||||
if (folderFilter === 'general') {
|
||||
filters.push(
|
||||
({ item }) => (item.kind === 'folder' || item.kind === 'dashboard') && item.parentUID === undefined
|
||||
);
|
||||
}
|
||||
|
||||
return filters.every((filterPredicate) => filterPredicate(filterItem));
|
||||
})
|
||||
|
||||
.map(({ item }) => {
|
||||
const random = Chance(item.uid);
|
||||
return {
|
||||
resource: 'folders',
|
||||
resource: typeMap[item.kind],
|
||||
name: item.uid,
|
||||
title: item.title,
|
||||
field: {
|
||||
|
||||
+61
-49
@@ -11,6 +11,32 @@ const baseResponse = {
|
||||
apiVersion: 'folder.grafana.app/v1beta1',
|
||||
};
|
||||
|
||||
const folderToAppPlatform = (folder: (typeof mockTree)[number]['item'], id?: number, namespace?: string) => {
|
||||
return {
|
||||
...baseResponse,
|
||||
|
||||
metadata: {
|
||||
name: folder.uid,
|
||||
namespace: namespace ?? 'default',
|
||||
uid: folder.uid,
|
||||
creationTimestamp: '2023-01-01T00:00:00Z',
|
||||
annotations: {
|
||||
// TODO: Generalise annotations in fixture data
|
||||
'grafana.app/createdBy': 'user:1',
|
||||
'grafana.app/updatedBy': 'user:2',
|
||||
'grafana.app/managedBy': 'user',
|
||||
'grafana.app/updatedTimestamp': '2024-01-01T00:00:00Z',
|
||||
'grafana.app/folder': folder.kind === 'folder' ? folder.parentUID : undefined,
|
||||
},
|
||||
labels: {
|
||||
'grafana.app/deprecatedInternalID': id ?? '123',
|
||||
},
|
||||
},
|
||||
spec: { title: folder.title, description: '' },
|
||||
status: {},
|
||||
};
|
||||
};
|
||||
|
||||
const folderNotFoundError = getErrorResponse('folder not found', 404);
|
||||
|
||||
const getFolderHandler = () =>
|
||||
@@ -26,28 +52,9 @@ const getFolderHandler = () =>
|
||||
return HttpResponse.json(folderNotFoundError, { status: 404 });
|
||||
}
|
||||
|
||||
return HttpResponse.json({
|
||||
...baseResponse,
|
||||
metadata: {
|
||||
name: response.item.uid,
|
||||
namespace,
|
||||
uid: response.item.uid,
|
||||
creationTimestamp: '2023-01-01T00:00:00Z',
|
||||
annotations: {
|
||||
// TODO: Generalise annotations in fixture data
|
||||
'grafana.app/createdBy': 'user:1',
|
||||
'grafana.app/updatedBy': 'user:2',
|
||||
'grafana.app/managedBy': 'user',
|
||||
'grafana.app/updatedTimestamp': '2024-01-01T00:00:00Z',
|
||||
'grafana.app/folder': response.item.kind === 'folder' ? response.item.parentUID : undefined,
|
||||
},
|
||||
labels: {
|
||||
'grafana.app/deprecatedInternalID': '123',
|
||||
},
|
||||
},
|
||||
spec: { title: response.item.title, description: '' },
|
||||
status: {},
|
||||
});
|
||||
const appPlatformFolder = folderToAppPlatform(response.item, undefined, namespace);
|
||||
|
||||
return HttpResponse.json(appPlatformFolder);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -121,35 +128,40 @@ const createFolderHandler = () =>
|
||||
|
||||
const parentUid = body?.metadata?.annotations?.['grafana.app/folder'];
|
||||
const random = Chance(title);
|
||||
const name = random.string({ length: 10 });
|
||||
const uid = random.string({ length: 45 });
|
||||
const id = random.integer({ min: 1, max: 1000 });
|
||||
|
||||
return HttpResponse.json({
|
||||
...baseResponse,
|
||||
metadata: {
|
||||
name,
|
||||
namespace,
|
||||
uid,
|
||||
resourceVersion: '1756207979831',
|
||||
generation: 1,
|
||||
creationTimestamp: '2025-08-26T11:32:59Z',
|
||||
labels: {
|
||||
'grafana.app/deprecatedInternalID': id,
|
||||
},
|
||||
annotations: {
|
||||
'grafana.app/createdBy': 'user:1',
|
||||
'grafana.app/folder': parentUid,
|
||||
'grafana.app/updatedBy': 'user:1',
|
||||
'grafana.app/updatedTimestamp': '2025-08-26T11:32:59Z',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
title,
|
||||
description: '',
|
||||
},
|
||||
status: {},
|
||||
});
|
||||
const appPlatformFolder = folderToAppPlatform(
|
||||
{ uid, title, parentUID: parentUid, kind: 'folder' },
|
||||
id,
|
||||
namespace
|
||||
);
|
||||
return HttpResponse.json(appPlatformFolder);
|
||||
}
|
||||
);
|
||||
export default [getFolderHandler(), getFolderParentsHandler(), createFolderHandler()];
|
||||
|
||||
const replaceFolderHandler = () =>
|
||||
http.put<{ folderUid: string; namespace: string }, PartialFolderPayload>(
|
||||
'/apis/folder.grafana.app/v1beta1/namespaces/:namespace/folders/:folderUid',
|
||||
async ({ params, request }) => {
|
||||
const body = await request.json();
|
||||
const { folderUid } = params;
|
||||
const response = mockTree.find(({ item }) => {
|
||||
return item.uid === folderUid;
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
return HttpResponse.json(folderNotFoundError, { status: 404 });
|
||||
}
|
||||
|
||||
const modifiedFolder = {
|
||||
...response.item,
|
||||
title: body.spec.title,
|
||||
};
|
||||
|
||||
const appPlatformFolder = folderToAppPlatform(modifiedFolder);
|
||||
|
||||
return HttpResponse.json(appPlatformFolder);
|
||||
}
|
||||
);
|
||||
export default [getFolderHandler(), getFolderParentsHandler(), createFolderHandler(), replaceFolderHandler()];
|
||||
|
||||
Reference in New Issue
Block a user