grafana/public/app/features/dashboard/api/ResponseTransformers.ts

180 lines
6.8 KiB
TypeScript
Raw Normal View History

Dashboard API versions handling (#96666) * structure apic * API versioning proposal * Make api service independent from version * Update v2 * Fix public dashboards page test * Uncomment reload dashboard feature code * Revert * Betterer * Fix imports * useV2DashboardsAPI feature toggle * POC/v2 schema: Add v1<-> v2 transformers (#97058) * Make dshboard access interface more precise * Add first pass for schema v1<->v2 transformers * Update response transformer test * Import fixes * Manage dashboards validation: Handle v2 schema * Handle new dashboard with v2 * Fix tests * Move dashboard is folder error handling to legacy API implementation * Add tests for dashboard api client * betterer * Use dashboard DTO when capturing dashbaord impression * prettier * Dashboard API: resolve folder metadata * Add tests for resolving folder metadata * Fix DashboardPicker * Renames and nits * POC Alternative Suggestion for Dashboard API versions handling (#97789) * Add transitional_dashboard_api, reset components that are not ready for v2 schema, and start working on migrating DashboardPicker to use v2 schema * reset DashboardScenePageStateManager * Improve logic in transitional api, also remove isDashboardResource checks from components * REmove transitional_dashboard_api and apply PR feedback * Apply PR feedback, use 'v2' as a parameter and remove unnecesary if * Fix tests * Adding missing comments from original PR and also changing order to improve diffing in github :) * update betterer * fix prettier * Add tests for DashboardPicker * Do not use unified alerting mocks * Fix unused type in dashboard test * Improve comments in DahboardPicker * Update folder validation fn * Validation update * Update legacy api test * Lint --------- Co-authored-by: alexandra vargas <alexa1866@gmail.com> Co-authored-by: Alexa V <239999+axelavargas@users.noreply.github.com> Co-authored-by: Ivan Ortega <ivanortegaalba@gmail.com>
2024-12-18 05:17:09 +08:00
import {
DashboardV2Spec,
defaultDashboardV2Spec,
defaultTimeSettingsSpec,
} from '@grafana/schema/dist/esm/schema/dashboard/v2alpha0/dashboard.gen';
import { transformCursorSynctoEnum } from 'app/features/dashboard-scene/serialization/transformToV2TypesUtils';
import { DashboardDataDTO, DashboardDTO } from 'app/types';
import { DashboardWithAccessInfo } from './types';
import { isDashboardResource, isDashboardV0Spec, isDashboardV2Spec } from './utils';
export function ensureV2Response(
dto: DashboardDTO | DashboardWithAccessInfo<DashboardDataDTO> | DashboardWithAccessInfo<DashboardV2Spec>
): DashboardWithAccessInfo<DashboardV2Spec> {
if (isDashboardResource(dto) && isDashboardV2Spec(dto.spec)) {
return dto as DashboardWithAccessInfo<DashboardV2Spec>;
}
// after discarding the dto is not a v2 spec, we can safely assume it's a v0 spec or a dashboardDTO
dto = dto as unknown as DashboardWithAccessInfo<DashboardDataDTO> | DashboardDTO;
const timeSettingsDefaults = defaultTimeSettingsSpec();
const dashboardDefaults = defaultDashboardV2Spec();
const dashboard = isDashboardResource(dto) ? dto.spec : dto.dashboard;
const accessAndMeta = isDashboardResource(dto)
? {
...dto.access,
created: dto.metadata.creationTimestamp,
createdBy: dto.metadata.annotations?.['grafana.app/createdBy'],
updatedBy: dto.metadata.annotations?.['grafana.app/updatedBy'],
updated: dto.metadata.annotations?.['grafana.app/updatedTimestamp'],
folderUid: dto.metadata.annotations?.['grafana.app/folder'],
slug: dto.metadata.annotations?.['grafana.app/slug'],
}
: dto.meta;
const spec: DashboardV2Spec = {
title: dashboard.title,
description: dashboard.description,
tags: dashboard.tags,
schemaVersion: dashboard.schemaVersion,
cursorSync: transformCursorSynctoEnum(dashboard.graphTooltip),
preload: dashboard.preload || dashboardDefaults.preload,
liveNow: dashboard.liveNow,
editable: dashboard.editable,
timeSettings: {
from: dashboard.time?.from || timeSettingsDefaults.from,
to: dashboard.time?.to || timeSettingsDefaults.to,
timezone: dashboard.timezone || timeSettingsDefaults.timezone,
autoRefresh: dashboard.refresh || timeSettingsDefaults.autoRefresh,
autoRefreshIntervals: dashboard.timepicker?.refresh_intervals || timeSettingsDefaults.autoRefreshIntervals,
fiscalYearStartMonth: dashboard.fiscalYearStartMonth || timeSettingsDefaults.fiscalYearStartMonth,
hideTimepicker: dashboard.timepicker?.hidden || timeSettingsDefaults.hideTimepicker,
quickRanges: dashboard.timepicker?.time_options || timeSettingsDefaults.quickRanges,
weekStart: dashboard.weekStart || timeSettingsDefaults.weekStart,
nowDelay: dashboard.timepicker?.nowDelay || timeSettingsDefaults.nowDelay,
},
links: dashboard.links || [],
annotations: [], // TODO
variables: [], // todo
elements: {}, // todo
layout: {
// todo
kind: 'GridLayout',
spec: {
items: [],
},
},
};
return {
apiVersion: 'v2alpha1',
kind: 'DashboardWithAccessInfo',
metadata: {
creationTimestamp: accessAndMeta.created || '', // TODO verify this empty string is valid
name: dashboard.uid,
resourceVersion: dashboard.version?.toString() || '0',
annotations: {
'grafana.app/createdBy': accessAndMeta.createdBy,
'grafana.app/updatedBy': accessAndMeta.updatedBy,
'grafana.app/updatedTimestamp': accessAndMeta.updated,
'grafana.app/folder': accessAndMeta.folderUid,
'grafana.app/slug': accessAndMeta.slug,
},
},
spec,
access: {
url: accessAndMeta.url || '',
canAdmin: accessAndMeta.canAdmin,
canDelete: accessAndMeta.canDelete,
canEdit: accessAndMeta.canEdit,
canSave: accessAndMeta.canSave,
canShare: accessAndMeta.canShare,
canStar: accessAndMeta.canStar,
slug: accessAndMeta.slug,
annotationsPermissions: accessAndMeta.annotationsPermissions,
},
};
}
export function ensureV1Response(
dashboard: DashboardDTO | DashboardWithAccessInfo<DashboardV2Spec> | DashboardWithAccessInfo<DashboardDataDTO>
): DashboardDTO {
// if dashboard is not on v0 schema or v2 schema, return as is
if (!isDashboardResource(dashboard)) {
return dashboard;
}
const spec = dashboard.spec;
// if dashboard is on v0 schema
if (isDashboardV0Spec(spec)) {
return {
meta: {
...dashboard.access,
isNew: false,
isFolder: false,
uid: dashboard.metadata.name,
k8s: dashboard.metadata,
},
dashboard: spec,
};
} else {
// if dashboard is on v2 schema convert to v1 schema
return {
meta: {
created: dashboard.metadata.creationTimestamp,
createdBy: dashboard.metadata.annotations?.['grafana.app/createdBy'] ?? '',
updated: dashboard.metadata.annotations?.['grafana.app/updatedTimestamp'],
updatedBy: dashboard.metadata.annotations?.['grafana.app/updatedBy'],
folderUid: dashboard.metadata.annotations?.['grafana.app/folder'],
slug: dashboard.metadata.annotations?.['grafana.app/slug'],
url: dashboard.access.url,
canAdmin: dashboard.access.canAdmin,
canDelete: dashboard.access.canDelete,
canEdit: dashboard.access.canEdit,
canSave: dashboard.access.canSave,
canShare: dashboard.access.canShare,
canStar: dashboard.access.canStar,
annotationsPermissions: dashboard.access.annotationsPermissions,
},
dashboard: {
uid: dashboard.metadata.name,
title: spec.title,
description: spec.description,
tags: spec.tags,
schemaVersion: spec.schemaVersion,
// @ts-ignore TODO: Use transformers for these enums
// graphTooltip: spec.cursorSync, // Assuming transformCursorSynctoEnum is reversible
preload: spec.preload,
liveNow: spec.liveNow,
editable: spec.editable,
time: {
from: spec.timeSettings.from,
to: spec.timeSettings.to,
},
timezone: spec.timeSettings.timezone,
refresh: spec.timeSettings.autoRefresh,
timepicker: {
refresh_intervals: spec.timeSettings.autoRefreshIntervals,
hidden: spec.timeSettings.hideTimepicker,
time_options: spec.timeSettings.quickRanges,
nowDelay: spec.timeSettings.nowDelay,
},
fiscalYearStartMonth: spec.timeSettings.fiscalYearStartMonth,
weekStart: spec.timeSettings.weekStart,
version: parseInt(dashboard.metadata.resourceVersion, 10),
links: spec.links, // Assuming transformDashboardLinksToEnums is reversible
annotations: { list: [] }, // TODO
},
};
}
}
export const ResponseTransformers = {
ensureV2Response,
ensureV1Response,
};