useLokiLabels.ts 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { useState, useEffect } from 'react';
  2. import { DataSourceStatus } from '@grafana/ui/src/types/datasource';
  3. import LokiLanguageProvider from 'app/plugins/datasource/loki/language_provider';
  4. import { CascaderOption } from 'app/plugins/datasource/loki/components/LokiQueryFieldForm';
  5. import { useRefMounted } from 'app/core/hooks/useRefMounted';
  6. /**
  7. *
  8. * @param languageProvider
  9. * @param languageProviderInitialised
  10. * @param activeOption rc-cascader provided option used to fetch option's values that hasn't been loaded yet
  11. *
  12. * @description Fetches missing labels and enables labels refresh
  13. */
  14. export const useLokiLabels = (
  15. languageProvider: LokiLanguageProvider,
  16. languageProviderInitialised: boolean,
  17. activeOption: CascaderOption[],
  18. datasourceStatus: DataSourceStatus,
  19. initialDatasourceStatus?: DataSourceStatus // used for test purposes
  20. ) => {
  21. const mounted = useRefMounted();
  22. // State
  23. const [logLabelOptions, setLogLabelOptions] = useState([]);
  24. const [shouldTryRefreshLabels, setRefreshLabels] = useState(false);
  25. const [prevDatasourceStatus, setPrevDatasourceStatus] = useState(
  26. initialDatasourceStatus || DataSourceStatus.Connected
  27. );
  28. const [shouldForceRefreshLabels, setForceRefreshLabels] = useState(false);
  29. // Async
  30. const fetchOptionValues = async (option: string) => {
  31. await languageProvider.fetchLabelValues(option);
  32. if (mounted.current) {
  33. setLogLabelOptions(languageProvider.logLabelOptions);
  34. }
  35. };
  36. const tryLabelsRefresh = async () => {
  37. await languageProvider.refreshLogLabels(shouldForceRefreshLabels);
  38. if (mounted.current) {
  39. setRefreshLabels(false);
  40. setForceRefreshLabels(false);
  41. setLogLabelOptions(languageProvider.logLabelOptions);
  42. }
  43. };
  44. // Effects
  45. // This effect performs loading of options that hasn't been loaded yet
  46. // It's a subject of activeOption state change only. This is because of specific behavior or rc-cascader
  47. // https://github.com/react-component/cascader/blob/master/src/Cascader.jsx#L165
  48. useEffect(() => {
  49. if (languageProviderInitialised) {
  50. const targetOption = activeOption[activeOption.length - 1];
  51. if (targetOption) {
  52. const nextOptions = logLabelOptions.map(option => {
  53. if (option.value === targetOption.value) {
  54. return {
  55. ...option,
  56. loading: true,
  57. };
  58. }
  59. return option;
  60. });
  61. setLogLabelOptions(nextOptions); // to set loading
  62. fetchOptionValues(targetOption.value);
  63. }
  64. }
  65. }, [activeOption]);
  66. // This effect is performed on shouldTryRefreshLabels or shouldForceRefreshLabels state change only.
  67. // Since shouldTryRefreshLabels is reset AFTER the labels are refreshed we are secured in case of trying to refresh
  68. // when previous refresh hasn't finished yet
  69. useEffect(() => {
  70. if (shouldTryRefreshLabels || shouldForceRefreshLabels) {
  71. tryLabelsRefresh();
  72. }
  73. }, [shouldTryRefreshLabels, shouldForceRefreshLabels]);
  74. // This effect is performed on datasourceStatus state change only.
  75. // We want to make sure to only force refresh AFTER a disconnected state thats why we store the previous datasourceStatus in state
  76. useEffect(() => {
  77. if (datasourceStatus === DataSourceStatus.Connected && prevDatasourceStatus === DataSourceStatus.Disconnected) {
  78. setForceRefreshLabels(true);
  79. }
  80. setPrevDatasourceStatus(datasourceStatus);
  81. }, [datasourceStatus]);
  82. return {
  83. logLabelOptions,
  84. setLogLabelOptions,
  85. refreshLabels: () => setRefreshLabels(true),
  86. };
  87. };