datasource.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // Libraries
  2. import { AnyAction, createAction } from '@reduxjs/toolkit';
  3. import { DataSourceApi, HistoryItem } from '@grafana/data';
  4. import { RefreshPicker } from '@grafana/ui';
  5. import { stopQueryState } from 'app/core/utils/explore';
  6. import { ExploreItemState, ThunkResult } from 'app/types';
  7. import { ExploreId } from 'app/types/explore';
  8. import { importQueries, runQueries } from './query';
  9. import { changeRefreshInterval } from './time';
  10. import { createEmptyQueryResponse, loadAndInitDatasource } from './utils';
  11. //
  12. // Actions and Payloads
  13. //
  14. /**
  15. * Updates datasource instance before datasource loading has started
  16. */
  17. export interface UpdateDatasourceInstancePayload {
  18. exploreId: ExploreId;
  19. datasourceInstance: DataSourceApi;
  20. history: HistoryItem[];
  21. }
  22. export const updateDatasourceInstanceAction = createAction<UpdateDatasourceInstancePayload>(
  23. 'explore/updateDatasourceInstance'
  24. );
  25. //
  26. // Action creators
  27. //
  28. /**
  29. * Loads a new datasource identified by the given name.
  30. */
  31. export function changeDatasource(
  32. exploreId: ExploreId,
  33. datasourceUid: string,
  34. options?: { importQueries: boolean }
  35. ): ThunkResult<void> {
  36. return async (dispatch, getState) => {
  37. const orgId = getState().user.orgId;
  38. const { history, instance } = await loadAndInitDatasource(orgId, datasourceUid);
  39. const currentDataSourceInstance = getState().explore[exploreId]!.datasourceInstance;
  40. dispatch(
  41. updateDatasourceInstanceAction({
  42. exploreId,
  43. datasourceInstance: instance,
  44. history,
  45. })
  46. );
  47. if (options?.importQueries) {
  48. const queries = getState().explore[exploreId]!.queries;
  49. await dispatch(importQueries(exploreId, queries, currentDataSourceInstance, instance));
  50. }
  51. if (getState().explore[exploreId]!.isLive) {
  52. dispatch(changeRefreshInterval(exploreId, RefreshPicker.offOption.value));
  53. }
  54. // Exception - we only want to run queries on data source change, if the queries were imported
  55. if (options?.importQueries) {
  56. dispatch(runQueries(exploreId));
  57. }
  58. };
  59. }
  60. //
  61. // Reducer
  62. //
  63. /**
  64. * Reducer for an Explore area, to be used by the global Explore reducer.
  65. */
  66. // Redux Toolkit uses ImmerJs as part of their solution to ensure that state objects are not mutated.
  67. // ImmerJs has an autoFreeze option that freezes objects from change which means this reducer can't be migrated to createSlice
  68. // because the state would become frozen and during run time we would get errors because flot (Graph lib) would try to mutate
  69. // the frozen state.
  70. // https://github.com/reduxjs/redux-toolkit/issues/242
  71. export const datasourceReducer = (state: ExploreItemState, action: AnyAction): ExploreItemState => {
  72. if (updateDatasourceInstanceAction.match(action)) {
  73. const { datasourceInstance, history } = action.payload;
  74. // Custom components
  75. stopQueryState(state.querySubscription);
  76. return {
  77. ...state,
  78. datasourceInstance,
  79. graphResult: null,
  80. tableResult: null,
  81. logsResult: null,
  82. logsVolumeDataProvider: undefined,
  83. logsVolumeData: undefined,
  84. queryResponse: createEmptyQueryResponse(),
  85. loading: false,
  86. queryKeys: [],
  87. history,
  88. datasourceMissing: false,
  89. };
  90. }
  91. return state;
  92. };