Skip to content

Commit e25071a

Browse files
DariaKunoichiGurinderRawala
authored andcommitted
Load dashboard with specific version passed from FN UI (#68)
1 parent 47b3c38 commit e25071a

File tree

8 files changed

+61
-25
lines changed

8 files changed

+61
-25
lines changed

pkg/api/dashboard.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,12 @@ func (hs *HTTPServer) GetDashboardVersion(c *contextmodel.ReqContext) response.R
904904
CreatedBy: creator,
905905
}
906906

907-
return response.JSON(http.StatusOK, dashVersionMeta)
907+
dto := dtos.DashboardFullWithMeta{
908+
Dashboard: res.Data,
909+
Meta: meta,
910+
}
911+
912+
return response.JSON(http.StatusOK, dto)
908913
}
909914

910915
// swagger:route POST /dashboards/calculate-diff dashboards calculateDashboardDiff

public/app/core/reducers/fn-slice.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface FnGlobalState {
88
FNDashboard: boolean;
99
uid: string;
1010
slug: string;
11+
version: number;
1112
mode: GrafanaThemeType.Light | GrafanaThemeType.Dark;
1213
controlsContainer: string | null;
1314
pageTitle: string;
@@ -20,7 +21,7 @@ export type UpdateFNGlobalStateAction = PayloadAction<Partial<FnGlobalState>>;
2021

2122
export type SetFnStateAction = PayloadAction<Omit<FnGlobalState, 'hiddenVariables'>>;
2223

23-
export type FnPropMappedFromState = Extract<keyof FnGlobalState, 'FNDashboard' | 'hiddenVariables' | 'mode' | 'uid' | 'queryParams' | 'slug'>;
24+
export type FnPropMappedFromState = Extract<keyof FnGlobalState, 'FNDashboard' | 'hiddenVariables' | 'mode' | 'uid' | 'queryParams' | 'slug' | 'version'>;
2425
export type FnStateProp = keyof FnGlobalState;
2526

2627
export type FnPropsMappedFromState = Pick<FnGlobalState, FnPropMappedFromState>;
@@ -34,6 +35,7 @@ export const fnStateProps: FnStateProp[] = [
3435
'queryParams',
3536
'slug',
3637
'uid',
38+
'version',
3739
];
3840

3941
export const fnPropsMappedFromState: readonly FnPropMappedFromState[] = [
@@ -43,6 +45,7 @@ export const fnPropsMappedFromState: readonly FnPropMappedFromState[] = [
4345
'uid',
4446
'queryParams',
4547
'slug',
48+
'version',
4649
] as const;
4750

4851
const INITIAL_MODE = GrafanaThemeType.Light;
@@ -54,6 +57,7 @@ export const INITIAL_FN_STATE: FnGlobalState = {
5457
FNDashboard: false,
5558
uid: '',
5659
slug: '',
60+
version: 1,
5761
mode: INITIAL_MODE,
5862
controlsContainer: null,
5963
pageTitle: '',

public/app/core/services/backend_srv.ts

+21-7
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,20 @@ export class BackendSrv implements BackendService {
524524
return getDashboardAPI().getDashboardDTO(uid);
525525
}
526526

527-
validateDashboard(dashboard: DashboardModel): Promise<ValidateDashboardResponse> {
528-
// support for this function will be implemented in the k8s flavored api-server
529-
// hidden by experimental feature flag:
530-
// config.featureToggles.showDashboardValidationWarnings
531-
return Promise.resolve({
532-
isValid: false,
533-
message: 'dashboard validation is not supported',
527+
getDashboardByUidVersion(uid: string, version: number): Promise<DashboardDTO> {
528+
return this.get<DashboardDTO>(`/api/dashboards/uid/${uid}/versions/${version}`);
529+
}
530+
531+
validateDashboard(dashboard: DashboardModel) {
532+
// We want to send the dashboard as a JSON string (in the JSON body payload) so we can get accurate error line numbers back
533+
const dashboardJson = JSON.stringify(dashboard, replaceJsonNulls, 2);
534+
535+
return this.request<ValidateDashboardResponse>({
536+
method: 'POST',
537+
url: `/api/dashboards/validate`,
538+
data: { dashboard: dashboardJson },
539+
showSuccessAlert: false,
540+
showErrorAlert: false,
534541
});
535542
}
536543

@@ -562,3 +569,10 @@ interface ValidateDashboardResponse {
562569
isValid: boolean;
563570
message?: string;
564571
}
572+
573+
function replaceJsonNulls<T extends unknown>(key: string, value: T): T | undefined {
574+
if (typeof value === 'number' && !isFinite(value)) {
575+
return undefined;
576+
}
577+
return value;
578+
}

public/app/features/dashboard/containers/DashboardPage.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const connector = connect(mapStateToProps, mapDispatchToProps);
9292
type OwnProps = {
9393
isPublic?: boolean;
9494
controlsContainer?: string | null;
95+
version?: FNDashboardProps['version'];
9596
fnLoader?: FNDashboardProps['fnLoader'];
9697
isLoading?: FNDashboardProps['isLoading']
9798
};
@@ -201,7 +202,8 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
201202
urlSlug: match.params.slug,
202203
urlUid: match.params.uid,
203204
urlType: match.params.type,
204-
urlFolderUid: queryParams.folderUid,
205+
urlFolderId: queryParams.folderId,
206+
version: match.params.version,
205207
panelType: queryParams.panelType,
206208
routeName: this.props.route.routeName,
207209
fixUrl: !isPublic && !FNDashboard,
@@ -225,6 +227,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
225227

226228
if (
227229
prevProps.match.params.uid !== match.params.uid ||
230+
prevProps.match.params.version !== match.params.version ||
228231
(routeReloadCounter !== undefined && this.forceRouteReloadCounter !== routeReloadCounter)
229232
) {
230233
this.initDashboard();

public/app/features/dashboard/services/DashboardLoaderSrv.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ export class DashboardLoaderSrv {
3535
};
3636
}
3737

38-
loadDashboard(type: UrlQueryValue, slug: string | undefined, uid: string | undefined): Promise<DashboardDTO> {
39-
const stateManager = getDashboardScenePageStateManager();
38+
loadDashboard(type: UrlQueryValue, slug: any, uid: any, version: any) {
4039
let promise;
4140

4241
if (type === 'script' && slug) {
@@ -76,15 +75,23 @@ export class DashboardLoaderSrv {
7675
},
7776
};
7877
});
79-
} else if (uid) {
80-
const cachedDashboard = stateManager.getDashboardFromCache(uid);
81-
if (cachedDashboard) {
82-
return Promise.resolve(cachedDashboard);
83-
}
84-
85-
promise = getDashboardAPI()
86-
.getDashboardDTO(uid)
87-
.then((result) => {
78+
} else if (version !== undefined) {
79+
promise = backendSrv
80+
.getDashboardByUidVersion(uid, version)
81+
.then((result: any) => {
82+
if (result.meta.isFolder) {
83+
appEvents.emit(AppEvents.alertError, ['Dashboard with version not found']);
84+
throw new Error('Dashboard with version not found');
85+
}
86+
return result;
87+
})
88+
.catch(() => {
89+
return this._dashboardLoadFailed('Not found', true);
90+
});
91+
} else {
92+
promise = backendSrv
93+
.getDashboardByUid(uid)
94+
.then((result: any) => {
8895
if (result.meta.isFolder) {
8996
appEvents.emit(AppEvents.alertError, ['Dashboard not found']);
9097
throw new Error('Dashboard not found');

public/app/features/dashboard/state/initDashboard.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ export interface InitDashboardArgs {
3737
urlUid?: string;
3838
urlSlug?: string;
3939
urlType?: string;
40-
urlFolderUid?: string;
40+
urlFolderId?: string;
41+
version?: number;
4142
panelType?: string;
4243
accessToken?: string;
4344
routeName?: string;
@@ -84,10 +85,10 @@ async function fetchDashboard(
8485
return dashDTO;
8586
}
8687
case DashboardRoutes.Public: {
87-
return await dashboardLoaderSrv.loadDashboard('public', args.urlSlug, args.accessToken);
88+
return await dashboardLoaderSrv.loadDashboard('public', args.urlSlug, args.accessToken, args.version);
8889
}
8990
case DashboardRoutes.Normal: {
90-
const dashDTO: DashboardDTO = await dashboardLoaderSrv.loadDashboard(args.urlType, args.urlSlug, args.urlUid);
91+
const dashDTO: DashboardDTO = await dashboardLoaderSrv.loadDashboard(args.urlType, args.urlSlug, args.urlUid, args.version);
9192

9293
// only the folder API has information about ancestors
9394
// get parent folder (if it exists) and put it in the store
@@ -127,7 +128,7 @@ async function fetchDashboard(
127128
}
128129
case DashboardRoutes.Path: {
129130
const path = args.urlSlug ?? '';
130-
return await dashboardLoaderSrv.loadDashboard(DashboardRoutes.Path, path, path);
131+
return await dashboardLoaderSrv.loadDashboard(DashboardRoutes.Path, path, path, args.version);
131132
}
132133
default:
133134
throw { message: 'Unknown route ' + args.routeName };

public/app/fn-app/create-mfe.ts

+1
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ class createMfe {
292292
uid: other.uid,
293293
hiddenVariables: other.hiddenVariables,
294294
slug: other.slug,
295+
version: other.version,
295296
queryParams: other.queryParams,
296297
})
297298
);

public/app/fn-app/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export interface FNDashboardProps {
2222
name: string;
2323
uid: string;
2424
slug: string;
25+
version: number;
2526
mode: GrafanaThemeType.Dark | GrafanaThemeType.Light;
2627
queryParams: ParsedQuery<string>;
2728
fnError?: ReactNode;

0 commit comments

Comments
 (0)