actions.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { ThunkAction } from 'redux-thunk';
  2. import config from '../../../core/config';
  3. import { getBackendSrv } from '@grafana/runtime';
  4. import { getDatasourceSrv } from 'app/features/plugins/datasource_srv';
  5. import { LayoutMode } from 'app/core/components/LayoutSelector/LayoutSelector';
  6. import { updateLocation, updateNavIndex, UpdateNavIndexAction } from 'app/core/actions';
  7. import { buildNavModel } from './navModel';
  8. import { DataSourceSettings, DataSourcePluginMeta } from '@grafana/ui';
  9. import { StoreState } from 'app/types';
  10. import { LocationUpdate } from '@grafana/runtime';
  11. import { actionCreatorFactory } from 'app/core/redux';
  12. import { ActionOf, noPayloadActionCreatorFactory } from 'app/core/redux/actionCreatorFactory';
  13. import { getPluginSettings } from 'app/features/plugins/PluginSettingsCache';
  14. import { importDataSourcePlugin } from 'app/features/plugins/plugin_loader';
  15. export const dataSourceLoaded = actionCreatorFactory<DataSourceSettings>('LOAD_DATA_SOURCE').create();
  16. export const dataSourcesLoaded = actionCreatorFactory<DataSourceSettings[]>('LOAD_DATA_SOURCES').create();
  17. export const dataSourceMetaLoaded = actionCreatorFactory<DataSourcePluginMeta>('LOAD_DATA_SOURCE_META').create();
  18. export const dataSourceTypesLoad = noPayloadActionCreatorFactory('LOAD_DATA_SOURCE_TYPES').create();
  19. export const dataSourceTypesLoaded = actionCreatorFactory<DataSourcePluginMeta[]>('LOADED_DATA_SOURCE_TYPES').create();
  20. export const setDataSourcesSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCES_SEARCH_QUERY').create();
  21. export const setDataSourcesLayoutMode = actionCreatorFactory<LayoutMode>('SET_DATA_SOURCES_LAYOUT_MODE').create();
  22. export const setDataSourceTypeSearchQuery = actionCreatorFactory<string>('SET_DATA_SOURCE_TYPE_SEARCH_QUERY').create();
  23. export const setDataSourceName = actionCreatorFactory<string>('SET_DATA_SOURCE_NAME').create();
  24. export const setIsDefault = actionCreatorFactory<boolean>('SET_IS_DEFAULT').create();
  25. export type Action =
  26. | UpdateNavIndexAction
  27. | ActionOf<DataSourceSettings>
  28. | ActionOf<DataSourceSettings[]>
  29. | ActionOf<DataSourcePluginMeta>
  30. | ActionOf<DataSourcePluginMeta[]>
  31. | ActionOf<LocationUpdate>;
  32. type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
  33. export function loadDataSources(): ThunkResult<void> {
  34. return async dispatch => {
  35. const response = await getBackendSrv().get('/api/datasources');
  36. dispatch(dataSourcesLoaded(response));
  37. };
  38. }
  39. export function loadDataSource(id: number): ThunkResult<void> {
  40. return async dispatch => {
  41. const dataSource = await getBackendSrv().get(`/api/datasources/${id}`);
  42. const pluginInfo = (await getPluginSettings(dataSource.type)) as DataSourcePluginMeta;
  43. const plugin = await importDataSourcePlugin(pluginInfo);
  44. dispatch(dataSourceLoaded(dataSource));
  45. dispatch(dataSourceMetaLoaded(pluginInfo));
  46. dispatch(updateNavIndex(buildNavModel(dataSource, plugin)));
  47. };
  48. }
  49. export function addDataSource(plugin: DataSourcePluginMeta): ThunkResult<void> {
  50. return async (dispatch, getStore) => {
  51. await dispatch(loadDataSources());
  52. const dataSources = getStore().dataSources.dataSources;
  53. const newInstance = {
  54. name: plugin.name,
  55. type: plugin.id,
  56. access: 'proxy',
  57. isDefault: dataSources.length === 0,
  58. };
  59. if (nameExits(dataSources, newInstance.name)) {
  60. newInstance.name = findNewName(dataSources, newInstance.name);
  61. }
  62. const result = await getBackendSrv().post('/api/datasources', newInstance);
  63. dispatch(updateLocation({ path: `/datasources/edit/${result.id}` }));
  64. };
  65. }
  66. export function loadDataSourceTypes(): ThunkResult<void> {
  67. return async dispatch => {
  68. dispatch(dataSourceTypesLoad());
  69. const result = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' });
  70. dispatch(dataSourceTypesLoaded(result as DataSourcePluginMeta[]));
  71. };
  72. }
  73. export function updateDataSource(dataSource: DataSourceSettings): ThunkResult<void> {
  74. return async dispatch => {
  75. await getBackendSrv().put(`/api/datasources/${dataSource.id}`, dataSource);
  76. await updateFrontendSettings();
  77. return dispatch(loadDataSource(dataSource.id));
  78. };
  79. }
  80. export function deleteDataSource(): ThunkResult<void> {
  81. return async (dispatch, getStore) => {
  82. const dataSource = getStore().dataSources.dataSource;
  83. await getBackendSrv().delete(`/api/datasources/${dataSource.id}`);
  84. await updateFrontendSettings();
  85. dispatch(updateLocation({ path: '/datasources' }));
  86. };
  87. }
  88. interface ItemWithName {
  89. name: string;
  90. }
  91. export function nameExits(dataSources: ItemWithName[], name: string) {
  92. return (
  93. dataSources.filter(dataSource => {
  94. return dataSource.name.toLowerCase() === name.toLowerCase();
  95. }).length > 0
  96. );
  97. }
  98. export function findNewName(dataSources: ItemWithName[], name: string) {
  99. // Need to loop through current data sources to make sure
  100. // the name doesn't exist
  101. while (nameExits(dataSources, name)) {
  102. // If there's a duplicate name that doesn't end with '-x'
  103. // we can add -1 to the name and be done.
  104. if (!nameHasSuffix(name)) {
  105. name = `${name}-1`;
  106. } else {
  107. // if there's a duplicate name that ends with '-x'
  108. // we can try to increment the last digit until the name is unique
  109. // remove the 'x' part and replace it with the new number
  110. name = `${getNewName(name)}${incrementLastDigit(getLastDigit(name))}`;
  111. }
  112. }
  113. return name;
  114. }
  115. function updateFrontendSettings() {
  116. return getBackendSrv()
  117. .get('/api/frontend/settings')
  118. .then(settings => {
  119. config.datasources = settings.datasources;
  120. config.defaultDatasource = settings.defaultDatasource;
  121. getDatasourceSrv().init();
  122. });
  123. }
  124. function nameHasSuffix(name: string) {
  125. return name.endsWith('-', name.length - 1);
  126. }
  127. function getLastDigit(name: string) {
  128. return parseInt(name.slice(-1), 10);
  129. }
  130. function incrementLastDigit(digit: number) {
  131. return isNaN(digit) ? 1 : digit + 1;
  132. }
  133. function getNewName(name: string) {
  134. return name.slice(0, name.length - 1);
  135. }