Files
grafana/public/app/features/datasources/components/EditDataSource.tsx
Marcus Andersson b804b2f073 Plugins: Added hook to make it easier to track interactions in plugins (#56126)
* first stab at context away plugin tracking.

* adding a plugin context and a hook to get hold of a tracker that always appends the plugin context information.

* wip

* improved the code a bit.

* wip

* Fixed type errors.

* added datasource_uid to data sources.

* fixed error message when trying to use hook outside of context.

* small refactoring according to feedback.

* using the correct provider for data source context.

* check not needed.

* enforcing the interaction name to start with grafana_plugin_

* exposing guards for the other context type.

* added structure for writing reporter hook tests.

* added some more tests.

* added tests.

* reverted back to inheritance between context types.

* adding mock for getDataSourceSrv
2022-11-02 16:57:57 +01:00

188 lines
5.7 KiB
TypeScript

import { AnyAction } from '@reduxjs/toolkit';
import React from 'react';
import {
DataSourcePluginContextProvider,
DataSourcePluginMeta,
DataSourceSettings as DataSourceSettingsType,
} from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
import PageLoader from 'app/core/components/PageLoader/PageLoader';
import { DataSourceSettingsState, useDispatch } from 'app/types';
import {
dataSourceLoaded,
setDataSourceName,
setIsDefault,
useDataSource,
useDataSourceExploreUrl,
useDataSourceMeta,
useDataSourceRights,
useDataSourceSettings,
useDeleteLoadedDataSource,
useInitDataSourceSettings,
useTestDataSource,
useUpdateDatasource,
} from '../state';
import { DataSourceRights } from '../types';
import { BasicSettings } from './BasicSettings';
import { ButtonRow } from './ButtonRow';
import { CloudInfoBox } from './CloudInfoBox';
import { DataSourceLoadError } from './DataSourceLoadError';
import { DataSourceMissingRightsMessage } from './DataSourceMissingRightsMessage';
import { DataSourcePluginConfigPage } from './DataSourcePluginConfigPage';
import { DataSourcePluginSettings } from './DataSourcePluginSettings';
import { DataSourcePluginState } from './DataSourcePluginState';
import { DataSourceReadOnlyMessage } from './DataSourceReadOnlyMessage';
import { DataSourceTestingStatus } from './DataSourceTestingStatus';
export type Props = {
// The ID of the data source
uid: string;
// The ID of the custom datasource setting page
pageId?: string | null;
};
export function EditDataSource({ uid, pageId }: Props) {
useInitDataSourceSettings(uid);
const dispatch = useDispatch();
const dataSource = useDataSource(uid);
const dataSourceMeta = useDataSourceMeta(dataSource.type);
const dataSourceSettings = useDataSourceSettings();
const dataSourceRights = useDataSourceRights(uid);
const exploreUrl = useDataSourceExploreUrl(uid);
const onDelete = useDeleteLoadedDataSource();
const onTest = useTestDataSource(uid);
const onUpdate = useUpdateDatasource();
const onDefaultChange = (value: boolean) => dispatch(setIsDefault(value));
const onNameChange = (name: string) => dispatch(setDataSourceName(name));
const onOptionsChange = (ds: DataSourceSettingsType) => dispatch(dataSourceLoaded(ds));
return (
<EditDataSourceView
pageId={pageId}
dataSource={dataSource}
dataSourceMeta={dataSourceMeta}
dataSourceSettings={dataSourceSettings}
dataSourceRights={dataSourceRights}
exploreUrl={exploreUrl}
onDelete={onDelete}
onDefaultChange={onDefaultChange}
onNameChange={onNameChange}
onOptionsChange={onOptionsChange}
onTest={onTest}
onUpdate={onUpdate}
/>
);
}
export type ViewProps = {
pageId?: string | null;
dataSource: DataSourceSettingsType;
dataSourceMeta: DataSourcePluginMeta;
dataSourceSettings: DataSourceSettingsState;
dataSourceRights: DataSourceRights;
exploreUrl: string;
onDelete: () => void;
onDefaultChange: (isDefault: boolean) => AnyAction;
onNameChange: (name: string) => AnyAction;
onOptionsChange: (dataSource: DataSourceSettingsType) => AnyAction;
onTest: () => void;
onUpdate: (dataSource: DataSourceSettingsType) => Promise<DataSourceSettingsType>;
};
export function EditDataSourceView({
pageId,
dataSource,
dataSourceMeta,
dataSourceSettings,
dataSourceRights,
exploreUrl,
onDelete,
onDefaultChange,
onNameChange,
onOptionsChange,
onTest,
onUpdate,
}: ViewProps) {
const { plugin, loadError, testingStatus, loading } = dataSourceSettings;
const { readOnly, hasWriteRights, hasDeleteRights } = dataSourceRights;
const hasDataSource = dataSource.id > 0;
const dsi = getDataSourceSrv()?.getInstanceSettings(dataSource.uid);
const hasAlertingEnabled = Boolean(dsi?.meta?.alerting ?? false);
const isAlertManagerDatasource = dsi?.type === 'alertmanager';
const alertingSupported = hasAlertingEnabled || isAlertManagerDatasource;
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
await onUpdate({ ...dataSource });
onTest();
};
if (loadError) {
return <DataSourceLoadError dataSourceRights={dataSourceRights} onDelete={onDelete} />;
}
if (loading) {
return <PageLoader />;
}
// TODO - is this needed?
if (!hasDataSource || !dsi) {
return null;
}
if (pageId) {
return (
<DataSourcePluginContextProvider instanceSettings={dsi}>
<DataSourcePluginConfigPage pageId={pageId} plugin={plugin} />;
</DataSourcePluginContextProvider>
);
}
return (
<form onSubmit={onSubmit}>
{!hasWriteRights && <DataSourceMissingRightsMessage />}
{readOnly && <DataSourceReadOnlyMessage />}
{dataSourceMeta.state && <DataSourcePluginState state={dataSourceMeta.state} />}
<CloudInfoBox dataSource={dataSource} />
<BasicSettings
dataSourceName={dataSource.name}
isDefault={dataSource.isDefault}
onDefaultChange={onDefaultChange}
onNameChange={onNameChange}
alertingSupported={alertingSupported}
/>
{plugin && (
<DataSourcePluginContextProvider instanceSettings={dsi}>
<DataSourcePluginSettings
plugin={plugin}
dataSource={dataSource}
dataSourceMeta={dataSourceMeta}
onModelChange={onOptionsChange}
/>
</DataSourcePluginContextProvider>
)}
<DataSourceTestingStatus testingStatus={testingStatus} />
<ButtonRow
onSubmit={onSubmit}
onDelete={onDelete}
onTest={onTest}
exploreUrl={exploreUrl}
canSave={!readOnly && hasWriteRights}
canDelete={!readOnly && hasDeleteRights}
/>
</form>
);
}