Import: Load gcom dashboards from URL (#41799)

* mob next [ci-skip] [ci skip] [skip ci]

* mob next [ci-skip] [ci skip] [skip ci]

* mob next [ci-skip] [ci skip] [skip ci]

* mob next [ci-skip] [ci skip] [skip ci]

* mob next [ci-skip] [ci skip] [skip ci]

* mob next [ci-skip] [ci skip] [skip ci]

* mob next [ci-skip] [ci skip] [skip ci]

* Update import in reducer test

Co-authored-by: thisisobate <obasiuche62@gmail.com>
Co-authored-by: kay delaney <kay@grafana.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com>
This commit is contained in:
Ashley Harrison 2021-11-17 12:26:44 +00:00 committed by GitHub
parent 6c8bc749ca
commit 4f9c096599
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 46 deletions

View File

@ -1,33 +1,67 @@
import React, { FormEvent, PureComponent } from 'react';
import { MapDispatchToProps, MapStateToProps } from 'react-redux';
import { connect, ConnectedProps } from 'react-redux';
import { css } from '@emotion/css';
import { AppEvents, GrafanaTheme2, NavModel } from '@grafana/data';
import { AppEvents, GrafanaTheme2, LoadingState } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Button, stylesFactory, withTheme2, Input, TextArea, Field, Form, FileUpload, Themeable2 } from '@grafana/ui';
import {
Button,
Field,
FileUpload,
Form,
HorizontalGroup,
Input,
Spinner,
stylesFactory,
TextArea,
Themeable2,
VerticalGroup,
withTheme2,
} from '@grafana/ui';
import Page from 'app/core/components/Page/Page';
import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp';
import { ImportDashboardOverview } from './components/ImportDashboardOverview';
import { validateDashboardJson, validateGcomDashboard } from './utils/validation';
import { fetchGcomDashboard, importDashboardJson } from './state/actions';
import appEvents from 'app/core/app_events';
import { getNavModel } from 'app/core/selectors/navModel';
import { StoreState } from 'app/types';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { cleanUpAction } from '../../core/actions/cleanUp';
interface OwnProps extends Themeable2 {}
type DashboardImportPageRouteSearchParams = {
gcomDashboardId?: string;
};
interface ConnectedProps {
navModel: NavModel;
isLoaded: boolean;
}
type OwnProps = Themeable2 & GrafanaRouteComponentProps<{}, DashboardImportPageRouteSearchParams>;
interface DispatchProps {
fetchGcomDashboard: typeof fetchGcomDashboard;
importDashboardJson: typeof importDashboardJson;
}
const mapStateToProps = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'import', undefined, true),
loadingState: state.importDashboard.state,
});
type Props = OwnProps & ConnectedProps & DispatchProps;
const mapDispatchToProps = {
fetchGcomDashboard,
importDashboardJson,
cleanUpAction,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
type Props = OwnProps & ConnectedProps<typeof connector>;
class UnthemedDashboardImport extends PureComponent<Props> {
constructor(props: Props) {
super(props);
const { gcomDashboardId } = this.props.queryParams;
if (gcomDashboardId) {
this.getGcomDashboard({ gcomDashboard: gcomDashboardId });
return;
}
}
componentWillUnmount() {
this.props.cleanUpAction({ stateSelector: (state: StoreState) => state.importDashboard });
}
onFileUpload = (event: FormEvent<HTMLInputElement>) => {
const { importDashboardJson } = this.props;
const file = event.currentTarget.files && event.currentTarget.files.length > 0 && event.currentTarget.files[0];
@ -135,36 +169,30 @@ class UnthemedDashboardImport extends PureComponent<Props> {
}
render() {
const { isLoaded, navModel } = this.props;
const { loadingState, navModel } = this.props;
return (
<Page navModel={navModel}>
<Page.Contents>{isLoaded ? <ImportDashboardOverview /> : this.renderImportForm()}</Page.Contents>
<Page.Contents>
{loadingState === LoadingState.Loading && (
<VerticalGroup justify="center">
<HorizontalGroup justify="center">
<Spinner size={32} />
</HorizontalGroup>
</VerticalGroup>
)}
{[LoadingState.Error, LoadingState.NotStarted].includes(loadingState) && this.renderImportForm()}
{loadingState === LoadingState.Done && <ImportDashboardOverview />}
</Page.Contents>
</Page>
);
}
}
const DashboardImportUnConnected = withTheme2(UnthemedDashboardImport);
const mapStateToProps: MapStateToProps<ConnectedProps, OwnProps, StoreState> = (state: StoreState) => ({
navModel: getNavModel(state.navIndex, 'import', undefined, true),
isLoaded: state.importDashboard.isLoaded,
});
const mapDispatchToProps: MapDispatchToProps<DispatchProps, Props> = {
fetchGcomDashboard,
importDashboardJson,
};
export const DashboardImportPage = connectWithCleanUp(
mapStateToProps,
mapDispatchToProps,
(state) => state.importDashboard
)(DashboardImportUnConnected);
export default DashboardImportPage;
DashboardImportPage.displayName = 'DashboardImport';
const DashboardImport = connector(DashboardImportUnConnected);
DashboardImport.displayName = 'DashboardImport';
export default DashboardImport;
const importStyles = stylesFactory((theme: GrafanaTheme2) => {
return {

View File

@ -2,6 +2,8 @@ import { AppEvents, DataSourceInstanceSettings, locationUtil } from '@grafana/da
import { getBackendSrv } from 'app/core/services/backend_srv';
import {
clearDashboard,
fetchDashboard,
fetchFailed,
ImportDashboardDTO,
InputType,
LibraryPanelInput,
@ -23,11 +25,13 @@ import { LibraryElementExport } from '../../dashboard/components/DashExportModal
export function fetchGcomDashboard(id: string): ThunkResult<void> {
return async (dispatch) => {
try {
dispatch(fetchDashboard());
const dashboard = await getBackendSrv().get(`/api/gnet/dashboards/${id}`);
dispatch(setGcomDashboard(dashboard));
dispatch(processInputs(dashboard.json));
dispatch(processElements(dashboard.json));
} catch (error) {
dispatch(fetchFailed());
appEvents.emit(AppEvents.alertError, [error.data.message || error]);
}
};

View File

@ -15,6 +15,7 @@ import {
setLibraryPanelInputs,
} from './reducers';
import { LibraryElementDTO } from '../../library-panels/types';
import { LoadingState } from '@grafana/data';
describe('importDashboardReducer', () => {
describe('when setGcomDashboard action is dispatched', () => {
@ -32,7 +33,7 @@ describe('importDashboardReducer', () => {
},
meta: { updatedAt: '2001-01-01', orgName: 'Some Org' },
source: DashboardSource.Gcom,
isLoaded: true,
state: LoadingState.Done,
});
});
});
@ -49,7 +50,7 @@ describe('importDashboardReducer', () => {
id: null,
},
source: DashboardSource.Json,
isLoaded: true,
state: LoadingState.Done,
});
});
});
@ -63,13 +64,13 @@ describe('importDashboardReducer', () => {
title: 'Imported',
id: null,
},
isLoaded: true,
state: LoadingState.Done,
})
.whenActionIsDispatched(clearDashboard())
.thenStateShouldEqual({
...initialImportDashboardState,
dashboard: {},
isLoaded: false,
state: LoadingState.NotStarted,
});
});
});

View File

@ -1,5 +1,5 @@
import { createSlice, Draft, PayloadAction } from '@reduxjs/toolkit';
import { DataSourceInstanceSettings } from '@grafana/data';
import { DataSourceInstanceSettings, LoadingState } from '@grafana/data';
import { LibraryElementDTO } from '../../library-panels/types';
export enum DashboardSource {
@ -57,7 +57,7 @@ export interface ImportDashboardState {
dashboard: any;
source: DashboardSource;
inputs: DashboardInputs;
isLoaded: boolean;
state: LoadingState;
}
export const initialImportDashboardState: ImportDashboardState = {
@ -65,7 +65,7 @@ export const initialImportDashboardState: ImportDashboardState = {
dashboard: {},
source: DashboardSource.Json,
inputs: {} as DashboardInputs,
isLoaded: false,
state: LoadingState.NotStarted,
};
const importDashboardSlice = createSlice({
@ -79,7 +79,7 @@ const importDashboardSlice = createSlice({
};
state.meta = { updatedAt: action.payload.updatedAt, orgName: action.payload.orgName };
state.source = DashboardSource.Gcom;
state.isLoaded = true;
state.state = LoadingState.Done;
},
setJsonDashboard: (state: Draft<ImportDashboardState>, action: PayloadAction<any>) => {
state.dashboard = {
@ -88,11 +88,11 @@ const importDashboardSlice = createSlice({
};
state.meta = initialImportDashboardState.meta;
state.source = DashboardSource.Json;
state.isLoaded = true;
state.state = LoadingState.Done;
},
clearDashboard: (state: Draft<ImportDashboardState>) => {
state.dashboard = {};
state.isLoaded = false;
state.state = LoadingState.NotStarted;
},
setInputs: (state: Draft<ImportDashboardState>, action: PayloadAction<any[]>) => {
state.inputs = {
@ -104,6 +104,13 @@ const importDashboardSlice = createSlice({
setLibraryPanelInputs: (state: Draft<ImportDashboardState>, action: PayloadAction<LibraryPanelInput[]>) => {
state.inputs.libraryPanels = action.payload;
},
fetchFailed: (state: Draft<ImportDashboardState>) => {
state.dashboard = {};
state.state = LoadingState.Error;
},
fetchDashboard: (state: Draft<ImportDashboardState>) => {
state.state = LoadingState.Loading;
},
},
});
@ -113,6 +120,8 @@ export const {
setGcomDashboard,
setJsonDashboard,
setLibraryPanelInputs,
fetchFailed,
fetchDashboard,
} = importDashboardSlice.actions;
export const importDashboardReducer = importDashboardSlice.reducer;