Commit dbf04709 by Ivana Huckova Committed by GitHub

Chore: Remove unused Loki and Cloudwatch syntax providers (#29686)

* Remove syntax provider in Cloudwatch

* Remove syntax provider and refactor useLokiLabels
parent 854f6229
...@@ -7,8 +7,6 @@ import { InlineFormLabel } from '@grafana/ui'; ...@@ -7,8 +7,6 @@ import { InlineFormLabel } from '@grafana/ui';
import { CloudWatchDatasource } from '../datasource'; import { CloudWatchDatasource } from '../datasource';
import { CloudWatchLogsQuery, CloudWatchQuery } from '../types'; import { CloudWatchLogsQuery, CloudWatchQuery } from '../types';
import { CloudWatchLogsQueryField } from './LogsQueryField'; import { CloudWatchLogsQueryField } from './LogsQueryField';
import { useCloudWatchSyntax } from '../useCloudwatchSyntax';
import { CloudWatchLanguageProvider } from '../language_provider';
import CloudWatchLink from './CloudWatchLink'; import CloudWatchLink from './CloudWatchLink';
import { css } from 'emotion'; import { css } from 'emotion';
...@@ -36,11 +34,6 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor ...@@ -36,11 +34,6 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor
}; };
} }
const { isSyntaxReady, syntax } = useCloudWatchSyntax(
datasource.languageProvider as CloudWatchLanguageProvider,
absolute
);
return ( return (
<CloudWatchLogsQueryField <CloudWatchLogsQueryField
exploreId={exploreId} exploreId={exploreId}
...@@ -52,8 +45,6 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor ...@@ -52,8 +45,6 @@ export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor
history={[]} history={[]}
data={data} data={data}
absoluteRange={absolute} absoluteRange={absolute}
syntaxLoaded={isSyntaxReady}
syntax={syntax}
allowCustomValue={allowCustomValue} allowCustomValue={allowCustomValue}
ExtraFieldElement={ ExtraFieldElement={
<InlineFormLabel className={`gf-form-label--btn ${labelClass}`} width="auto" tooltip="Link to Graph in AWS"> <InlineFormLabel className={`gf-form-label--btn ${labelClass}`} width="auto" tooltip="Link to Graph in AWS">
......
...@@ -17,8 +17,6 @@ describe('CloudWatchLogsQueryField', () => { ...@@ -17,8 +17,6 @@ describe('CloudWatchLogsQueryField', () => {
<CloudWatchLogsQueryField <CloudWatchLogsQueryField
history={[]} history={[]}
absoluteRange={{ from: 1, to: 10 }} absoluteRange={{ from: 1, to: 10 }}
syntaxLoaded={false}
syntax={{} as any}
exploreId={ExploreId.left} exploreId={ExploreId.left}
datasource={ datasource={
{ {
...@@ -153,8 +151,6 @@ describe('CloudWatchLogsQueryField', () => { ...@@ -153,8 +151,6 @@ describe('CloudWatchLogsQueryField', () => {
<CloudWatchLogsQueryField <CloudWatchLogsQueryField
history={[]} history={[]}
absoluteRange={{ from: 1, to: 10 }} absoluteRange={{ from: 1, to: 10 }}
syntaxLoaded={false}
syntax={{} as any}
exploreId={ExploreId.left} exploreId={ExploreId.left}
datasource={ datasource={
{ {
......
...@@ -24,7 +24,7 @@ import syntax from '../syntax'; ...@@ -24,7 +24,7 @@ import syntax from '../syntax';
import { ExploreQueryFieldProps, AbsoluteTimeRange, SelectableValue, AppEvents } from '@grafana/data'; import { ExploreQueryFieldProps, AbsoluteTimeRange, SelectableValue, AppEvents } from '@grafana/data';
import { CloudWatchQuery, CloudWatchLogsQuery } from '../types'; import { CloudWatchQuery, CloudWatchLogsQuery } from '../types';
import { CloudWatchDatasource } from '../datasource'; import { CloudWatchDatasource } from '../datasource';
import { Grammar, LanguageMap, languages as prismLanguages } from 'prismjs'; import { LanguageMap, languages as prismLanguages } from 'prismjs';
import { CloudWatchLanguageProvider } from '../language_provider'; import { CloudWatchLanguageProvider } from '../language_provider';
import { css } from 'emotion'; import { css } from 'emotion';
import { ExploreId } from 'app/types'; import { ExploreId } from 'app/types';
...@@ -36,8 +36,6 @@ export interface CloudWatchLogsQueryFieldProps extends ExploreQueryFieldProps<Cl ...@@ -36,8 +36,6 @@ export interface CloudWatchLogsQueryFieldProps extends ExploreQueryFieldProps<Cl
absoluteRange: AbsoluteTimeRange; absoluteRange: AbsoluteTimeRange;
onLabelsRefresh?: () => void; onLabelsRefresh?: () => void;
ExtraFieldElement?: ReactNode; ExtraFieldElement?: ReactNode;
syntaxLoaded: boolean;
syntax: Grammar | null;
exploreId: ExploreId; exploreId: ExploreId;
allowCustomValue?: boolean; allowCustomValue?: boolean;
} }
...@@ -294,7 +292,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs ...@@ -294,7 +292,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
}; };
render() { render() {
const { ExtraFieldElement, data, query, syntaxLoaded, datasource, allowCustomValue } = this.props; const { ExtraFieldElement, data, query, datasource, allowCustomValue } = this.props;
const { const {
selectedLogGroups, selectedLogGroups,
availableLogGroups, availableLogGroups,
...@@ -375,7 +373,6 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs ...@@ -375,7 +373,6 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
cleanText={cleanText} cleanText={cleanText}
placeholder="Enter a CloudWatch Logs Insights query (run with Shift+Enter)" placeholder="Enter a CloudWatch Logs Insights query (run with Shift+Enter)"
portalOrigin="cloudwatch" portalOrigin="cloudwatch"
syntaxLoaded={syntaxLoaded}
disabled={loadingLogGroups || selectedLogGroups.length === 0} disabled={loadingLogGroups || selectedLogGroups.length === 0}
/> />
</div> </div>
......
import { useState, useEffect } from 'react';
import { Grammar } from 'prismjs';
import { AbsoluteTimeRange } from '@grafana/data';
import { useRefMounted } from 'app/core/hooks/useRefMounted';
import { CloudWatchLanguageProvider } from './language_provider';
/**
* Initialise the language provider. Returns a languageProviderInitialized boolean cause there does not seem other way
* to know if the provider is already initialised or not. By the initialisation it modifies the provided
* languageProvider directly.
*/
const useInitLanguageProvider = (languageProvider: CloudWatchLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
const mounted = useRefMounted();
const [languageProviderInitialized, setLanguageProviderInitialized] = useState(false);
// Async
const initializeLanguageProvider = async () => {
languageProvider.initialRange = absoluteRange;
await languageProvider.start();
if (mounted.current) {
setLanguageProviderInitialized(true);
}
};
useEffect(() => {
initializeLanguageProvider();
}, []);
return languageProviderInitialized;
};
/**
* Returns syntax from languageProvider and initialises global Prism syntax. Waits until languageProvider itself is
* initialised (outside of this hook).
*/
const useCloudwatchSyntax = (languageProvider: CloudWatchLanguageProvider, languageProviderInitialized: boolean) => {
// State
const [syntax, setSyntax] = useState<Grammar | null>(null);
// Effects
useEffect(() => {
if (languageProviderInitialized) {
const syntax = languageProvider.getSyntax();
setSyntax(syntax);
}
}, [languageProviderInitialized, languageProvider]);
return {
isSyntaxReady: !!syntax,
syntax,
};
};
/**
* Initializes given language provider, exposes Loki syntax and enables loading label option values
*/
export const useCloudWatchSyntax = (languageProvider: CloudWatchLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
const languageProviderInitialized = useInitLanguageProvider(languageProvider, absoluteRange);
const { isSyntaxReady, syntax } = useCloudwatchSyntax(languageProvider, languageProviderInitialized);
return {
isSyntaxReady,
syntax,
};
};
...@@ -3,7 +3,7 @@ import React, { memo } from 'react'; ...@@ -3,7 +3,7 @@ import React, { memo } from 'react';
// Types // Types
import { LokiQuery } from '../types'; import { LokiQuery } from '../types';
import { useLokiSyntaxAndLabels } from './useLokiSyntaxAndLabels'; import { useLokiLabels } from './useLokiLabels';
import { LokiQueryFieldForm } from './LokiQueryFieldForm'; import { LokiQueryFieldForm } from './LokiQueryFieldForm';
import LokiDatasource from '../datasource'; import LokiDatasource from '../datasource';
...@@ -22,7 +22,7 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito ...@@ -22,7 +22,7 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
to: Date.now(), to: Date.now(),
}; };
const { isSyntaxReady, setActiveOption, refreshLabels, syntax, logLabelOptions } = useLokiSyntaxAndLabels( const { setActiveOption, refreshLabels, logLabelOptions, labelsLoaded } = useLokiLabels(
datasource.languageProvider, datasource.languageProvider,
absolute absolute
); );
...@@ -43,8 +43,7 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito ...@@ -43,8 +43,7 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
onLoadOptions={setActiveOption} onLoadOptions={setActiveOption}
onLabelsRefresh={refreshLabels} onLabelsRefresh={refreshLabels}
absoluteRange={absolute} absoluteRange={absolute}
syntax={syntax} labelsLoaded={labelsLoaded}
syntaxLoaded={isSyntaxReady}
logLabelOptions={logLabelOptions} logLabelOptions={logLabelOptions}
/> />
</div> </div>
......
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent } from 'react';
import { LokiQueryFieldForm, LokiQueryFieldFormProps } from './LokiQueryFieldForm'; import { LokiQueryFieldForm, LokiQueryFieldFormProps } from './LokiQueryFieldForm';
import { useLokiSyntaxAndLabels } from './useLokiSyntaxAndLabels'; import { useLokiLabels } from './useLokiLabels';
import LokiLanguageProvider from '../language_provider'; import LokiLanguageProvider from '../language_provider';
type LokiQueryFieldProps = Omit< type LokiQueryFieldProps = Omit<
LokiQueryFieldFormProps, LokiQueryFieldFormProps,
'syntax' | 'syntaxLoaded' | 'onLoadOptions' | 'onLabelsRefresh' | 'logLabelOptions' | 'absoluteRange' 'labelsLoaded' | 'onLoadOptions' | 'onLabelsRefresh' | 'logLabelOptions' | 'absoluteRange'
>; >;
export const LokiQueryField: FunctionComponent<LokiQueryFieldProps> = props => { export const LokiQueryField: FunctionComponent<LokiQueryFieldProps> = props => {
const { datasource, range, ...otherProps } = props; const { datasource, range, ...otherProps } = props;
const absoluteTimeRange = { from: range!.from!.valueOf(), to: range!.to!.valueOf() }; // Range here is never optional const absoluteTimeRange = { from: range!.from!.valueOf(), to: range!.to!.valueOf() }; // Range here is never optional
const { isSyntaxReady, setActiveOption, refreshLabels, syntax, logLabelOptions } = useLokiSyntaxAndLabels( const { setActiveOption, refreshLabels, logLabelOptions, labelsLoaded } = useLokiLabels(
datasource.languageProvider as LokiLanguageProvider, datasource.languageProvider as LokiLanguageProvider,
absoluteTimeRange absoluteTimeRange
); );
...@@ -29,8 +29,7 @@ export const LokiQueryField: FunctionComponent<LokiQueryFieldProps> = props => { ...@@ -29,8 +29,7 @@ export const LokiQueryField: FunctionComponent<LokiQueryFieldProps> = props => {
onLoadOptions={setActiveOption} onLoadOptions={setActiveOption}
onLabelsRefresh={refreshLabels} onLabelsRefresh={refreshLabels}
absoluteRange={absoluteTimeRange} absoluteRange={absoluteTimeRange}
syntax={syntax} labelsLoaded={labelsLoaded}
syntaxLoaded={isSyntaxReady}
logLabelOptions={logLabelOptions} logLabelOptions={logLabelOptions}
{...otherProps} {...otherProps}
/> />
......
...@@ -20,7 +20,7 @@ import { Plugin, Node } from 'slate'; ...@@ -20,7 +20,7 @@ import { Plugin, Node } from 'slate';
import { DOMUtil } from '@grafana/ui'; import { DOMUtil } from '@grafana/ui';
import { ExploreQueryFieldProps, AbsoluteTimeRange } from '@grafana/data'; import { ExploreQueryFieldProps, AbsoluteTimeRange } from '@grafana/data';
import { LokiQuery, LokiOptions } from '../types'; import { LokiQuery, LokiOptions } from '../types';
import { Grammar, LanguageMap, languages as prismLanguages } from 'prismjs'; import { LanguageMap, languages as prismLanguages } from 'prismjs';
import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider'; import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider';
import LokiDatasource from '../datasource'; import LokiDatasource from '../datasource';
import LokiOptionFields from './LokiOptionFields'; import LokiOptionFields from './LokiOptionFields';
...@@ -64,9 +64,8 @@ function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadTe ...@@ -64,9 +64,8 @@ function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadTe
export interface LokiQueryFieldFormProps extends ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions> { export interface LokiQueryFieldFormProps extends ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions> {
history: LokiHistoryItem[]; history: LokiHistoryItem[];
syntax: Grammar | null;
logLabelOptions: CascaderOption[]; logLabelOptions: CascaderOption[];
syntaxLoaded: boolean; labelsLoaded: boolean;
absoluteRange: AbsoluteTimeRange; absoluteRange: AbsoluteTimeRange;
onLoadOptions: (selectedOptions: CascaderOption[]) => void; onLoadOptions: (selectedOptions: CascaderOption[]) => void;
onLabelsRefresh?: () => void; onLabelsRefresh?: () => void;
...@@ -140,18 +139,19 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr ...@@ -140,18 +139,19 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
const { const {
ExtraFieldElement, ExtraFieldElement,
query, query,
syntaxLoaded, labelsLoaded,
logLabelOptions, logLabelOptions,
onLoadOptions, onLoadOptions,
onLabelsRefresh, onLabelsRefresh,
datasource, datasource,
runOnBlur, runOnBlur,
} = this.props; } = this.props;
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider; const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined; const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined;
const hasLogLabels = logLabelOptions && logLabelOptions.length > 0; const hasLogLabels = logLabelOptions && logLabelOptions.length > 0;
const chooserText = getChooserText(syntaxLoaded, hasLogLabels); const chooserText = getChooserText(labelsLoaded, hasLogLabels);
const buttonDisabled = !(syntaxLoaded && hasLogLabels); const buttonDisabled = !(labelsLoaded && hasLogLabels);
return ( return (
<> <>
...@@ -179,7 +179,6 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr ...@@ -179,7 +179,6 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
onRunQuery={this.props.onRunQuery} onRunQuery={this.props.onRunQuery}
placeholder="Enter a Loki query (run with Shift+Enter)" placeholder="Enter a Loki query (run with Shift+Enter)"
portalOrigin="loki" portalOrigin="loki"
syntaxLoaded={syntaxLoaded}
/> />
</div> </div>
</div> </div>
......
import { renderHook, act } from '@testing-library/react-hooks'; import { renderHook, act } from '@testing-library/react-hooks';
import LanguageProvider from 'app/plugins/datasource/loki/language_provider'; import LanguageProvider from 'app/plugins/datasource/loki/language_provider';
import { useLokiLabels } from './useLokiLabels'; import { getLokiLabels, useLokiLabels } from './useLokiLabels';
import { AbsoluteTimeRange } from '@grafana/data'; import { AbsoluteTimeRange } from '@grafana/data';
import { makeMockLokiDatasource } from '../mocks'; import { makeMockLokiDatasource } from '../mocks';
import { CascaderOption } from '@grafana/ui';
describe('useLokiLabels hook', () => { // Mocks
it('should refresh labels', async () => { const datasource = makeMockLokiDatasource({});
const datasource = makeMockLokiDatasource({}); const languageProvider = new LanguageProvider(datasource);
const languageProvider = new LanguageProvider(datasource);
const logLabelOptionsMock = ['Holy mock!']; const logLabelOptionsMock = ['Holy mock!'];
const rangeMock: AbsoluteTimeRange = { const logLabelOptionsMock2 = ['Mock the hell?!'];
from: 1560153109000, const logLabelOptionsMock3 = ['Oh my mock!'];
to: 1560153109000,
}; const rangeMock: AbsoluteTimeRange = {
from: 1560153109000,
to: 1560153109000,
};
describe('getLokiLabels hook', () => {
it('should refresh labels', async () => {
languageProvider.logLabelOptions = ['initial']; languageProvider.logLabelOptions = ['initial'];
languageProvider.refreshLogLabels = () => { languageProvider.refreshLogLabels = () => {
...@@ -21,10 +27,64 @@ describe('useLokiLabels hook', () => { ...@@ -21,10 +27,64 @@ describe('useLokiLabels hook', () => {
return Promise.resolve(); return Promise.resolve();
}; };
const { result, waitForNextUpdate } = renderHook(() => useLokiLabels(languageProvider, true, rangeMock)); const { result, waitForNextUpdate } = renderHook(() => getLokiLabels(languageProvider, true, rangeMock));
expect(result.current.logLabelOptions).toEqual(['initial']); expect(result.current.logLabelOptions).toEqual(['initial']);
act(() => result.current.refreshLabels()); act(() => result.current.refreshLabels());
await waitForNextUpdate(); await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock); expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock);
}); });
}); });
describe('useLokiLabels hook', () => {
languageProvider.refreshLogLabels = () => {
languageProvider.logLabelOptions = logLabelOptionsMock;
return Promise.resolve();
};
languageProvider.fetchLogLabels = () => {
languageProvider.logLabelOptions = logLabelOptionsMock2;
return Promise.resolve([]);
};
const activeOptionMock: CascaderOption = {
label: '',
value: '',
};
it('should fetch labels on first call', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLokiLabels(languageProvider, rangeMock));
expect(result.current.logLabelOptions).toEqual([]);
await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock2);
});
it('should try to fetch missing options when active option changes', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLokiLabels(languageProvider, rangeMock));
await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock2);
languageProvider.fetchLabelValues = (key: string, absoluteRange: AbsoluteTimeRange) => {
languageProvider.logLabelOptions = logLabelOptionsMock3;
return Promise.resolve([]);
};
act(() => result.current.setActiveOption([activeOptionMock]));
await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock3);
});
it('should refresh labels', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLokiLabels(languageProvider, rangeMock));
expect(result.current.logLabelOptions).toEqual([]);
act(() => result.current.refreshLabels());
await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock);
});
});
...@@ -7,22 +7,49 @@ import LokiLanguageProvider from 'app/plugins/datasource/loki/language_provider' ...@@ -7,22 +7,49 @@ import LokiLanguageProvider from 'app/plugins/datasource/loki/language_provider'
import { useRefMounted } from 'app/core/hooks/useRefMounted'; import { useRefMounted } from 'app/core/hooks/useRefMounted';
/** /**
* Initialise the language provider. Returns a languageProviderInitialized boolean cause there does not seem other way
* to know if the provider is already initialised or not. By the initialisation it modifies the provided
* languageProvider directly.
*/
const useInitLanguageProvider = (languageProvider: LokiLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
const mounted = useRefMounted();
const [languageProviderInitialized, setLanguageProviderInitialized] = useState(false);
// Async
const initializeLanguageProvider = async () => {
languageProvider.initialRange = absoluteRange;
await languageProvider.start();
if (mounted.current) {
setLanguageProviderInitialized(true);
}
};
useEffect(() => {
initializeLanguageProvider();
}, []);
return languageProviderInitialized;
};
/**
* *
* @param languageProvider * @param languageProvider
* @param languageProviderInitialised * @param languageProviderInitialized
* @param absoluteRange * @param absoluteRange
* *
* @description Fetches missing labels and enables labels refresh * @description Fetches missing labels and enables labels refresh
*/ */
export const useLokiLabels = ( export const getLokiLabels = (
languageProvider: LokiLanguageProvider, languageProvider: LokiLanguageProvider,
languageProviderInitialised: boolean, languageProviderInitialized: boolean,
absoluteRange: AbsoluteTimeRange absoluteRange: AbsoluteTimeRange
) => { ) => {
const mounted = useRefMounted(); const mounted = useRefMounted();
// State // State
const [logLabelOptions, setLogLabelOptions] = useState<any>([]); const [logLabelOptions, setLogLabelOptions] = useState<any>([]);
const [labelsLoaded, setLabelsLoaded] = useState(false);
const [shouldTryRefreshLabels, setRefreshLabels] = useState(false); const [shouldTryRefreshLabels, setRefreshLabels] = useState(false);
const [prevAbsoluteRange, setPrevAbsoluteRange] = useState<AbsoluteTimeRange | null>(null); const [prevAbsoluteRange, setPrevAbsoluteRange] = useState<AbsoluteTimeRange | null>(null);
/** /**
...@@ -37,6 +64,7 @@ export const useLokiLabels = ( ...@@ -37,6 +64,7 @@ export const useLokiLabels = (
await languageProvider.fetchLabelValues(option, absoluteRange); await languageProvider.fetchLabelValues(option, absoluteRange);
if (mounted.current) { if (mounted.current) {
setLogLabelOptions(languageProvider.logLabelOptions); setLogLabelOptions(languageProvider.logLabelOptions);
setLabelsLoaded(true);
} }
}; };
...@@ -56,7 +84,7 @@ export const useLokiLabels = ( ...@@ -56,7 +84,7 @@ export const useLokiLabels = (
// It's a subject of activeOption state change only. This is because of specific behavior or rc-cascader // It's a subject of activeOption state change only. This is because of specific behavior or rc-cascader
// https://github.com/react-component/cascader/blob/master/src/Cascader.jsx#L165 // https://github.com/react-component/cascader/blob/master/src/Cascader.jsx#L165
useEffect(() => { useEffect(() => {
if (languageProviderInitialised) { if (languageProviderInitialized) {
const targetOption = activeOption[activeOption.length - 1]; const targetOption = activeOption[activeOption.length - 1];
if (targetOption) { if (targetOption) {
const nextOptions = logLabelOptions.map((option: any) => { const nextOptions = logLabelOptions.map((option: any) => {
...@@ -85,14 +113,35 @@ export const useLokiLabels = ( ...@@ -85,14 +113,35 @@ export const useLokiLabels = (
// Initialize labels from the provider after it gets initialized (it's initialisation happens outside of this hook) // Initialize labels from the provider after it gets initialized (it's initialisation happens outside of this hook)
useEffect(() => { useEffect(() => {
if (languageProviderInitialised) { if (languageProviderInitialized) {
setLogLabelOptions(languageProvider.logLabelOptions); setLogLabelOptions(languageProvider.logLabelOptions);
setLabelsLoaded(true);
} }
}, [languageProviderInitialised]); }, [languageProviderInitialized]);
return { return {
logLabelOptions, logLabelOptions,
refreshLabels: () => setRefreshLabels(true), refreshLabels: () => setRefreshLabels(true),
setActiveOption, setActiveOption,
labelsLoaded,
};
};
/**
* Initializes given language provider and enables loading label option values
*/
export const useLokiLabels = (languageProvider: LokiLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
const languageProviderInitialized = useInitLanguageProvider(languageProvider, absoluteRange);
const { logLabelOptions, refreshLabels, setActiveOption, labelsLoaded } = getLokiLabels(
languageProvider,
languageProviderInitialized,
absoluteRange
);
return {
logLabelOptions,
refreshLabels,
setActiveOption,
labelsLoaded,
}; };
}; };
import { renderHook, act } from '@testing-library/react-hooks';
import { AbsoluteTimeRange } from '@grafana/data';
import { CascaderOption } from '@grafana/ui';
import LanguageProvider from 'app/plugins/datasource/loki/language_provider';
import { useLokiSyntaxAndLabels } from './useLokiSyntaxAndLabels';
import { makeMockLokiDatasource } from '../mocks';
describe('useLokiSyntax hook', () => {
const datasource = makeMockLokiDatasource({});
const languageProvider = new LanguageProvider(datasource);
const logLabelOptionsMock = ['Holy mock!'];
const logLabelOptionsMock2 = ['Mock the hell?!'];
const logLabelOptionsMock3 = ['Oh my mock!'];
const rangeMock: AbsoluteTimeRange = {
from: 1560153109000,
to: 1560163909000,
};
languageProvider.refreshLogLabels = () => {
languageProvider.logLabelOptions = logLabelOptionsMock;
return Promise.resolve();
};
languageProvider.fetchLogLabels = () => {
languageProvider.logLabelOptions = logLabelOptionsMock2;
return Promise.resolve([]);
};
const activeOptionMock: CascaderOption = {
label: '',
value: '',
};
it('should provide Loki syntax when used', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLokiSyntaxAndLabels(languageProvider, rangeMock));
expect(result.current.syntax).toEqual(null);
await waitForNextUpdate();
expect(result.current.syntax).toEqual(languageProvider.getSyntax());
});
it('should fetch labels on first call', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLokiSyntaxAndLabels(languageProvider, rangeMock));
expect(result.current.isSyntaxReady).toBeFalsy();
expect(result.current.logLabelOptions).toEqual([]);
await waitForNextUpdate();
expect(result.current.isSyntaxReady).toBeTruthy();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock2);
});
it('should try to fetch missing options when active option changes', async () => {
const { result, waitForNextUpdate } = renderHook(() => useLokiSyntaxAndLabels(languageProvider, rangeMock));
await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock2);
languageProvider.fetchLabelValues = (key: string, absoluteRange: AbsoluteTimeRange) => {
languageProvider.logLabelOptions = logLabelOptionsMock3;
return Promise.resolve([]);
};
act(() => result.current.setActiveOption([activeOptionMock]));
await waitForNextUpdate();
expect(result.current.logLabelOptions).toEqual(logLabelOptionsMock3);
});
});
import { useState, useEffect } from 'react';
import { Grammar } from 'prismjs';
import { AbsoluteTimeRange } from '@grafana/data';
import LokiLanguageProvider from 'app/plugins/datasource/loki/language_provider';
import { useLokiLabels } from 'app/plugins/datasource/loki/components/useLokiLabels';
import { useRefMounted } from 'app/core/hooks/useRefMounted';
/**
* Initialise the language provider. Returns a languageProviderInitialized boolean cause there does not seem other way
* to know if the provider is already initialised or not. By the initialisation it modifies the provided
* languageProvider directly.
*/
const useInitLanguageProvider = (languageProvider: LokiLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
const mounted = useRefMounted();
const [languageProviderInitialized, setLanguageProviderInitialized] = useState(false);
// Async
const initializeLanguageProvider = async () => {
languageProvider.initialRange = absoluteRange;
await languageProvider.start();
if (mounted.current) {
setLanguageProviderInitialized(true);
}
};
useEffect(() => {
initializeLanguageProvider();
}, []);
return languageProviderInitialized;
};
/**
* Returns syntax from languageProvider and initialises global Prism syntax. Waits until languageProvider itself is
* initialised (outside of this hook).
*/
const useLokiSyntax = (languageProvider: LokiLanguageProvider, languageProviderInitialized: boolean) => {
// State
const [syntax, setSyntax] = useState<Grammar | null>(null);
// Effects
useEffect(() => {
if (languageProviderInitialized) {
const syntax = languageProvider.getSyntax();
setSyntax(syntax);
}
}, [languageProviderInitialized, languageProvider]);
return {
isSyntaxReady: !!syntax,
syntax,
};
};
/**
* Initializes given language provider, exposes Loki syntax and enables loading label option values
*/
export const useLokiSyntaxAndLabels = (languageProvider: LokiLanguageProvider, absoluteRange: AbsoluteTimeRange) => {
const languageProviderInitialized = useInitLanguageProvider(languageProvider, absoluteRange);
const { logLabelOptions, refreshLabels, setActiveOption } = useLokiLabels(
languageProvider,
languageProviderInitialized,
absoluteRange
);
const { isSyntaxReady, syntax } = useLokiSyntax(languageProvider, languageProviderInitialized);
return {
isSyntaxReady,
syntax,
logLabelOptions,
setActiveOption,
refreshLabels,
};
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment