actions.ts 7.1 KB

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