Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion x-pack/plugins/ml/common/types/es_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
* 2.0.
*/

import { SearchResponse, ShardsResponse } from 'elasticsearch';
import type { SearchResponse, ShardsResponse } from 'elasticsearch';
import { buildEsQuery } from '../../../../../src/plugins/data/common/es_query/es_query';
import type { DslQuery } from '../../../../../src/plugins/data/common/es_query/kuery';
import type { JsonObject } from '../../../../../src/plugins/kibana_utils/common';

export const HITS_TOTAL_RELATION = {
EQ: 'eq',
Expand All @@ -30,3 +33,5 @@ export interface SearchResponse7<T = any> {
hits: SearchResponse7Hits<T>;
aggregations?: any;
}

export type InfluencersFilterQuery = ReturnType<typeof buildEsQuery> | DslQuery | JsonObject;
5 changes: 3 additions & 2 deletions x-pack/plugins/ml/common/types/ml_url_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ML_PAGES } from '../constants/ml_url_generator';
import type { DataFrameAnalysisConfigType } from './data_frame_analytics';
import type { SearchQueryLanguage } from '../constants/search';
import type { ListingPageUrlState } from './common';
import type { InfluencersFilterQuery } from './es_client';

type OptionalPageState = object | undefined;

Expand Down Expand Up @@ -113,9 +114,9 @@ export interface ExplorerAppState {
viewByFromPage?: number;
};
mlExplorerFilter: {
influencersFilterQuery?: unknown;
influencersFilterQuery?: InfluencersFilterQuery;
filterActive?: boolean;
filteredFields?: string[];
filteredFields?: Array<string | number>;
queryString?: string;
};
query?: any;
Expand Down
8 changes: 8 additions & 0 deletions x-pack/plugins/ml/common/util/anomaly_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,18 @@ export enum ENTITY_FIELD_TYPE {
PARTITON = 'partition',
}

export const ENTITY_FIELD_OPERATIONS = {
ADD: '+',
REMOVE: '-',
} as const;

export type EntityFieldOperation = typeof ENTITY_FIELD_OPERATIONS[keyof typeof ENTITY_FIELD_OPERATIONS];

export interface EntityField {
fieldName: string;
fieldValue: string | number | undefined;
fieldType?: ENTITY_FIELD_TYPE;
operation?: EntityFieldOperation;
}

// List of function descriptions for which actual values from record level results should be displayed.
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/ml/public/__mocks__/core_start.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { coreMock } from '../../../../../src/core/public/mocks';
import { createMlStartDepsMock } from './ml_start_deps';

export const createCoreStartMock = () =>
coreMock.createSetup({ pluginStartDeps: createMlStartDepsMock() });
28 changes: 28 additions & 0 deletions x-pack/plugins/ml/public/__mocks__/ml_start_deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { uiActionsPluginMock } from '../../../../../src/plugins/ui_actions/public/mocks';
import { dataPluginMock } from '../../../../../src/plugins/data/public/mocks';
import { kibanaLegacyPluginMock } from '../../../../../src/plugins/kibana_legacy/public/mocks';
import { embeddablePluginMock } from '../../../../../src/plugins/embeddable/public/mocks';
import { lensPluginMock } from '../../../lens/public/mocks';
import { triggersActionsUiMock } from '../../../triggers_actions_ui/public/mocks';

export const createMlStartDepsMock = () => ({
data: dataPluginMock.createStartContract(),
share: {
urlGenerators: { getUrlGenerator: jest.fn() },
},
kibanaLegacy: kibanaLegacyPluginMock.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),
spaces: jest.fn(),
embeddable: embeddablePluginMock.createStartContract(),
maps: jest.fn(),
lens: lensPluginMock.createStartContract(),
triggersActionsUi: triggersActionsUiMock.createStart(),
fileUpload: jest.fn(),
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { EuiSelect } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { usePageUrlState } from '../../../util/url_state';

interface TableInterval {
export interface TableInterval {
display: string;
val: string;
}
Expand Down Expand Up @@ -64,16 +64,24 @@ export const useTableInterval = (): [TableInterval, (v: TableInterval) => void]
export const SelectInterval: FC = () => {
const [interval, setInterval] = useTableInterval();

const onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
setInterval(optionValueToInterval(e.target.value));
return <SelectIntervalUI interval={interval} onChange={setInterval} />;
};

interface SelectIntervalUIProps {
interval: TableInterval;
onChange: (interval: TableInterval) => void;
}
export const SelectIntervalUI: FC<SelectIntervalUIProps> = ({ interval, onChange }) => {
const handleOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
onChange(optionValueToInterval(e.target.value));
};

return (
<EuiSelect
options={OPTIONS}
className="ml-select-interval"
value={interval.val}
onChange={onChange}
onChange={handleOnChange}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const optionsMap = {
[criticalLabel]: ANOMALY_THRESHOLD.CRITICAL,
};

interface TableSeverity {
export interface TableSeverity {
val: number;
display: string;
color: string;
Expand Down Expand Up @@ -67,7 +67,7 @@ export const SEVERITY_OPTIONS: TableSeverity[] = [
},
];

function optionValueToThreshold(value: number) {
export function optionValueToThreshold(value: number) {
// Get corresponding threshold object with required display and val properties from the specified value.
let threshold = SEVERITY_OPTIONS.find((opt) => opt.val === value);

Expand Down Expand Up @@ -121,17 +121,26 @@ interface Props {
export const SelectSeverity: FC<Props> = ({ classNames } = { classNames: '' }) => {
const [severity, setSeverity] = useTableSeverity();

const onChange = (valueDisplay: string) => {
setSeverity(optionValueToThreshold(optionsMap[valueDisplay]));
return <SelectSeverityUI severity={severity} onChange={setSeverity} />;
};

export const SelectSeverityUI: FC<{
classNames?: string;
severity: TableSeverity;
onChange: (s: TableSeverity) => void;
}> = ({ classNames = '', severity, onChange }) => {
const handleOnChange = (valueDisplay: string) => {
onChange(optionValueToThreshold(optionsMap[valueDisplay]));
};

return (
<EuiSuperSelect
data-test-subj={'mlAnomalySeverityThresholdControls'}
className={classNames}
hasDividers
options={getSeverityOptions()}
valueOfSelected={severity.display}
onChange={onChange}
onChange={handleOnChange}
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useMemo } from 'react';
import { useUiSettings } from '../../contexts/kibana';
import { TimeBuckets } from '../../util/time_buckets';
import { UI_SETTINGS } from '../../../../../../../src/plugins/data/common';

export const useTimeBuckets = () => {
const uiSettings = useUiSettings();
return useMemo(() => {
return new TimeBuckets({
'histogram:maxBars': uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS),
'histogram:barTarget': uiSettings.get(UI_SETTINGS.HISTOGRAM_BAR_TARGET),
dateFormat: uiSettings.get('dateFormat'),
'dateFormat:scaled': uiSettings.get('dateFormat:scaled'),
});
}, [uiSettings]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ export function CustomSelectionTable({
isSelected={isItemSelected(item[tableItemId])}
isSelectable={true}
hasActions={true}
data-test-subj="mlCustomSelectionTableRow"
data-test-subj={`mlCustomSelectionTableRow row-${item[tableItemId]}`}
>
{cells}
</EuiTableRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
import { EMPTY_FIELD_VALUE_LABEL } from '../../timeseriesexplorer/components/entity_control/entity_control';
import { MLCATEGORY } from '../../../../common/constants/field_types';
import { ENTITY_FIELD_OPERATIONS } from '../../../../common/util/anomaly_utils';

export type EntityCellFilter = (
entityName: string,
Expand Down Expand Up @@ -40,7 +41,7 @@ function getAddFilter({ entityName, entityValue, filter }: EntityCellProps) {
<EuiButtonIcon
size="s"
className="filter-button"
onClick={() => filter(entityName, entityValue, '+')}
onClick={() => filter(entityName, entityValue, ENTITY_FIELD_OPERATIONS.ADD)}
iconType="plusInCircle"
aria-label={i18n.translate('xpack.ml.anomaliesTable.entityCell.addFilterAriaLabel', {
defaultMessage: 'Add filter',
Expand All @@ -65,7 +66,7 @@ function getRemoveFilter({ entityName, entityValue, filter }: EntityCellProps) {
<EuiButtonIcon
size="s"
className="filter-button"
onClick={() => filter(entityName, entityValue, '-')}
onClick={() => filter(entityName, entityValue, ENTITY_FIELD_OPERATIONS.REMOVE)}
iconType="minusInCircle"
aria-label={i18n.translate('xpack.ml.anomaliesTable.entityCell.removeFilterAriaLabel', {
defaultMessage: 'Remove filter',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@
* 2.0.
*/

export const useMlKibana = jest.fn(() => {
return {
services: {
application: {
navigateToApp: jest.fn(),
export const kibanaContextMock = {
services: {
chrome: { recentlyAccessed: { add: jest.fn() } },
application: { navigateToApp: jest.fn() },
http: {
basePath: {
get: jest.fn(),
},
},
};
share: {
urlGenerators: { getUrlGenerator: jest.fn() },
},
},
};

export const useMlKibana = jest.fn(() => {
return kibanaContextMock;
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';

const timefilterMock = dataPluginMock.createStartContract().query.timefilter.timefilter;
export const timefilterMock = dataPluginMock.createStartContract().query.timefilter.timefilter;

export const useTimefilter = jest.fn(() => {
return timefilterMock;
Expand Down
Loading