grafana/public/app/features/datasources/state/hooks.ts
mikkancso c72322874d
Connections: Update "Your connections/Data sources" page (#58589)
* navtree.go: update Data sources title and subtitle

* DataSourceList: move add button to header

* DataSourcesList: add buttons to items

The action buttons are added inside `<Card.Tags>` so that they end up at
the right end of the card, as it was designed.

The "Build a Dashboard" button's functionality is not defined yet.

* DataSourcesListHeader: add sort picker

* fix css

* tests: look for the updated "Add new data source" text

* tests: use an async test method to verify component updates are wrapped in an act()

* update e2e selector for add data source button

* fix DataSourceList{,Page} tests

* add comment for en dash character

* simplify sorting

* add link to Build a Dashboard button

* fix test

* test build a dashboard and explore buttons

* test sorting data source elements

* DataSourceAddButton: hide button when user has no permission

* PageActionBar: remove unneeded '?'

* DataSourcesList: hide explore button if user has no permission

* DataSourcesListPage.test: make setup prop explicit

* DataSourcesList: use theme.spacing

* datasources: assure explore url includes appSubUrl

* fix tests and add test case for missing permissions

Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
2022-11-30 09:41:01 +01:00

165 lines
4.7 KiB
TypeScript

import { useContext, useEffect } from 'react';
import { DataSourcePluginMeta, DataSourceSettings, NavModelItem } from '@grafana/data';
import { cleanUpAction } from 'app/core/actions/cleanUp';
import appEvents from 'app/core/app_events';
import { contextSrv } from 'app/core/core';
import { getNavModel } from 'app/core/selectors/navModel';
import { AccessControlAction, useDispatch, useSelector } from 'app/types';
import { ShowConfirmModalEvent } from 'app/types/events';
import { DataSourceRights } from '../types';
import { constructDataSourceExploreUrl } from '../utils';
import {
initDataSourceSettings,
testDataSource,
loadDataSource,
loadDataSources,
loadDataSourcePlugins,
addDataSource,
updateDataSource,
deleteLoadedDataSource,
} from './actions';
import { DataSourcesRoutesContext } from './contexts';
import { getDataSourceLoadingNav, buildNavModel, getDataSourceNav } from './navModel';
import { getDataSource, getDataSourceMeta } from './selectors';
export const useInitDataSourceSettings = (uid: string) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(initDataSourceSettings(uid));
return function cleanUp() {
dispatch(
cleanUpAction({
cleanupAction: (state) => state.dataSourceSettings,
})
);
};
}, [uid, dispatch]);
};
export const useTestDataSource = (uid: string) => {
const dispatch = useDispatch();
return () => dispatch(testDataSource(uid));
};
export const useLoadDataSources = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadDataSources());
}, [dispatch]);
};
export const useLoadDataSource = (uid: string) => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadDataSource(uid));
}, [dispatch, uid]);
};
export const useLoadDataSourcePlugins = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadDataSourcePlugins());
}, [dispatch]);
};
export const useAddDatasource = () => {
const dispatch = useDispatch();
const dataSourcesRoutes = useDataSourcesRoutes();
return (plugin: DataSourcePluginMeta) => {
dispatch(addDataSource(plugin, dataSourcesRoutes.Edit));
};
};
export const useUpdateDatasource = () => {
const dispatch = useDispatch();
return async (dataSource: DataSourceSettings) => dispatch(updateDataSource(dataSource));
};
export const useDeleteLoadedDataSource = () => {
const dispatch = useDispatch();
const { name } = useSelector((state) => state.dataSources.dataSource);
return () => {
appEvents.publish(
new ShowConfirmModalEvent({
title: 'Delete',
text: `Are you sure you want to delete the "${name}" data source?`,
yesText: 'Delete',
icon: 'trash-alt',
onConfirm: () => dispatch(deleteLoadedDataSource()),
})
);
};
};
export const useDataSource = (uid: string) => {
return useSelector((state) => getDataSource(state.dataSources, uid));
};
export const useDataSourceExploreUrl = (uid: string) => {
const dataSource = useDataSource(uid);
return constructDataSourceExploreUrl(dataSource);
};
export const useDataSourceMeta = (pluginType: string): DataSourcePluginMeta => {
return useSelector((state) => getDataSourceMeta(state.dataSources, pluginType));
};
export const useDataSourceSettings = () => {
return useSelector((state) => state.dataSourceSettings);
};
export const useDataSourceSettingsNav = (dataSourceId: string, pageId: string | null) => {
const dataSource = useDataSource(dataSourceId);
const { plugin, loadError, loading } = useDataSourceSettings();
const navIndex = useSelector((state) => state.navIndex);
const navIndexId = pageId ? `datasource-${pageId}-${dataSourceId}` : `datasource-settings-${dataSourceId}`;
if (loadError) {
const node: NavModelItem = {
text: loadError,
subTitle: 'Data Source Error',
icon: 'exclamation-triangle',
};
return {
node: node,
main: node,
};
}
if (loading || !plugin) {
return getNavModel(navIndex, navIndexId, getDataSourceLoadingNav('settings'));
}
return getNavModel(navIndex, navIndexId, getDataSourceNav(buildNavModel(dataSource, plugin), pageId || 'settings'));
};
export const useDataSourceRights = (uid: string): DataSourceRights => {
const dataSource = useDataSource(uid);
const readOnly = dataSource.readOnly === true;
const hasWriteRights = contextSrv.hasPermissionInMetadata(AccessControlAction.DataSourcesWrite, dataSource);
const hasDeleteRights = contextSrv.hasPermissionInMetadata(AccessControlAction.DataSourcesDelete, dataSource);
return {
readOnly,
hasWriteRights,
hasDeleteRights,
};
};
export const useDataSourcesRoutes = () => {
return useContext(DataSourcesRoutesContext);
};