actions.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import { ThunkAction } from 'redux-thunk';
  2. import config from '../../../core/config';
  3. import { getBackendSrv } from 'app/core/services/backend_srv';
  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 { UpdateLocationAction } from 'app/core/actions/location';
  8. import { buildNavModel } from './navModel';
  9. import { DataSourceSettings } from '@grafana/ui/src/types';
  10. import { Plugin, StoreState } from 'app/types';
  11. export enum ActionTypes {
  12. LoadDataSources = 'LOAD_DATA_SOURCES',
  13. LoadDataSourceTypes = 'LOAD_DATA_SOURCE_TYPES',
  14. LoadDataSource = 'LOAD_DATA_SOURCE',
  15. LoadDataSourceMeta = 'LOAD_DATA_SOURCE_META',
  16. SetDataSourcesSearchQuery = 'SET_DATA_SOURCES_SEARCH_QUERY',
  17. SetDataSourcesLayoutMode = 'SET_DATA_SOURCES_LAYOUT_MODE',
  18. SetDataSourceTypeSearchQuery = 'SET_DATA_SOURCE_TYPE_SEARCH_QUERY',
  19. SetDataSourceName = 'SET_DATA_SOURCE_NAME',
  20. SetIsDefault = 'SET_IS_DEFAULT',
  21. }
  22. interface LoadDataSourcesAction {
  23. type: ActionTypes.LoadDataSources;
  24. payload: DataSourceSettings[];
  25. }
  26. interface SetDataSourcesSearchQueryAction {
  27. type: ActionTypes.SetDataSourcesSearchQuery;
  28. payload: string;
  29. }
  30. interface SetDataSourcesLayoutModeAction {
  31. type: ActionTypes.SetDataSourcesLayoutMode;
  32. payload: LayoutMode;
  33. }
  34. interface LoadDataSourceTypesAction {
  35. type: ActionTypes.LoadDataSourceTypes;
  36. payload: Plugin[];
  37. }
  38. interface SetDataSourceTypeSearchQueryAction {
  39. type: ActionTypes.SetDataSourceTypeSearchQuery;
  40. payload: string;
  41. }
  42. interface LoadDataSourceAction {
  43. type: ActionTypes.LoadDataSource;
  44. payload: DataSourceSettings;
  45. }
  46. interface LoadDataSourceMetaAction {
  47. type: ActionTypes.LoadDataSourceMeta;
  48. payload: Plugin;
  49. }
  50. interface SetDataSourceNameAction {
  51. type: ActionTypes.SetDataSourceName;
  52. payload: string;
  53. }
  54. interface SetIsDefaultAction {
  55. type: ActionTypes.SetIsDefault;
  56. payload: boolean;
  57. }
  58. const dataSourcesLoaded = (dataSources: DataSourceSettings[]): LoadDataSourcesAction => ({
  59. type: ActionTypes.LoadDataSources,
  60. payload: dataSources,
  61. });
  62. const dataSourceLoaded = (dataSource: DataSourceSettings): LoadDataSourceAction => ({
  63. type: ActionTypes.LoadDataSource,
  64. payload: dataSource,
  65. });
  66. const dataSourceMetaLoaded = (dataSourceMeta: Plugin): LoadDataSourceMetaAction => ({
  67. type: ActionTypes.LoadDataSourceMeta,
  68. payload: dataSourceMeta,
  69. });
  70. const dataSourceTypesLoaded = (dataSourceTypes: Plugin[]): LoadDataSourceTypesAction => ({
  71. type: ActionTypes.LoadDataSourceTypes,
  72. payload: dataSourceTypes,
  73. });
  74. export const setDataSourcesSearchQuery = (searchQuery: string): SetDataSourcesSearchQueryAction => ({
  75. type: ActionTypes.SetDataSourcesSearchQuery,
  76. payload: searchQuery,
  77. });
  78. export const setDataSourcesLayoutMode = (layoutMode: LayoutMode): SetDataSourcesLayoutModeAction => ({
  79. type: ActionTypes.SetDataSourcesLayoutMode,
  80. payload: layoutMode,
  81. });
  82. export const setDataSourceTypeSearchQuery = (query: string): SetDataSourceTypeSearchQueryAction => ({
  83. type: ActionTypes.SetDataSourceTypeSearchQuery,
  84. payload: query,
  85. });
  86. export const setDataSourceName = (name: string) => ({
  87. type: ActionTypes.SetDataSourceName,
  88. payload: name,
  89. });
  90. export const setIsDefault = (state: boolean) => ({
  91. type: ActionTypes.SetIsDefault,
  92. payload: state,
  93. });
  94. export type Action =
  95. | LoadDataSourcesAction
  96. | SetDataSourcesSearchQueryAction
  97. | SetDataSourcesLayoutModeAction
  98. | UpdateLocationAction
  99. | LoadDataSourceTypesAction
  100. | SetDataSourceTypeSearchQueryAction
  101. | LoadDataSourceAction
  102. | UpdateNavIndexAction
  103. | LoadDataSourceMetaAction
  104. | SetDataSourceNameAction
  105. | SetIsDefaultAction;
  106. type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;
  107. export function loadDataSources(): ThunkResult<void> {
  108. return async dispatch => {
  109. const response = await getBackendSrv().get('/api/datasources');
  110. dispatch(dataSourcesLoaded(response));
  111. };
  112. }
  113. export function loadDataSource(id: number): ThunkResult<void> {
  114. return async dispatch => {
  115. const dataSource = await getBackendSrv().get(`/api/datasources/${id}`);
  116. const pluginInfo = await getBackendSrv().get(`/api/plugins/${dataSource.type}/settings`);
  117. dispatch(dataSourceLoaded(dataSource));
  118. dispatch(dataSourceMetaLoaded(pluginInfo));
  119. dispatch(updateNavIndex(buildNavModel(dataSource, pluginInfo)));
  120. };
  121. }
  122. export function addDataSource(plugin: Plugin): ThunkResult<void> {
  123. return async (dispatch, getStore) => {
  124. await dispatch(loadDataSources());
  125. const dataSources = getStore().dataSources.dataSources;
  126. const newInstance = {
  127. name: plugin.name,
  128. type: plugin.id,
  129. access: 'proxy',
  130. isDefault: dataSources.length === 0,
  131. };
  132. if (nameExits(dataSources, newInstance.name)) {
  133. newInstance.name = findNewName(dataSources, newInstance.name);
  134. }
  135. const result = await getBackendSrv().post('/api/datasources', newInstance);
  136. dispatch(updateLocation({ path: `/datasources/edit/${result.id}` }));
  137. };
  138. }
  139. export function loadDataSourceTypes(): ThunkResult<void> {
  140. return async dispatch => {
  141. const result = await getBackendSrv().get('/api/plugins', { enabled: 1, type: 'datasource' });
  142. dispatch(dataSourceTypesLoaded(result));
  143. };
  144. }
  145. export function updateDataSource(dataSource: DataSourceSettings): ThunkResult<void> {
  146. return async dispatch => {
  147. await getBackendSrv().put(`/api/datasources/${dataSource.id}`, dataSource);
  148. await updateFrontendSettings();
  149. return dispatch(loadDataSource(dataSource.id));
  150. };
  151. }
  152. export function deleteDataSource(): ThunkResult<void> {
  153. return async (dispatch, getStore) => {
  154. const dataSource = getStore().dataSources.dataSource;
  155. await getBackendSrv().delete(`/api/datasources/${dataSource.id}`);
  156. dispatch(updateLocation({ path: '/datasources' }));
  157. };
  158. }
  159. export function nameExits(dataSources, name) {
  160. return (
  161. dataSources.filter(dataSource => {
  162. return dataSource.name.toLowerCase() === name.toLowerCase();
  163. }).length > 0
  164. );
  165. }
  166. export function findNewName(dataSources, name) {
  167. // Need to loop through current data sources to make sure
  168. // the name doesn't exist
  169. while (nameExits(dataSources, name)) {
  170. // If there's a duplicate name that doesn't end with '-x'
  171. // we can add -1 to the name and be done.
  172. if (!nameHasSuffix(name)) {
  173. name = `${name}-1`;
  174. } else {
  175. // if there's a duplicate name that ends with '-x'
  176. // we can try to increment the last digit until the name is unique
  177. // remove the 'x' part and replace it with the new number
  178. name = `${getNewName(name)}${incrementLastDigit(getLastDigit(name))}`;
  179. }
  180. }
  181. return name;
  182. }
  183. function updateFrontendSettings() {
  184. return getBackendSrv()
  185. .get('/api/frontend/settings')
  186. .then(settings => {
  187. config.datasources = settings.datasources;
  188. config.defaultDatasource = settings.defaultDatasource;
  189. getDatasourceSrv().init();
  190. });
  191. }
  192. function nameHasSuffix(name) {
  193. return name.endsWith('-', name.length - 1);
  194. }
  195. function getLastDigit(name) {
  196. return parseInt(name.slice(-1), 10);
  197. }
  198. function incrementLastDigit(digit) {
  199. return isNaN(digit) ? 1 : digit + 1;
  200. }
  201. function getNewName(name) {
  202. return name.slice(0, name.length - 1);
  203. }