Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] Fix swim lane time selection with a single time point and the Watcher URL #89125

Merged
merged 8 commits into from
Jan 26, 2021
13 changes: 12 additions & 1 deletion x-pack/plugins/ml/common/types/ml_url_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,18 @@ export interface ExplorerAppState {
mlExplorerSwimlane: {
selectedType?: 'overall' | 'viewBy';
selectedLanes?: string[];
selectedTimes?: [number, number];
/**
* @deprecated legacy query param variable, use `selectedLanes`
*/
selectedLane?: string[] | string;
/**
* It's possible to have only "from" time boundaries, e.g. in the Watcher URL
*/
selectedTimes?: [number, number] | number;
darnautov marked this conversation as resolved.
Show resolved Hide resolved
/**
* @deprecated legacy query param variable, use `selectedTimes`
*/
selectedTime?: [number, number] | number;
showTopFieldValues?: boolean;
viewByFieldName?: string;
viewByPerPage?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,38 @@ describe('useSelectedCells', () => {
},
});
});

test('should extend single time point selection with a bucket interval value', () => {
(useTimefilter() as jest.Mocked<TimefilterContract>).getBounds.mockReturnValue({
min: moment(1498824778 * 1000),
max: moment(1502366798 * 1000),
});

const urlState = {
mlExplorerSwimlane: {
selectedType: 'overall',
selectedLanes: ['Overall'],
selectedTimes: 1498780800,
showTopFieldValues: true,
viewByFieldName: 'apache2.access.remote_ip',
viewByFromPage: 1,
viewByPerPage: 10,
},
mlExplorerFilter: {},
} as ExplorerAppState;

const setUrlState = jest.fn();

const bucketInterval = 86400;

const { result } = renderHook(() => useSelectedCells(urlState, setUrlState, bucketInterval));

expect(result.current[0]).toEqual({
lanes: ['Overall'],
showTopFieldValues: true,
times: [1498780800, 1498867200],
type: 'overall',
viewByFieldName: 'apache2.access.remote_ip',
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,33 @@ export const useSelectedCells = (
const timeBounds = timeFilter.getBounds();

// keep swimlane selection, restore selectedCells from AppState
const selectedCells = useMemo(() => {
return appState?.mlExplorerSwimlane?.selectedType !== undefined
? {
type: appState.mlExplorerSwimlane.selectedType,
lanes: appState.mlExplorerSwimlane.selectedLanes!,
times: appState.mlExplorerSwimlane.selectedTimes!,
showTopFieldValues: appState.mlExplorerSwimlane.showTopFieldValues,
viewByFieldName: appState.mlExplorerSwimlane.viewByFieldName,
}
: undefined;
const selectedCells: AppStateSelectedCells | undefined = useMemo(() => {
if (!appState?.mlExplorerSwimlane?.selectedType) {
return;
}

let times =
appState.mlExplorerSwimlane.selectedTimes ?? appState.mlExplorerSwimlane.selectedTime!;
if (typeof times === 'number' && bucketIntervalInSeconds) {
times = [times, times + bucketIntervalInSeconds];
}

let lanes =
appState.mlExplorerSwimlane.selectedLanes ?? appState.mlExplorerSwimlane.selectedLane!;

if (typeof lanes === 'string') {
lanes = [lanes];
}

return {
type: appState.mlExplorerSwimlane.selectedType,
lanes,
times,
showTopFieldValues: appState.mlExplorerSwimlane.showTopFieldValues,
viewByFieldName: appState.mlExplorerSwimlane.viewByFieldName,
} as AppStateSelectedCells;
// TODO fix appState to use memoization
}, [JSON.stringify(appState?.mlExplorerSwimlane)]);
}, [JSON.stringify(appState?.mlExplorerSwimlane), bucketIntervalInSeconds]);

const setSelectedCells = useCallback(
(swimlaneSelectedCells?: AppStateSelectedCells) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<br />
<br />

<a href="<%= serverAddress %>#/explorer/?_g=(ml:(jobIds:!('{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0._source.job_id}}')),time:(from:'{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0.fields.start.0}}',mode:absolute,to:'{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0.fields.end.0}}'))&_a=(explorer:(mlExplorerSwimlane:(selectedLane:Overall,selectedTime:{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0.fields.timestamp_epoch.0}},selectedType:overall)),query:(query_string:(analyze_wildcard:!t,query:'*')))">
<a href="<%= serverAddress %>#/explorer/?_g=(ml:(jobIds:!('{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0._source.job_id}}')),time:(from:'{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0.fields.start.0}}',mode:absolute,to:'{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0.fields.end.0}}'))&_a=(explorer:(mlExplorerSwimlane:(selectedLanes:!(Overall),selectedTimes:{{ctx.payload.aggregations.bucket_results.top_bucket_hits.hits.hits.0.fields.timestamp_epoch.0}},selectedType:overall)),query:(query_string:(analyze_wildcard:!t,query:'*')))">
<%= openInAnomalyExplorerLinkText %>
</a>
<br />
Expand Down