actions.ts 6.8 KB

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