mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ML] Move nested property utilities and url state to packages (#147912)
Effort to deduplicate code. Move nested property utilities and url state to packages. Boilerplate for the packages was created likes this: ``` node scripts/generate package @kbn/ml-url-state --web --dir ./x-pack/packages/ml/url_state node scripts/generate package @kbn/ml-nested-property --web --dir ./x-pack/packages/ml/nested_property ``` I consolidated the different `url_state.ts` files. One thing to note: Each one had its own definition for `pageKey: AppStateKey`. I changed that and made it just `pageKey: string`, I suspect it's good enough. Otherwise we'd have a reverse dependency on all consuming code. Alternative: We could refactor to require overriding a generic to pass in allowed values.
This commit is contained in:
parent
4ec4acaaaf
commit
8a44ba3158
81 changed files with 467 additions and 614 deletions
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
|
@ -1072,4 +1072,6 @@ x-pack/packages/ml/agg_utils @elastic/ml-ui
|
|||
x-pack/packages/ml/aiops_components @elastic/ml-ui
|
||||
x-pack/packages/ml/aiops_utils @elastic/ml-ui
|
||||
x-pack/packages/ml/is_populated_object @elastic/ml-ui
|
||||
x-pack/packages/ml/nested_property @elastic/ml-ui
|
||||
x-pack/packages/ml/string_hash @elastic/ml-ui
|
||||
x-pack/packages/ml/url_state @elastic/ml-ui
|
||||
|
|
|
@ -350,7 +350,9 @@
|
|||
"@kbn/mapbox-gl": "link:packages/kbn-mapbox-gl",
|
||||
"@kbn/ml-agg-utils": "link:x-pack/packages/ml/agg_utils",
|
||||
"@kbn/ml-is-populated-object": "link:x-pack/packages/ml/is_populated_object",
|
||||
"@kbn/ml-nested-property": "link:x-pack/packages/ml/nested_property",
|
||||
"@kbn/ml-string-hash": "link:x-pack/packages/ml/string_hash",
|
||||
"@kbn/ml-url-state": "link:x-pack/packages/ml/url_state",
|
||||
"@kbn/monaco": "link:packages/kbn-monaco",
|
||||
"@kbn/osquery-io-ts-types": "link:packages/kbn-osquery-io-ts-types",
|
||||
"@kbn/plugin-discovery": "link:packages/kbn-plugin-discovery",
|
||||
|
|
|
@ -816,10 +816,14 @@
|
|||
"@kbn/ml-agg-utils/*": ["x-pack/packages/ml/agg_utils/*"],
|
||||
"@kbn/ml-is-populated-object": ["x-pack/packages/ml/is_populated_object"],
|
||||
"@kbn/ml-is-populated-object/*": ["x-pack/packages/ml/is_populated_object/*"],
|
||||
"@kbn/ml-nested-property": ["x-pack/packages/ml/nested_property"],
|
||||
"@kbn/ml-nested-property/*": ["x-pack/packages/ml/nested_property/*"],
|
||||
"@kbn/ml-plugin": ["x-pack/plugins/ml"],
|
||||
"@kbn/ml-plugin/*": ["x-pack/plugins/ml/*"],
|
||||
"@kbn/ml-string-hash": ["x-pack/packages/ml/string_hash"],
|
||||
"@kbn/ml-string-hash/*": ["x-pack/packages/ml/string_hash/*"],
|
||||
"@kbn/ml-url-state": ["x-pack/packages/ml/url_state"],
|
||||
"@kbn/ml-url-state/*": ["x-pack/packages/ml/url_state/*"],
|
||||
"@kbn/monaco": ["packages/kbn-monaco"],
|
||||
"@kbn/monaco/*": ["packages/kbn-monaco/*"],
|
||||
"@kbn/monitoring-collection-plugin": ["x-pack/plugins/monitoring_collection"],
|
||||
|
|
3
x-pack/packages/ml/nested_property/README.md
Normal file
3
x-pack/packages/ml/nested_property/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/ml-nested-property
|
||||
|
||||
Provides functionality similar to lodash's get() except that it's TypeScript aware and able to infer return types.
|
9
x-pack/packages/ml/nested_property/index.ts
Normal file
9
x-pack/packages/ml/nested_property/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { getNestedProperty } from './src/get_nested_property';
|
||||
export { setNestedProperty } from './src/set_nested_property';
|
12
x-pack/packages/ml/nested_property/jest.config.js
Normal file
12
x-pack/packages/ml/nested_property/jest.config.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/x-pack/packages/ml/nested_property'],
|
||||
};
|
5
x-pack/packages/ml/nested_property/kibana.jsonc
Normal file
5
x-pack/packages/ml/nested_property/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/ml-nested-property",
|
||||
"owner": "@elastic/ml-ui"
|
||||
}
|
9
x-pack/packages/ml/nested_property/package.json
Normal file
9
x-pack/packages/ml/nested_property/package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "@kbn/ml-nested-property",
|
||||
"description": "TypeScript-aware utility functions to get/set attributes from objects.",
|
||||
"author": "Machine Learning UI",
|
||||
"homepage": "https://docs.elastic.dev/kibana-dev-docs/api/kbn-ml-nested-property",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getNestedProperty } from './object_utils';
|
||||
import { getNestedProperty } from './get_nested_property';
|
||||
|
||||
describe('object_utils', () => {
|
||||
test('getNestedProperty()', () => {
|
|
@ -33,21 +33,3 @@ export function getNestedProperty(
|
|||
|
||||
return o;
|
||||
}
|
||||
|
||||
export const setNestedProperty = (obj: Record<string, any>, accessor: string, value: any) => {
|
||||
let ref = obj;
|
||||
const accessors = accessor.split('.');
|
||||
const len = accessors.length;
|
||||
for (let i = 0; i < len - 1; i++) {
|
||||
const attribute = accessors[i];
|
||||
if (ref[attribute] === undefined) {
|
||||
ref[attribute] = {};
|
||||
}
|
||||
|
||||
ref = ref[attribute];
|
||||
}
|
||||
|
||||
ref[accessors[len - 1]] = value;
|
||||
|
||||
return obj;
|
||||
};
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 { setNestedProperty } from './set_nested_property';
|
||||
|
||||
describe('object_utils', () => {
|
||||
test('setNestedProperty()', () => {
|
||||
function getTestObj() {
|
||||
return {
|
||||
the: {
|
||||
nested: {
|
||||
value: 'the-nested-value',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getFalseyObject() {
|
||||
return {
|
||||
the: {
|
||||
nested: {
|
||||
value: false,
|
||||
},
|
||||
other_nested: {
|
||||
value: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const test1 = setNestedProperty(getTestObj(), 'the', 'update');
|
||||
expect(test1.the).toBe('update');
|
||||
|
||||
const test2 = setNestedProperty(getTestObj(), 'the$', 'update');
|
||||
expect(test2.the$).toBe('update');
|
||||
|
||||
const test3 = setNestedProperty(getTestObj(), 'the$', 'the-default-value');
|
||||
expect(test3.the$).toBe('the-default-value');
|
||||
|
||||
const test4 = setNestedProperty(getTestObj(), 'the.neSted', 'update');
|
||||
expect(test4.the.neSted).toBe('update');
|
||||
|
||||
const test5 = setNestedProperty(getTestObj(), 'the.nested', 'update');
|
||||
expect(test5.the.nested).toStrictEqual('update');
|
||||
|
||||
const test6 = setNestedProperty(getTestObj(), 'the.nested.vaLue', 'update');
|
||||
expect(test6.the.nested.vaLue).toBe('update');
|
||||
|
||||
const test7 = setNestedProperty(getTestObj(), 'the.nested.value', 'update');
|
||||
expect(test7.the.nested.value).toBe('update');
|
||||
|
||||
const test8 = setNestedProperty(getTestObj(), 'the.nested.value.didntExist', 'update');
|
||||
expect(test8.the.nested.value.didntExist).toBe('update');
|
||||
|
||||
const test9 = setNestedProperty(
|
||||
getTestObj(),
|
||||
'the.nested.value.didntExist',
|
||||
'the-default-value'
|
||||
);
|
||||
expect(test9.the.nested.value.didntExist).toBe('the-default-value');
|
||||
|
||||
const test10 = setNestedProperty(getFalseyObject(), 'the.nested.value', 'update');
|
||||
expect(test10.the.nested.value).toBe('update');
|
||||
|
||||
const test11 = setNestedProperty(getFalseyObject(), 'the.other_nested.value', 'update');
|
||||
expect(test11.the.other_nested.value).toBe('update');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const setNestedProperty = (obj: Record<string, any>, accessor: string, value: any) => {
|
||||
let ref = obj;
|
||||
const accessors = accessor.split('.');
|
||||
const len = accessors.length;
|
||||
for (let i = 0; i < len - 1; i++) {
|
||||
const attribute = accessors[i];
|
||||
if (typeof ref[attribute] !== 'object') {
|
||||
ref[attribute] = {};
|
||||
}
|
||||
|
||||
ref = ref[attribute];
|
||||
}
|
||||
|
||||
ref[accessors[len - 1]] = value;
|
||||
|
||||
return obj;
|
||||
};
|
18
x-pack/packages/ml/nested_property/tsconfig.json
Normal file
18
x-pack/packages/ml/nested_property/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
]
|
||||
}
|
3
x-pack/packages/ml/url_state/README.md
Normal file
3
x-pack/packages/ml/url_state/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/ml-url-state
|
||||
|
||||
URL state management.
|
19
x-pack/packages/ml/url_state/index.ts
Normal file
19
x-pack/packages/ml/url_state/index.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
isRisonSerializationRequired,
|
||||
parseUrlState,
|
||||
usePageUrlState,
|
||||
useUrlState,
|
||||
PageUrlStateService,
|
||||
Provider,
|
||||
UrlStateProvider,
|
||||
type Accessor,
|
||||
type Dictionary,
|
||||
type SetUrlState,
|
||||
} from './src/url_state';
|
12
x-pack/packages/ml/url_state/jest.config.js
Normal file
12
x-pack/packages/ml/url_state/jest.config.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../../..',
|
||||
roots: ['<rootDir>/x-pack/packages/ml/url_state'],
|
||||
};
|
5
x-pack/packages/ml/url_state/kibana.jsonc
Normal file
5
x-pack/packages/ml/url_state/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-common",
|
||||
"id": "@kbn/ml-url-state",
|
||||
"owner": "@elastic/ml-ui"
|
||||
}
|
9
x-pack/packages/ml/url_state/package.json
Normal file
9
x-pack/packages/ml/url_state/package.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"name": "@kbn/ml-url-state",
|
||||
"description": "Url state management utilities.",
|
||||
"author": "Machine Learning UI",
|
||||
"homepage": "https://docs.elastic.dev/kibana-dev-docs/api/kbn-ml-url-state",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
|
@ -11,23 +11,25 @@ import React, {
|
|||
useCallback,
|
||||
useContext,
|
||||
useMemo,
|
||||
FC,
|
||||
useRef,
|
||||
useEffect,
|
||||
type FC,
|
||||
} from 'react';
|
||||
import { isEqual } from 'lodash';
|
||||
import { decode, encode } from '@kbn/rison';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { getNestedProperty } from '@kbn/ml-nested-property';
|
||||
import { decode, encode } from '@kbn/rison';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import { Dictionary } from '../../../common/types/common';
|
||||
|
||||
import { getNestedProperty } from './object_utils';
|
||||
import { MlPages } from '../../../common/constants/locator';
|
||||
export interface Dictionary<TValue> {
|
||||
[id: string]: TValue;
|
||||
}
|
||||
|
||||
type Accessor = '_a' | '_g';
|
||||
export type Accessor = '_a' | '_g';
|
||||
export type SetUrlState = (
|
||||
accessor: Accessor,
|
||||
attribute: string | Dictionary<any>,
|
||||
|
@ -48,7 +50,7 @@ const risonSerializedParams = new Set(['_a', '_g']);
|
|||
* Checks if the URL query parameter requires rison serialization.
|
||||
* @param queryParam
|
||||
*/
|
||||
function isRisonSerializationRequired(queryParam: string): boolean {
|
||||
export function isRisonSerializationRequired(queryParam: string): boolean {
|
||||
return risonSerializedParams.has(queryParam);
|
||||
}
|
||||
|
||||
|
@ -86,7 +88,7 @@ export const urlStateStore = createContext<UrlState>({
|
|||
setUrlState: () => {},
|
||||
});
|
||||
|
||||
const { Provider } = urlStateStore;
|
||||
export const { Provider } = urlStateStore;
|
||||
|
||||
export const UrlStateProvider: FC = ({ children }) => {
|
||||
const history = useHistory();
|
||||
|
@ -183,15 +185,6 @@ export const useUrlState = (
|
|||
return [urlState, setUrlState];
|
||||
};
|
||||
|
||||
type LegacyUrlKeys = 'mlExplorerSwimlane';
|
||||
|
||||
export type AppStateKey =
|
||||
| 'mlSelectSeverity'
|
||||
| 'mlSelectInterval'
|
||||
| 'mlAnomaliesTable'
|
||||
| MlPages
|
||||
| LegacyUrlKeys;
|
||||
|
||||
/**
|
||||
* Service for managing URL state of particular page.
|
||||
*/
|
||||
|
@ -235,16 +228,21 @@ export class PageUrlStateService<T> {
|
|||
}
|
||||
}
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: string;
|
||||
pageUrlState: object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for managing the URL state of the page.
|
||||
*/
|
||||
export const usePageUrlState = <PageUrlState extends object>(
|
||||
pageKey: AppStateKey,
|
||||
defaultState?: PageUrlState
|
||||
export const usePageUrlState = <T extends PageUrlState>(
|
||||
pageKey: T['pageKey'],
|
||||
defaultState?: T['pageUrlState']
|
||||
): [
|
||||
PageUrlState,
|
||||
(update: Partial<PageUrlState>, replaceState?: boolean) => void,
|
||||
PageUrlStateService<PageUrlState>
|
||||
T['pageUrlState'],
|
||||
(update: Partial<T['pageUrlState']>, replaceState?: boolean) => void,
|
||||
PageUrlStateService<T['pageUrlState']>
|
||||
] => {
|
||||
const [appState, setAppState] = useUrlState('_a');
|
||||
const pageState = appState?.[pageKey];
|
||||
|
@ -255,9 +253,9 @@ export const usePageUrlState = <PageUrlState extends object>(
|
|||
setCallback.current = setAppState;
|
||||
}, [setAppState]);
|
||||
|
||||
const prevPageState = useRef<PageUrlState | undefined>();
|
||||
const prevPageState = useRef<T['pageUrlState'] | undefined>();
|
||||
|
||||
const resultPageState: PageUrlState = useMemo(() => {
|
||||
const resultPageState: T['pageUrlState'] = useMemo(() => {
|
||||
const result = {
|
||||
...(defaultState ?? {}),
|
||||
...(pageState ?? {}),
|
||||
|
@ -283,7 +281,7 @@ export const usePageUrlState = <PageUrlState extends object>(
|
|||
}, [pageState]);
|
||||
|
||||
const onStateUpdate = useCallback(
|
||||
(update: Partial<PageUrlState>, replaceState?: boolean) => {
|
||||
(update: Partial<T['pageUrlState']>, replaceState?: boolean) => {
|
||||
if (!setCallback?.current) {
|
||||
throw new Error('Callback for URL state update has not been initialized.');
|
||||
}
|
||||
|
@ -300,7 +298,7 @@ export const usePageUrlState = <PageUrlState extends object>(
|
|||
[pageKey, resultPageState]
|
||||
);
|
||||
|
||||
const pageUrlStateService = useMemo(() => new PageUrlStateService<PageUrlState>(), []);
|
||||
const pageUrlStateService = useMemo(() => new PageUrlStateService<T['pageUrlState']>(), []);
|
||||
|
||||
useEffect(
|
||||
function updatePageUrlService() {
|
23
x-pack/packages/ml/url_state/tsconfig.json
Normal file
23
x-pack/packages/ml/url_state/tsconfig.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"extends": "../../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
"react"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
],
|
||||
"kbn_references": [
|
||||
"@kbn/ml-nested-property",
|
||||
"@kbn/rison",
|
||||
"@kbn/ml-is-populated-object",
|
||||
]
|
||||
}
|
|
@ -18,6 +18,7 @@ import { type DataViewField } from '@kbn/data-views-plugin/public';
|
|||
import { startWith } from 'rxjs';
|
||||
import useMount from 'react-use/lib/useMount';
|
||||
import type { Query, Filter } from '@kbn/es-query';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import {
|
||||
createMergedEsQuery,
|
||||
getEsQueryFromSavedSearch,
|
||||
|
@ -27,9 +28,13 @@ import { useTimefilter, useTimeRangeUpdates } from '../../hooks/use_time_filter'
|
|||
import { useChangePointResults } from './use_change_point_agg_request';
|
||||
import { type TimeBuckets, TimeBucketsInterval } from '../../../common/time_buckets';
|
||||
import { useDataSource } from '../../hooks/use_data_source';
|
||||
import { usePageUrlState } from '../../hooks/use_url_state';
|
||||
import { useTimeBuckets } from '../../hooks/use_time_buckets';
|
||||
|
||||
export interface ChangePointDetectionPageUrlState {
|
||||
pageKey: 'changePoint';
|
||||
pageUrlState: ChangePointDetectionRequestParams;
|
||||
}
|
||||
|
||||
export interface ChangePointDetectionRequestParams {
|
||||
fn: string;
|
||||
splitField: string;
|
||||
|
@ -157,7 +162,7 @@ export const ChangePointDetectionContextProvider: FC = ({ children }) => {
|
|||
}, [dataView]);
|
||||
|
||||
const [requestParamsFromUrl, updateRequestParams] =
|
||||
usePageUrlState<ChangePointDetectionRequestParams>('changePoint');
|
||||
usePageUrlState<ChangePointDetectionPageUrlState>('changePoint');
|
||||
|
||||
const resultQuery = useMemo<Query>(() => {
|
||||
return (
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { SavedSearch } from '@kbn/saved-search-plugin/public';
|
||||
import React, { FC } from 'react';
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
import { PageHeader } from '../page_header';
|
||||
import { ChangePointDetectionContextProvider } from './change_point_detection_context';
|
||||
import { DataSourceContext } from '../../hooks/use_data_source';
|
||||
import { UrlStateProvider } from '../../hooks/use_url_state';
|
||||
import { SavedSearchSavedObject } from '../../application/utils/search_utils';
|
||||
import { AiopsAppContext, AiopsAppDependencies } from '../../hooks/use_aiops_app_context';
|
||||
import { ChangePointDetectionPage } from './change_point_detection_page';
|
||||
|
|
|
@ -20,14 +20,15 @@ import {
|
|||
OnRefreshProps,
|
||||
OnTimeChangeProps,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { useRefreshIntervalUpdates, useTimeRangeUpdates } from '../../hooks/use_time_filter';
|
||||
import { useUrlState } from '../../hooks/use_url_state';
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { aiopsRefresh$ } from '../../application/services/timefilter_refresh_service';
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@ import { i18n } from '@kbn/i18n';
|
|||
import type { SavedSearch } from '@kbn/discover-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
import {
|
||||
SEARCH_QUERY_LANGUAGE,
|
||||
SearchQueryLanguage,
|
||||
SavedSearchSavedObject,
|
||||
} from '../../application/utils/search_utils';
|
||||
import { UrlStateProvider } from '../../hooks/use_url_state';
|
||||
import type { AiopsAppDependencies } from '../../hooks/use_aiops_app_context';
|
||||
import { AiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
|
||||
|
@ -41,6 +41,11 @@ const defaultSearchQuery = {
|
|||
match_all: {},
|
||||
};
|
||||
|
||||
export interface AiOpsPageUrlState {
|
||||
pageKey: 'AIOPS_INDEX_VIEWER';
|
||||
pageUrlState: AiOpsIndexBasedAppState;
|
||||
}
|
||||
|
||||
export interface AiOpsIndexBasedAppState {
|
||||
searchString?: Query['query'];
|
||||
searchQuery?: Query['query'];
|
||||
|
|
|
@ -27,16 +27,16 @@ import { Filter, FilterStateStore, Query } from '@kbn/es-query';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { SavedSearch } from '@kbn/discover-plugin/public';
|
||||
|
||||
import { useUrlState, usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { SearchQueryLanguage, SavedSearchSavedObject } from '../../application/utils/search_utils';
|
||||
import { useUrlState, usePageUrlState, AppStateKey } from '../../hooks/use_url_state';
|
||||
import { useData } from '../../hooks/use_data';
|
||||
import { FullTimeRangeSelector } from '../full_time_range_selector';
|
||||
import { DocumentCountContent } from '../document_count_content/document_count_content';
|
||||
import { DatePickerWrapper } from '../date_picker_wrapper';
|
||||
import { SearchPanel } from '../search_panel';
|
||||
|
||||
import { restorableDefaults } from './explain_log_rate_spikes_app_state';
|
||||
import { restorableDefaults, type AiOpsPageUrlState } from './explain_log_rate_spikes_app_state';
|
||||
import { ExplainLogRateSpikesAnalysis } from './explain_log_rate_spikes_analysis';
|
||||
import type { GroupTableItem } from '../spike_analysis_table/types';
|
||||
import { useSpikeAnalysisTableRowContext } from '../spike_analysis_table/spike_analysis_table_row_provider';
|
||||
|
@ -79,7 +79,10 @@ export const ExplainLogRateSpikesPage: FC<ExplainLogRateSpikesPageProps> = ({
|
|||
setSelectedGroup,
|
||||
} = useSpikeAnalysisTableRowContext();
|
||||
|
||||
const [aiopsListState, setAiopsListState] = usePageUrlState(AppStateKey, restorableDefaults);
|
||||
const [aiopsListState, setAiopsListState] = usePageUrlState<AiOpsPageUrlState>(
|
||||
'AIOPS_INDEX_VIEWER',
|
||||
restorableDefaults
|
||||
);
|
||||
const [globalState, setGlobalState] = useUrlState('_g');
|
||||
|
||||
const [currentSavedSearch, setCurrentSavedSearch] = useState(savedSearch);
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
import React, { FC } from 'react';
|
||||
import type { SavedSearch } from '@kbn/discover-plugin/public';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
import { LogCategorizationPage } from './log_categorization_page';
|
||||
import { SavedSearchSavedObject } from '../../application/utils/search_utils';
|
||||
import type { AiopsAppDependencies } from '../../hooks/use_aiops_app_context';
|
||||
import { AiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { UrlStateProvider } from '../../hooks/use_url_state';
|
||||
|
||||
export interface LogCategorizationAppStateProps {
|
||||
dataView: DataView;
|
||||
|
|
|
@ -25,6 +25,7 @@ import {
|
|||
EuiLoadingContent,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { FullTimeRangeSelector } from '../full_time_range_selector';
|
||||
import { DatePickerWrapper } from '../date_picker_wrapper';
|
||||
import { useData } from '../../hooks/use_data';
|
||||
|
@ -33,7 +34,6 @@ import type {
|
|||
SearchQueryLanguage,
|
||||
SavedSearchSavedObject,
|
||||
} from '../../application/utils/search_utils';
|
||||
import { useUrlState } from '../../hooks/use_url_state';
|
||||
import { useAiopsAppContext } from '../../hooks/use_aiops_app_context';
|
||||
import { restorableDefaults } from '../explain_log_rate_spikes/explain_log_rate_spikes_app_state';
|
||||
import { useCategorizeRequest } from './use_categorize_request';
|
||||
|
|
|
@ -14,8 +14,8 @@ import {
|
|||
EuiPageContentHeader_Deprecated as EuiPageContentHeader,
|
||||
EuiPageContentHeaderSection_Deprecated as EuiPageContentHeaderSection,
|
||||
} from '@elastic/eui';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { FullTimeRangeSelectorProps } from '../full_time_range_selector/full_time_range_selector';
|
||||
import { useUrlState } from '../../hooks/use_url_state';
|
||||
import { useDataSource } from '../../hooks/use_data_source';
|
||||
import { useTimefilter } from '../../hooks/use_time_filter';
|
||||
import { FullTimeRangeSelector } from '../full_time_range_selector';
|
||||
|
|
|
@ -13,6 +13,7 @@ import type { ChangePoint } from '@kbn/ml-agg-utils';
|
|||
|
||||
import type { SavedSearch } from '@kbn/discover-plugin/public';
|
||||
|
||||
import type { Dictionary } from '@kbn/ml-url-state';
|
||||
import { useTimeBuckets } from './use_time_buckets';
|
||||
|
||||
import { useAiopsAppContext } from './use_aiops_app_context';
|
||||
|
@ -26,7 +27,6 @@ import {
|
|||
|
||||
import { useTimefilter } from './use_time_filter';
|
||||
import { useDocumentCountStats } from './use_document_count_stats';
|
||||
import type { Dictionary } from './use_url_state';
|
||||
import type { GroupTableItem } from '../components/spike_analysis_table/types';
|
||||
|
||||
const DEFAULT_BAR_TARGET = 75;
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
/*
|
||||
* 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 React, { FC } from 'react';
|
||||
import { parse, stringify } from 'query-string';
|
||||
import { createContext, useCallback, useContext, useMemo } from 'react';
|
||||
import { decode, encode } from '@kbn/rison';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
export interface Dictionary<TValue> {
|
||||
[id: string]: TValue;
|
||||
}
|
||||
|
||||
// TODO duplicate of ml/object_utils
|
||||
export const getNestedProperty = (
|
||||
obj: Record<string, any>,
|
||||
accessor: string,
|
||||
defaultValue?: any
|
||||
) => {
|
||||
const value = accessor.split('.').reduce((o, i) => o?.[i], obj);
|
||||
|
||||
if (value === undefined) return defaultValue;
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export type Accessor = '_a' | '_g';
|
||||
export type SetUrlState = (
|
||||
accessor: Accessor,
|
||||
attribute: string | Dictionary<any>,
|
||||
value?: any,
|
||||
replaceState?: boolean
|
||||
) => void;
|
||||
export interface UrlState {
|
||||
searchString: string;
|
||||
setUrlState: SetUrlState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of URL query parameters that require the rison serialization.
|
||||
*/
|
||||
const risonSerializedParams = new Set(['_a', '_g']);
|
||||
|
||||
/**
|
||||
* Checks if the URL query parameter requires rison serialization.
|
||||
* @param queryParam
|
||||
*/
|
||||
export function isRisonSerializationRequired(queryParam: string): boolean {
|
||||
return risonSerializedParams.has(queryParam);
|
||||
}
|
||||
|
||||
export function parseUrlState(search: string): Dictionary<any> {
|
||||
const urlState: Dictionary<any> = {};
|
||||
const parsedQueryString = parse(search, { sort: false });
|
||||
|
||||
try {
|
||||
Object.keys(parsedQueryString).forEach((a) => {
|
||||
if (isRisonSerializationRequired(a)) {
|
||||
urlState[a] = decode(parsedQueryString[a] as string);
|
||||
} else {
|
||||
urlState[a] = parsedQueryString[a];
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Could not read url state', error);
|
||||
}
|
||||
|
||||
return urlState;
|
||||
}
|
||||
|
||||
// Compared to the original appState/globalState,
|
||||
// this no longer makes use of fetch/save methods.
|
||||
// - Reading from `location.search` is the successor of `fetch`.
|
||||
// - `history.push()` is the successor of `save`.
|
||||
// - The exposed state and set call make use of the above and make sure that
|
||||
// different urlStates(e.g. `_a` / `_g`) don't overwrite each other.
|
||||
// This uses a context to be able to maintain only one instance
|
||||
// of the url state. It gets passed down with `UrlStateProvider`
|
||||
// and can be used via `useUrlState`.
|
||||
export const aiopsUrlStateStore = createContext<UrlState>({
|
||||
searchString: '',
|
||||
setUrlState: () => {},
|
||||
});
|
||||
|
||||
export const { Provider } = aiopsUrlStateStore;
|
||||
|
||||
export const UrlStateProvider: FC = ({ children }) => {
|
||||
const { Provider: StateProvider } = aiopsUrlStateStore;
|
||||
|
||||
const history = useHistory();
|
||||
const { search: urlSearchString } = useLocation();
|
||||
|
||||
const setUrlState: SetUrlState = useCallback(
|
||||
(
|
||||
accessor: Accessor,
|
||||
attribute: string | Dictionary<any>,
|
||||
value?: any,
|
||||
replaceState?: boolean
|
||||
) => {
|
||||
const prevSearchString = urlSearchString;
|
||||
const urlState = parseUrlState(prevSearchString);
|
||||
const parsedQueryString = parse(prevSearchString, { sort: false });
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(urlState, accessor)) {
|
||||
urlState[accessor] = {};
|
||||
}
|
||||
|
||||
if (typeof attribute === 'string') {
|
||||
if (isEqual(getNestedProperty(urlState, `${accessor}.${attribute}`), value)) {
|
||||
return prevSearchString;
|
||||
}
|
||||
|
||||
urlState[accessor][attribute] = value;
|
||||
} else {
|
||||
const attributes = attribute;
|
||||
Object.keys(attributes).forEach((a) => {
|
||||
urlState[accessor][a] = attributes[a];
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const oldLocationSearchString = stringify(parsedQueryString, {
|
||||
sort: false,
|
||||
encode: false,
|
||||
});
|
||||
|
||||
Object.keys(urlState).forEach((a) => {
|
||||
if (isRisonSerializationRequired(a)) {
|
||||
parsedQueryString[a] = encode(urlState[a]);
|
||||
} else {
|
||||
parsedQueryString[a] = urlState[a];
|
||||
}
|
||||
});
|
||||
const newLocationSearchString = stringify(parsedQueryString, {
|
||||
sort: false,
|
||||
encode: false,
|
||||
});
|
||||
|
||||
if (oldLocationSearchString !== newLocationSearchString) {
|
||||
const newSearchString = stringify(parsedQueryString, { sort: false });
|
||||
if (replaceState) {
|
||||
history.replace({ search: newSearchString });
|
||||
} else {
|
||||
history.push({ search: newSearchString });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Could not save url state', error);
|
||||
}
|
||||
},
|
||||
[history, urlSearchString]
|
||||
);
|
||||
|
||||
return (
|
||||
<StateProvider value={{ searchString: urlSearchString, setUrlState }}>{children}</StateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useUrlState = (accessor: Accessor) => {
|
||||
const { searchString, setUrlState: setUrlStateContext } = useContext(aiopsUrlStateStore);
|
||||
|
||||
const urlState = useMemo(() => {
|
||||
const fullUrlState = parseUrlState(searchString);
|
||||
if (typeof fullUrlState === 'object') {
|
||||
return fullUrlState[accessor];
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchString]);
|
||||
|
||||
const setUrlState = useCallback(
|
||||
(attribute: string | Dictionary<any>, value?: any, replaceState?: boolean) => {
|
||||
setUrlStateContext(accessor, attribute, value, replaceState);
|
||||
},
|
||||
[accessor, setUrlStateContext]
|
||||
);
|
||||
return [urlState, setUrlState];
|
||||
};
|
||||
|
||||
export const AppStateKey = 'AIOPS_INDEX_VIEWER';
|
||||
export const ChangePointStateKey = 'changePoint' as const;
|
||||
/**
|
||||
* Hook for managing the URL state of the page.
|
||||
*/
|
||||
export const usePageUrlState = <PageUrlState extends {}>(
|
||||
pageKey: typeof AppStateKey | typeof ChangePointStateKey,
|
||||
defaultState?: PageUrlState
|
||||
): [PageUrlState, (update: Partial<PageUrlState>, replaceState?: boolean) => void] => {
|
||||
const [appState, setAppState] = useUrlState('_a');
|
||||
const pageState = appState?.[pageKey];
|
||||
|
||||
const resultPageState: PageUrlState = useMemo(() => {
|
||||
return {
|
||||
...(defaultState ?? {}),
|
||||
...(pageState ?? {}),
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pageState]);
|
||||
|
||||
const onStateUpdate = useCallback(
|
||||
(update: Partial<PageUrlState>, replaceState?: boolean) => {
|
||||
setAppState(
|
||||
pageKey,
|
||||
{
|
||||
...resultPageState,
|
||||
...update,
|
||||
},
|
||||
replaceState
|
||||
);
|
||||
},
|
||||
[pageKey, resultPageState, setAppState]
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
return [resultPageState, onStateUpdate];
|
||||
}, [resultPageState, onStateUpdate]);
|
||||
};
|
|
@ -42,6 +42,7 @@
|
|||
"@kbn/logging",
|
||||
"@kbn/core-elasticsearch-server",
|
||||
"@kbn/es-types",
|
||||
"@kbn/ml-url-state",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -20,18 +20,20 @@ import {
|
|||
OnRefreshProps,
|
||||
OnTimeChangeProps,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import type { TimeRange } from '@kbn/es-query';
|
||||
import { TimeHistoryContract, UI_SETTINGS } from '@kbn/data-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { wrapWithTheme } from '@kbn/kibana-react-plugin/public';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import {
|
||||
useRefreshIntervalUpdates,
|
||||
useTimeRangeUpdates,
|
||||
} from '../../../index_data_visualizer/hooks/use_time_filter';
|
||||
import { useDataVisualizerKibana } from '../../../kibana_context';
|
||||
import { dataVisualizerRefresh$ } from '../../../index_data_visualizer/services/timefilter_refresh_service';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
|
||||
const DEFAULT_REFRESH_INTERVAL_MS = 5000;
|
||||
const DATE_PICKER_MAX_WIDTH = 540;
|
||||
|
|
|
@ -1,147 +0,0 @@
|
|||
/*
|
||||
* 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 { parse } from 'query-string';
|
||||
import { createContext, useCallback, useContext, useMemo } from 'react';
|
||||
import { decode } from '@kbn/rison';
|
||||
|
||||
export interface Dictionary<TValue> {
|
||||
[id: string]: TValue;
|
||||
}
|
||||
|
||||
// duplicate of ml/object_utils
|
||||
export const getNestedProperty = (
|
||||
obj: Record<string, any>,
|
||||
accessor: string,
|
||||
defaultValue?: any
|
||||
) => {
|
||||
const value = accessor.split('.').reduce((o, i) => o?.[i], obj);
|
||||
|
||||
if (value === undefined) return defaultValue;
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
export type Accessor = '_a' | '_g';
|
||||
export type SetUrlState = (
|
||||
accessor: Accessor,
|
||||
attribute: string | Dictionary<any>,
|
||||
value?: any,
|
||||
replaceState?: boolean
|
||||
) => void;
|
||||
export interface UrlState {
|
||||
searchString: string;
|
||||
setUrlState: SetUrlState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set of URL query parameters that require the rison serialization.
|
||||
*/
|
||||
const risonSerializedParams = new Set(['_a', '_g']);
|
||||
|
||||
/**
|
||||
* Checks if the URL query parameter requires rison serialization.
|
||||
* @param queryParam
|
||||
*/
|
||||
export function isRisonSerializationRequired(queryParam: string): boolean {
|
||||
return risonSerializedParams.has(queryParam);
|
||||
}
|
||||
|
||||
export function parseUrlState(search: string): Dictionary<any> {
|
||||
const urlState: Dictionary<any> = {};
|
||||
const parsedQueryString = parse(search, { sort: false });
|
||||
|
||||
try {
|
||||
Object.keys(parsedQueryString).forEach((a) => {
|
||||
if (isRisonSerializationRequired(a)) {
|
||||
urlState[a] = decode(parsedQueryString[a] as string);
|
||||
} else {
|
||||
urlState[a] = parsedQueryString[a];
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Could not read url state', error);
|
||||
}
|
||||
|
||||
return urlState;
|
||||
}
|
||||
|
||||
// Compared to the original appState/globalState,
|
||||
// this no longer makes use of fetch/save methods.
|
||||
// - Reading from `location.search` is the successor of `fetch`.
|
||||
// - `history.push()` is the successor of `save`.
|
||||
// - The exposed state and set call make use of the above and make sure that
|
||||
// different urlStates(e.g. `_a` / `_g`) don't overwrite each other.
|
||||
// This uses a context to be able to maintain only one instance
|
||||
// of the url state. It gets passed down with `UrlStateProvider`
|
||||
// and can be used via `useUrlState`.
|
||||
export const dataVisualizerUrlStateStore = createContext<UrlState>({
|
||||
searchString: '',
|
||||
setUrlState: () => {},
|
||||
});
|
||||
|
||||
export const { Provider } = dataVisualizerUrlStateStore;
|
||||
|
||||
export const useUrlState = (accessor: Accessor) => {
|
||||
const { searchString, setUrlState: setUrlStateContext } = useContext(dataVisualizerUrlStateStore);
|
||||
|
||||
const urlState = useMemo(() => {
|
||||
const fullUrlState = parseUrlState(searchString);
|
||||
if (typeof fullUrlState === 'object') {
|
||||
return fullUrlState[accessor];
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchString]);
|
||||
|
||||
const setUrlState = useCallback(
|
||||
(attribute: string | Dictionary<any>, value?: any, replaceState?: boolean) => {
|
||||
setUrlStateContext(accessor, attribute, value, replaceState);
|
||||
},
|
||||
[accessor, setUrlStateContext]
|
||||
);
|
||||
return [urlState, setUrlState];
|
||||
};
|
||||
|
||||
export type AppStateKey = 'DATA_VISUALIZER_INDEX_VIEWER';
|
||||
|
||||
/**
|
||||
* Hook for managing the URL state of the page.
|
||||
*/
|
||||
export const usePageUrlState = <PageUrlState extends {}>(
|
||||
pageKey: AppStateKey,
|
||||
defaultState?: PageUrlState
|
||||
): [PageUrlState, (update: Partial<PageUrlState>, replaceState?: boolean) => void] => {
|
||||
const [appState, setAppState] = useUrlState('_a');
|
||||
const pageState = appState?.[pageKey];
|
||||
|
||||
const resultPageState: PageUrlState = useMemo(() => {
|
||||
return {
|
||||
...(defaultState ?? {}),
|
||||
...(pageState ?? {}),
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pageState]);
|
||||
|
||||
const onStateUpdate = useCallback(
|
||||
(update: Partial<PageUrlState>, replaceState?: boolean) => {
|
||||
setAppState(
|
||||
pageKey,
|
||||
{
|
||||
...resultPageState,
|
||||
...update,
|
||||
},
|
||||
replaceState
|
||||
);
|
||||
},
|
||||
[pageKey, resultPageState, setAppState]
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
return [resultPageState, onStateUpdate];
|
||||
}, [resultPageState, onStateUpdate]);
|
||||
};
|
|
@ -5,17 +5,18 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { css } from '@emotion/react';
|
||||
import { flatten } from 'lodash';
|
||||
import React, { FC, useState, useEffect } from 'react';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiSpacer, EuiTitle } from '@elastic/eui';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { css } from '@emotion/react';
|
||||
import { flatten } from 'lodash';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { LinkCardProps } from '../../../common/components/link_card/link_card';
|
||||
import { useDataVisualizerKibana } from '../../../kibana_context';
|
||||
import { useUrlState } from '../../../common/util/url_state';
|
||||
import { LinkCard } from '../../../common/components/link_card';
|
||||
import { GetAdditionalLinks } from '../../../common/components/results_links';
|
||||
import { isDefined } from '../../../common/util/is_defined';
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
import React, { FC, useEffect, useMemo, useState, useCallback, useRef } from 'react';
|
||||
import type { Required } from 'utility-types';
|
||||
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
|
@ -18,15 +20,16 @@ import {
|
|||
EuiSpacer,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { Required } from 'utility-types';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Filter, FilterStateStore, Query } from '@kbn/es-query';
|
||||
import { generateFilters } from '@kbn/data-plugin/public';
|
||||
import { DataView, DataViewField } from '@kbn/data-views-plugin/public';
|
||||
import { usePageUrlState, useUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { useCurrentEuiTheme } from '../../../common/hooks/use_current_eui_theme';
|
||||
import { DV_RANDOM_SAMPLER_PREFERENCE, useStorage } from '../../hooks/use_storage';
|
||||
import { FullTimeRangeSelector } from '../full_time_range_selector';
|
||||
import { usePageUrlState, useUrlState } from '../../../common/util/url_state';
|
||||
import {
|
||||
DataVisualizerTable,
|
||||
ItemIdToExpandedRowMap,
|
||||
|
@ -36,7 +39,10 @@ import type { TotalFieldsStats } from '../../../common/components/stats_table/co
|
|||
import { OverallStats } from '../../types/overall_stats';
|
||||
import { IndexBasedDataVisualizerExpandedRow } from '../../../common/components/expanded_row/index_based_expanded_row';
|
||||
import { DATA_VISUALIZER_INDEX_VIEWER } from '../../constants/index_data_visualizer_viewer';
|
||||
import { DataVisualizerIndexBasedAppState } from '../../types/index_data_visualizer_state';
|
||||
import {
|
||||
DataVisualizerIndexBasedAppState,
|
||||
DataVisualizerIndexBasedPageUrlState,
|
||||
} from '../../types/index_data_visualizer_state';
|
||||
import { SEARCH_QUERY_LANGUAGE, SearchQueryLanguage } from '../../types/combined_query';
|
||||
import { SupportedFieldType, SavedSearchSavedObject } from '../../../../../common/types';
|
||||
import { useDataVisualizerKibana } from '../../../kibana_context';
|
||||
|
@ -140,10 +146,11 @@ export const IndexDataVisualizerView: FC<IndexDataVisualizerViewProps> = (dataVi
|
|||
const { notifications, uiSettings, data } = services;
|
||||
const { toasts } = notifications;
|
||||
|
||||
const [dataVisualizerListState, setDataVisualizerListState] = usePageUrlState(
|
||||
DATA_VISUALIZER_INDEX_VIEWER,
|
||||
restorableDefaults
|
||||
);
|
||||
const [dataVisualizerListState, setDataVisualizerListState] =
|
||||
usePageUrlState<DataVisualizerIndexBasedPageUrlState>(
|
||||
DATA_VISUALIZER_INDEX_VIEWER,
|
||||
restorableDefaults
|
||||
);
|
||||
const [globalState, setGlobalState] = useUrlState('_g');
|
||||
|
||||
const [currentSavedSearch, setCurrentSavedSearch] = useState(
|
||||
|
|
|
@ -14,6 +14,7 @@ import { type DataViewField, UI_SETTINGS } from '@kbn/data-plugin/common';
|
|||
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types';
|
||||
import seedrandom from 'seedrandom';
|
||||
import type { SamplingOption } from '@kbn/discover-plugin/public/application/main/components/field_stats_table/field_stats_table';
|
||||
import type { Dictionary } from '@kbn/ml-url-state';
|
||||
import type { RandomSamplerOption } from '../constants/random_sampler';
|
||||
import type { DataVisualizerIndexBasedAppState } from '../types/index_data_visualizer_state';
|
||||
import { useDataVisualizerKibana } from '../../kibana_context';
|
||||
|
@ -36,7 +37,6 @@ import { getDefaultPageState } from '../components/index_data_visualizer_view/in
|
|||
import { useFieldStatsSearchStrategy } from './use_field_stats';
|
||||
import { useOverallStats } from './use_overall_stats';
|
||||
import type { OverallStatsSearchStrategyParams } from '../../../../common/types/field_stats';
|
||||
import type { Dictionary } from '../../common/util/url_state';
|
||||
import type { AggregatableField, NonAggregatableField } from '../types/overall_stats';
|
||||
|
||||
const defaults = getDefaultPageState();
|
||||
|
|
|
@ -9,26 +9,26 @@ import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
|||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { parse, stringify } from 'query-string';
|
||||
import { isEqual, throttle } from 'lodash';
|
||||
import { EuiResizeObserver } from '@elastic/eui';
|
||||
import { encode } from '@kbn/rison';
|
||||
import { SimpleSavedObject } from '@kbn/core/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { EuiResizeObserver } from '@elastic/eui';
|
||||
import { getNestedProperty } from '@kbn/ml-nested-property';
|
||||
import {
|
||||
Provider as UrlStateContextProvider,
|
||||
parseUrlState,
|
||||
isRisonSerializationRequired,
|
||||
type Accessor,
|
||||
type Dictionary,
|
||||
type SetUrlState,
|
||||
} from '@kbn/ml-url-state';
|
||||
import { getCoreStart, getPluginsStart } from '../../kibana_services';
|
||||
import {
|
||||
IndexDataVisualizerViewProps,
|
||||
IndexDataVisualizerView,
|
||||
} from './components/index_data_visualizer_view';
|
||||
import {
|
||||
Accessor,
|
||||
Provider as UrlStateContextProvider,
|
||||
Dictionary,
|
||||
parseUrlState,
|
||||
SetUrlState,
|
||||
getNestedProperty,
|
||||
isRisonSerializationRequired,
|
||||
} from '../common/util/url_state';
|
||||
import { useDataVisualizerKibana } from '../kibana_context';
|
||||
import { GetAdditionalLinks } from '../common/components/results_links';
|
||||
import { DATA_VISUALIZER_APP_LOCATOR, IndexDataVisualizerLocatorParams } from './locator';
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Filter, TimeRange } from '@kbn/es-query';
|
|||
import type { RefreshInterval } from '@kbn/data-plugin/common';
|
||||
import { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/common';
|
||||
import { GlobalQueryStateFromUrl } from '@kbn/data-plugin/public';
|
||||
import { Dictionary, isRisonSerializationRequired } from '../../common/util/url_state';
|
||||
import { type Dictionary, isRisonSerializationRequired } from '@kbn/ml-url-state';
|
||||
import { SearchQueryLanguage } from '../types/combined_query';
|
||||
|
||||
export const DATA_VISUALIZER_APP_LOCATOR = 'DATA_VISUALIZER_APP_LOCATOR';
|
||||
|
|
|
@ -9,6 +9,14 @@ import type { Filter } from '@kbn/es-query';
|
|||
import type { Query } from '@kbn/data-plugin/common/query';
|
||||
import type { RandomSamplerOption } from '../constants/random_sampler';
|
||||
import type { SearchQueryLanguage } from './combined_query';
|
||||
|
||||
import type { DATA_VISUALIZER_INDEX_VIEWER } from '../constants/index_data_visualizer_viewer';
|
||||
|
||||
export interface DataVisualizerIndexBasedPageUrlState {
|
||||
pageKey: typeof DATA_VISUALIZER_INDEX_VIEWER;
|
||||
pageUrlState: Required<DataVisualizerIndexBasedAppState>;
|
||||
}
|
||||
|
||||
export interface ListingPageUrlState {
|
||||
pageSize: number;
|
||||
pageIndex: number;
|
||||
|
@ -16,6 +24,7 @@ export interface ListingPageUrlState {
|
|||
sortDirection: string;
|
||||
queryText?: string;
|
||||
}
|
||||
|
||||
export interface DataVisualizerIndexBasedAppState extends Omit<ListingPageUrlState, 'queryText'> {
|
||||
searchString?: Query['query'];
|
||||
searchQuery?: Query['query'];
|
||||
|
|
|
@ -50,6 +50,8 @@
|
|||
"@kbn/ml-agg-utils",
|
||||
"@kbn/test-jest-helpers",
|
||||
"@kbn/field-types",
|
||||
"@kbn/ml-nested-property",
|
||||
"@kbn/ml-url-state",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -17,6 +17,7 @@ import React, { Component } from 'react';
|
|||
import { EuiFlexGroup, EuiFlexItem, EuiInMemoryTable, EuiText } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { getColumns } from './anomalies_table_columns';
|
||||
|
||||
|
@ -26,7 +27,6 @@ import { mlTableService } from '../../services/table_service';
|
|||
import { RuleEditorFlyout } from '../rule_editor';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { INFLUENCERS_LIMIT, ANOMALIES_TABLE_TABS, MAX_CHARS } from './anomalies_table_constants';
|
||||
import { usePageUrlState } from '../../util/url_state';
|
||||
|
||||
export class AnomaliesTableInternal extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -10,9 +10,9 @@ import React, { FC, useMemo } from 'react';
|
|||
import { EuiButtonGroup, EuiButtonGroupProps } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import type { ExplorerJob } from '../../explorer/explorer_utils';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
import { useMlLocator, useNavigateToPath } from '../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { mount } from 'enzyme';
|
|||
|
||||
import { EuiSelect } from '@elastic/eui';
|
||||
|
||||
import { UrlStateProvider } from '../../../util/url_state';
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
|
||||
import { SelectInterval } from './select_interval';
|
||||
|
||||
|
|
|
@ -8,7 +8,12 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EuiIcon, EuiSelect, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { usePageUrlState } from '../../../util/url_state';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
interface TableIntervalPageUrlState {
|
||||
pageKey: 'mlSelectInterval';
|
||||
pageUrlState: TableInterval;
|
||||
}
|
||||
|
||||
export interface TableInterval {
|
||||
display: string;
|
||||
|
@ -55,7 +60,7 @@ function optionValueToInterval(value: string) {
|
|||
export const TABLE_INTERVAL_DEFAULT = optionValueToInterval('auto');
|
||||
|
||||
export const useTableInterval = (): [TableInterval, (v: TableInterval) => void] => {
|
||||
const [interval, updateCallback] = usePageUrlState<TableInterval>(
|
||||
const [interval, updateCallback] = usePageUrlState<TableIntervalPageUrlState>(
|
||||
'mlSelectInterval',
|
||||
TABLE_INTERVAL_DEFAULT
|
||||
);
|
||||
|
|
|
@ -12,7 +12,7 @@ import { mount } from 'enzyme';
|
|||
|
||||
import { EuiSuperSelect } from '@elastic/eui';
|
||||
|
||||
import { UrlStateProvider } from '../../../util/url_state';
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
|
||||
import { SelectSeverity } from './select_severity';
|
||||
|
||||
|
|
|
@ -9,25 +9,26 @@
|
|||
* React component for rendering a select element with threshold levels.
|
||||
*/
|
||||
import React, { Fragment, FC, useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { EuiHealth, EuiSpacer, EuiSuperSelect, EuiText, EuiSuperSelectProps } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { getSeverityColor } from '../../../../../common/util/anomaly_utils';
|
||||
import { usePageUrlState } from '../../../util/url_state';
|
||||
import { ANOMALY_THRESHOLD } from '../../../../../common';
|
||||
|
||||
const warningLabel = i18n.translate('xpack.ml.controls.selectSeverity.warningLabel', {
|
||||
const warningLabel: string = i18n.translate('xpack.ml.controls.selectSeverity.warningLabel', {
|
||||
defaultMessage: 'warning',
|
||||
});
|
||||
const minorLabel = i18n.translate('xpack.ml.controls.selectSeverity.minorLabel', {
|
||||
const minorLabel: string = i18n.translate('xpack.ml.controls.selectSeverity.minorLabel', {
|
||||
defaultMessage: 'minor',
|
||||
});
|
||||
const majorLabel = i18n.translate('xpack.ml.controls.selectSeverity.majorLabel', {
|
||||
const majorLabel: string = i18n.translate('xpack.ml.controls.selectSeverity.majorLabel', {
|
||||
defaultMessage: 'major',
|
||||
});
|
||||
const criticalLabel = i18n.translate('xpack.ml.controls.selectSeverity.criticalLabel', {
|
||||
const criticalLabel: string = i18n.translate('xpack.ml.controls.selectSeverity.criticalLabel', {
|
||||
defaultMessage: 'critical',
|
||||
});
|
||||
|
||||
|
@ -38,6 +39,11 @@ const optionsMap = {
|
|||
[criticalLabel]: ANOMALY_THRESHOLD.CRITICAL,
|
||||
};
|
||||
|
||||
export interface TableSeverityPageUrlState {
|
||||
pageKey: 'mlSelectSeverity';
|
||||
pageUrlState: TableSeverity;
|
||||
}
|
||||
|
||||
export interface TableSeverity {
|
||||
val: number;
|
||||
display: string;
|
||||
|
@ -82,7 +88,7 @@ export function optionValueToThreshold(value: number) {
|
|||
const TABLE_SEVERITY_DEFAULT = SEVERITY_OPTIONS[0];
|
||||
|
||||
export const useTableSeverity = () => {
|
||||
return usePageUrlState<TableSeverity>('mlSelectSeverity', TABLE_SEVERITY_DEFAULT);
|
||||
return usePageUrlState<TableSeverityPageUrlState>('mlSelectSeverity', TABLE_SEVERITY_DEFAULT);
|
||||
};
|
||||
|
||||
export const getSeverityOptions = () =>
|
||||
|
|
|
@ -15,9 +15,9 @@ import { i18n } from '@kbn/i18n';
|
|||
|
||||
import { CoreSetup } from '@kbn/core/public';
|
||||
|
||||
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types';
|
||||
|
||||
import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
|
||||
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types';
|
||||
import { getNestedProperty } from '@kbn/ml-nested-property';
|
||||
|
||||
import { DEFAULT_RESULTS_FIELD } from '../../../../common/constants/data_frame_analytics';
|
||||
import { extractErrorMessage } from '../../../../common/util/errors';
|
||||
|
@ -39,7 +39,6 @@ import {
|
|||
TOP_CLASSES,
|
||||
} from '../../data_frame_analytics/common/constants';
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../../../common/util/date_utils';
|
||||
import { getNestedProperty } from '../../util/object_utils';
|
||||
import { mlFieldFormatService } from '../../services/field_format_service';
|
||||
|
||||
import { DataGridItem, IndexPagination, RenderCellValue } from './types';
|
||||
|
|
|
@ -16,10 +16,10 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import './_index.scss';
|
||||
|
||||
import { Dictionary } from '../../../../common/types/common';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
import { IdBadges } from './id_badges';
|
||||
import {
|
||||
BADGE_LIMIT,
|
||||
|
|
|
@ -9,11 +9,10 @@ import { difference } from 'lodash';
|
|||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs';
|
||||
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
|
||||
import { useNotifications } from '../../contexts/kibana';
|
||||
import { useJobSelectionFlyout } from '../../contexts/ml/use_job_selection_flyout';
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ import { i18n } from '@kbn/i18n';
|
|||
import type { EuiSideNavItemType } from '@elastic/eui';
|
||||
import React, { ReactNode, useCallback, useMemo } from 'react';
|
||||
import { AIOPS_ENABLED, CHANGE_POINT_DETECTION_ENABLED } from '@kbn/aiops-plugin/common';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { NotificationsIndicator } from './notifications_indicator';
|
||||
import type { MlLocatorParams } from '../../../../common/types/locator';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
import { useMlLocator, useNavigateToPath } from '../../contexts/kibana';
|
||||
import { isFullLicense } from '../../license';
|
||||
import type { MlRoute } from '../../routing';
|
||||
|
|
|
@ -11,7 +11,8 @@ import React from 'react';
|
|||
|
||||
import { EuiSuperDatePicker } from '@elastic/eui';
|
||||
|
||||
import { useUrlState } from '../../../util/url_state';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service';
|
||||
import { useToastNotificationService } from '../../../services/toast_notification_service';
|
||||
|
||||
|
@ -34,7 +35,7 @@ jest.mock('@elastic/eui', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('../../../util/url_state', () => {
|
||||
jest.mock('@kbn/ml-url-state', () => {
|
||||
return {
|
||||
useUrlState: jest.fn(() => {
|
||||
return [{ refreshInterval: { value: 0, pause: true } }, jest.fn()];
|
||||
|
|
|
@ -24,8 +24,8 @@ import { TimeHistoryContract } from '@kbn/data-plugin/public';
|
|||
import { UI_SETTINGS } from '@kbn/data-plugin/common';
|
||||
|
||||
import { wrapWithTheme, toMountPoint } from '@kbn/kibana-react-plugin/public';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { mlTimefilterRefresh$ } from '../../../services/timefilter_refresh_service';
|
||||
import { useUrlState } from '../../../util/url_state';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
import {
|
||||
useRefreshIntervalUpdates,
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { LocatorGetUrlParams } from '@kbn/share-plugin/common/url_service';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { useMlKibana } from './kibana_context';
|
||||
import { ML_APP_LOCATOR } from '../../../../common/constants/locator';
|
||||
import { MlLocatorParams } from '../../../../common/types/locator';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
|
||||
export const useMlLocator = () => {
|
||||
const {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import { usePageUrlState } from '../../../../util/url_state';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { ML_PAGES } from '../../../../../../common/constants/locator';
|
||||
import { ExplorationPageUrlState } from '../../../../../../common/types/locator';
|
||||
import { SEARCH_QUERY_LANGUAGE } from '../../../../../../common/constants/search';
|
||||
|
@ -28,8 +28,13 @@ export function getDefaultExplorationPageUrlState(
|
|||
};
|
||||
}
|
||||
|
||||
interface UsePageUrlState {
|
||||
pageKey: typeof ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION;
|
||||
pageUrlState: ExplorationPageUrlState;
|
||||
}
|
||||
|
||||
export function useExplorationUrlState(overrides?: Partial<ExplorationPageUrlState>) {
|
||||
return usePageUrlState<ExplorationPageUrlState>(
|
||||
return usePageUrlState<UsePageUrlState>(
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION,
|
||||
getDefaultExplorationPageUrlState(overrides)
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { FC, useState, useEffect } from 'react';
|
|||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { OutlierExploration } from './components/outlier_exploration';
|
||||
import { RegressionExploration } from './components/regression_exploration';
|
||||
import { ClassificationExploration } from './components/classification_exploration';
|
||||
|
@ -24,7 +25,6 @@ import {
|
|||
AnalyticsIdSelectorControls,
|
||||
} from '../components/analytics_selector';
|
||||
import { AnalyticsEmptyPrompt } from '../analytics_management/components/empty_prompt';
|
||||
import { useUrlState } from '../../../util/url_state';
|
||||
import { SavedObjectsWarning } from '../../../components/saved_objects_warning';
|
||||
|
||||
export const Page: FC<{
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { useMlLocator, useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { DataFrameAnalyticsListAction, DataFrameAnalyticsListRow } from '../analytics_list/common';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
import { getViewLinkStatus } from '../action_view/get_view_link_status';
|
||||
import { useUrlState } from '../../../../../util/url_state';
|
||||
|
||||
import { mapActionButtonText, MapButton } from './map_button';
|
||||
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { useUrlState } from '../../../../../util/url_state';
|
||||
|
||||
import {
|
||||
DEFAULT_REFRESH_INTERVAL_MS,
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import React, { useState, FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
EuiCallOut,
|
||||
|
@ -16,14 +15,12 @@ import {
|
|||
} from '@elastic/eui';
|
||||
|
||||
import type { SimpleSavedObject } from '@kbn/core/public';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getNestedProperty } from '@kbn/ml-nested-property';
|
||||
import { SavedObjectFinderUi } from '@kbn/saved-objects-plugin/public';
|
||||
|
||||
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
|
||||
import { useToastNotificationService } from '../../../../../services/toast_notification_service';
|
||||
|
||||
import { getNestedProperty } from '../../../../../util/object_utils';
|
||||
|
||||
import { getDataViewAndSavedSearch, isCcsIndexPattern } from '../../../../../util/index_utils';
|
||||
|
||||
const fixedPageSize: number = 20;
|
||||
|
|
|
@ -9,14 +9,13 @@ import React, { FC, useMemo, useState } from 'react';
|
|||
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useUrlState } from '../../../util/url_state';
|
||||
import { useUrlState, usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { DataFrameAnalyticsList } from './components/analytics_list';
|
||||
import { useRefreshInterval } from './components/analytics_list/use_refresh_interval';
|
||||
import { NodeAvailableWarning } from '../../../components/node_available_warning';
|
||||
import { SavedObjectsWarning } from '../../../components/saved_objects_warning';
|
||||
import { UpgradeWarning } from '../../../components/upgrade';
|
||||
import { JobMap } from '../job_map';
|
||||
import { usePageUrlState } from '../../../util/url_state';
|
||||
import { ListingPageUrlState } from '../../../../../common/types/common';
|
||||
import { DataFrameAnalyticsListColumn } from './components/analytics_list/common';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
|
@ -25,6 +24,11 @@ import { useMlKibana } from '../../../contexts/kibana';
|
|||
import { useRefreshAnalyticsList } from '../../common';
|
||||
import { MlPageHeader } from '../../../components/page_header';
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
export const getDefaultDFAListState = (): ListingPageUrlState => ({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
|
@ -36,7 +40,7 @@ export const Page: FC = () => {
|
|||
const [blockRefresh, setBlockRefresh] = useState(false);
|
||||
const [globalState] = useUrlState('_g');
|
||||
|
||||
const [dfaPageState, setDfaPageState] = usePageUrlState(
|
||||
const [dfaPageState, setDfaPageState] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE,
|
||||
getDefaultDFAListState()
|
||||
);
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { FC, useState, useEffect, useCallback } from 'react';
|
|||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useUrlState } from '../../../util/url_state';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { NodeAvailableWarning } from '../../../components/node_available_warning';
|
||||
import { SavedObjectsWarning } from '../../../components/saved_objects_warning';
|
||||
import { UpgradeWarning } from '../../../components/upgrade';
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs';
|
||||
import { distinctUntilChanged, map, skipWhile, switchMap } from 'rxjs/operators';
|
||||
import type { PageUrlStateService } from '@kbn/ml-url-state';
|
||||
import { StateService } from '../services/state_service';
|
||||
import type { AnomalyExplorerCommonStateService } from './anomaly_explorer_common_state';
|
||||
import type { AnomalyTimelineStateService } from './anomaly_timeline_state_service';
|
||||
|
@ -16,7 +17,6 @@ import {
|
|||
} from './explorer_charts/explorer_charts_container_service';
|
||||
import { AnomalyExplorerChartsService } from '../services/anomaly_explorer_charts_service';
|
||||
import { getSelectionInfluencers, getSelectionJobIds } from './explorer_utils';
|
||||
import type { PageUrlStateService } from '../util/url_state';
|
||||
import type { TableSeverity } from '../components/controls/select_severity/select_severity';
|
||||
import { AnomalyExplorerUrlStateService } from './hooks/use_explorer_url_state';
|
||||
|
||||
|
|
|
@ -5,21 +5,30 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { PageUrlStateService, usePageUrlState } from '../../util/url_state';
|
||||
import { PageUrlStateService, usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { ExplorerAppState } from '../../../../common/types/locator';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
|
||||
export type AnomalyExplorerUrlStateService = PageUrlStateService<ExplorerAppState>;
|
||||
|
||||
interface LegacyExplorerPageUrlState {
|
||||
pageKey: 'mlExplorerSwimlane';
|
||||
pageUrlState: ExplorerAppState['mlExplorerSwimlane'];
|
||||
}
|
||||
|
||||
interface ExplorerPageUrlState {
|
||||
pageKey: typeof ML_PAGES.ANOMALY_EXPLORER;
|
||||
pageUrlState: ExplorerAppState;
|
||||
}
|
||||
|
||||
export function useExplorerUrlState() {
|
||||
/**
|
||||
* Originally `mlExplorerSwimlane` resided directly in the app URL state (`_a` URL state key).
|
||||
* With current URL structure it has been moved under the `explorer` key of the app state (_a).
|
||||
*/
|
||||
const [legacyExplorerState] =
|
||||
usePageUrlState<ExplorerAppState['mlExplorerSwimlane']>('mlExplorerSwimlane');
|
||||
const [legacyExplorerState] = usePageUrlState<LegacyExplorerPageUrlState>('mlExplorerSwimlane');
|
||||
|
||||
return usePageUrlState<ExplorerAppState>(ML_PAGES.ANOMALY_EXPLORER, {
|
||||
return usePageUrlState<ExplorerPageUrlState>(ML_PAGES.ANOMALY_EXPLORER, {
|
||||
mlExplorerSwimlane: legacyExplorerState,
|
||||
mlExplorerFilter: {},
|
||||
});
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
// @ts-ignore
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { JobsListView } from './components/jobs_list_view';
|
||||
import { usePageUrlState } from '../../util/url_state';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import { ListingPageUrlState } from '../../../../common/types/common';
|
||||
import { HelpMenu } from '../../components/help_menu';
|
||||
|
@ -18,6 +17,11 @@ import { MlPageHeader } from '../../components/page_header';
|
|||
import { HeaderMenuPortal } from '../../components/header_menu_portal';
|
||||
import { JobsActionMenu } from '../components/jobs_action_menu';
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
interface JobsPageProps {
|
||||
isMlEnabledInSpace?: boolean;
|
||||
lastRefresh?: number;
|
||||
|
@ -31,7 +35,7 @@ export const getDefaultAnomalyDetectionJobsListState = (): ListingPageUrlState =
|
|||
});
|
||||
|
||||
export const JobsPage: FC<JobsPageProps> = ({ isMlEnabledInSpace, lastRefresh }) => {
|
||||
const [pageState, setPageState] = usePageUrlState(
|
||||
const [pageState, setPageState] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE,
|
||||
getDefaultAnomalyDetectionJobsListState()
|
||||
);
|
||||
|
|
|
@ -23,6 +23,7 @@ import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/bas
|
|||
import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
|
||||
import useDebounce from 'react-use/lib/useDebounce';
|
||||
import useMount from 'react-use/lib/useMount';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { EntityFilter } from './entity_filter';
|
||||
import { useMlNotifications } from '../../contexts/ml/ml_notifications_context';
|
||||
import { ML_NOTIFICATIONS_MESSAGE_LEVEL } from '../../../../common/constants/notifications';
|
||||
|
@ -33,7 +34,6 @@ import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter';
|
|||
import { useRefresh } from '../../routing/use_refresh';
|
||||
import { useTableSettings } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings';
|
||||
import { ListingPageUrlState } from '../../../../common/types/common';
|
||||
import { usePageUrlState } from '../../util/url_state';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import type {
|
||||
MlNotificationMessageLevel,
|
||||
|
@ -47,6 +47,11 @@ const levelBadgeMap: Record<MlNotificationMessageLevel, IconColor> = {
|
|||
[ML_NOTIFICATIONS_MESSAGE_LEVEL.INFO]: 'default',
|
||||
};
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.NOTIFICATIONS;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
export const getDefaultNotificationsListState = (): ListingPageUrlState => ({
|
||||
pageIndex: 0,
|
||||
pageSize: 25,
|
||||
|
@ -81,7 +86,7 @@ export const NotificationsList: FC = () => {
|
|||
|
||||
const dateFormatter = useFieldFormatter(FIELD_FORMAT_IDS.DATE);
|
||||
|
||||
const [pageState, updatePageState] = usePageUrlState(
|
||||
const [pageState, updatePageState] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.NOTIFICATIONS,
|
||||
getDefaultNotificationsListState()
|
||||
);
|
||||
|
|
|
@ -18,9 +18,9 @@ import type {
|
|||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
|
||||
import { EuiLoadingContent } from '@elastic/eui';
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
import { MlNotificationsContextProvider } from '../contexts/ml/ml_notifications_context';
|
||||
import { MlContext, MlContextValue } from '../contexts/ml';
|
||||
import { UrlStateProvider } from '../util/url_state';
|
||||
|
||||
import { MlPage } from '../components/ml_page';
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { FC } from 'react';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { MlRoute, PageLoader, PageProps } from '../../router';
|
||||
|
@ -17,7 +18,6 @@ import { basicResolvers } from '../../resolvers';
|
|||
import { Page } from '../../../data_frame_analytics/pages/analytics_exploration';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataFrameAnalysisConfigType } from '../../../../../common/types/data_frame_analytics';
|
||||
import { useUrlState } from '../../../util/url_state';
|
||||
|
||||
export const analyticsJobExplorationRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
|
|
@ -13,6 +13,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { NavigateToPath, useMlKibana, useTimefilter } from '../../contexts/kibana';
|
||||
|
||||
import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs';
|
||||
|
@ -30,7 +31,6 @@ import { getDateFormatTz } from '../../explorer/explorer_utils';
|
|||
import { useJobSelection } from '../../components/job_selector/use_job_selection';
|
||||
import { useTableInterval } from '../../components/controls/select_interval';
|
||||
import { useTableSeverity } from '../../components/controls/select_severity';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { JOB_ID } from '../../../../common/constants/anomalies';
|
||||
import { MlAnnotationUpdatesContext } from '../../contexts/ml/ml_annotation_updates_context';
|
||||
|
|
|
@ -43,7 +43,16 @@ const MockedTimeseriesexplorerNoJobsFound = TimeseriesexplorerNoJobsFound as jes
|
|||
typeof TimeseriesexplorerNoJobsFound
|
||||
>;
|
||||
|
||||
jest.mock('../../util/url_state');
|
||||
jest.mock('@kbn/ml-url-state', () => {
|
||||
return {
|
||||
usePageUrlState: jest.fn(() => {
|
||||
return [{}, jest.fn(), {}];
|
||||
}),
|
||||
useUrlState: jest.fn(() => {
|
||||
return [{ refreshInterval: { value: 0, pause: true } }, jest.fn()];
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state');
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import moment from 'moment';
|
|||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { getViewableDetectors } from '../../timeseriesexplorer/timeseriesexplorer_utils/get_viewable_detectors';
|
||||
import { NavigateToPath, useNotifications } from '../../contexts/kibana';
|
||||
import { useMlContext } from '../../contexts/ml';
|
||||
|
@ -31,7 +32,6 @@ import {
|
|||
} from '../../timeseriesexplorer/timeseriesexplorer_utils';
|
||||
import { TimeSeriesExplorerPage } from '../../timeseriesexplorer/timeseriesexplorer_page';
|
||||
import { TimeseriesexplorerNoJobsFound } from '../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found';
|
||||
import { useUrlState } from '../../util/url_state';
|
||||
import { useTableInterval } from '../../components/controls/select_interval';
|
||||
import { useTableSeverity } from '../../components/controls/select_severity';
|
||||
|
||||
|
|
|
@ -5,10 +5,15 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { usePageUrlState } from '../../util/url_state';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { TimeSeriesExplorerAppState } from '../../../../common/types/locator';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
|
||||
export function useTimeSeriesExplorerUrlState() {
|
||||
return usePageUrlState<TimeSeriesExplorerAppState>(ML_PAGES.SINGLE_METRIC_VIEWER);
|
||||
interface TimeSeriesExplorerPageUrlState {
|
||||
pageKey: typeof ML_PAGES.SINGLE_METRIC_VIEWER;
|
||||
pageUrlState: TimeSeriesExplorerAppState;
|
||||
}
|
||||
|
||||
export function useTimeSeriesExplorerUrlState() {
|
||||
return usePageUrlState<TimeSeriesExplorerPageUrlState>(ML_PAGES.SINGLE_METRIC_VIEWER);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/bas
|
|||
import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types';
|
||||
import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { useModelActions } from './model_actions';
|
||||
import { ModelsTableToConfigMapping } from '.';
|
||||
import { ModelsBarStats, StatsBar } from '../../components/stats_bar';
|
||||
|
@ -39,7 +40,6 @@ import { BUILT_IN_MODEL_TAG } from '../../../../common/constants/data_frame_anal
|
|||
import { DeleteModelsModal } from './delete_models_modal';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import { ListingPageUrlState } from '../../../../common/types/common';
|
||||
import { usePageUrlState } from '../../util/url_state';
|
||||
import { ExpandedRow } from './expanded_row';
|
||||
import { useTableSettings } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings';
|
||||
import { useToastNotificationService } from '../../services/toast_notification_service';
|
||||
|
@ -59,6 +59,11 @@ export type ModelItem = TrainedModelConfigResponse & {
|
|||
|
||||
export type ModelItemFull = Required<ModelItem>;
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.TRAINED_MODELS_MANAGE;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
export const getDefaultModelsListState = (): ListingPageUrlState => ({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
|
@ -88,7 +93,7 @@ export const ModelsList: FC<Props> = ({
|
|||
// allow for an internally controlled page state which stores the state in the URL
|
||||
// or an external page state, which is passed in as a prop.
|
||||
// external page state is used on the management page.
|
||||
const [pageStateInternal, updatePageStateInternal] = usePageUrlState(
|
||||
const [pageStateInternal, updatePageStateInternal] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.TRAINED_MODELS_MANAGE,
|
||||
getDefaultModelsListState()
|
||||
);
|
||||
|
|
|
@ -17,9 +17,9 @@ import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/bas
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
|
||||
import { usePageUrlState } from '@kbn/ml-url-state';
|
||||
import { ModelsBarStats, StatsBar } from '../../components/stats_bar';
|
||||
import { NodeDeploymentStatsResponse } from '../../../../common/types/trained_models';
|
||||
import { usePageUrlState } from '../../util/url_state';
|
||||
import { ML_PAGES } from '../../../../common/constants/locator';
|
||||
import { useTrainedModelsApiService } from '../../services/ml_api_service/trained_models';
|
||||
import { useTableSettings } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings';
|
||||
|
@ -32,6 +32,11 @@ import { useRefresh } from '../../routing/use_refresh';
|
|||
|
||||
export type NodeItem = NodeDeploymentStatsResponse;
|
||||
|
||||
interface PageUrlState {
|
||||
pageKey: typeof ML_PAGES.TRAINED_MODELS_NODES;
|
||||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
export const getDefaultNodesListState = (): ListingPageUrlState => ({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
|
@ -55,7 +60,7 @@ export const NodesList: FC<NodesListProps> = ({ compactView = false }) => {
|
|||
const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState<Record<string, JSX.Element>>(
|
||||
{}
|
||||
);
|
||||
const [pageState, updatePageState] = usePageUrlState(
|
||||
const [pageState, updatePageState] = usePageUrlState<PageUrlState>(
|
||||
ML_PAGES.TRAINED_MODELS_NODES,
|
||||
getDefaultNodesListState()
|
||||
);
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { AppStateKey } from '../url_state';
|
||||
import { TABLE_INTERVAL_DEFAULT } from '../../components/controls/select_interval/select_interval';
|
||||
|
||||
export const useUrlState = jest.fn((accessor: '_a' | '_g') => {
|
||||
|
@ -14,7 +13,7 @@ export const useUrlState = jest.fn((accessor: '_a' | '_g') => {
|
|||
}
|
||||
});
|
||||
|
||||
export const usePageUrlState = jest.fn((pageKey: AppStateKey) => {
|
||||
export const usePageUrlState = jest.fn((pageKey: string) => {
|
||||
let state: unknown;
|
||||
switch (pageKey) {
|
||||
case 'timeseriesexplorer':
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
/*
|
||||
* 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 { getNestedProperty } from './object_utils';
|
||||
|
||||
describe('object_utils', () => {
|
||||
test('getNestedProperty()', () => {
|
||||
const testObj = {
|
||||
the: {
|
||||
nested: {
|
||||
value: 'the-nested-value',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const falseyObj = {
|
||||
the: {
|
||||
nested: {
|
||||
value: false,
|
||||
},
|
||||
other_nested: {
|
||||
value: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const test1 = getNestedProperty(testObj, 'the');
|
||||
expect(typeof test1).toBe('object');
|
||||
expect(Object.keys(test1)).toStrictEqual(['nested']);
|
||||
|
||||
const test2 = getNestedProperty(testObj, 'the$');
|
||||
expect(typeof test2).toBe('undefined');
|
||||
|
||||
const test3 = getNestedProperty(testObj, 'the$', 'the-default-value');
|
||||
expect(typeof test3).toBe('string');
|
||||
expect(test3).toBe('the-default-value');
|
||||
|
||||
const test4 = getNestedProperty(testObj, 'the.neSted');
|
||||
expect(typeof test4).toBe('undefined');
|
||||
|
||||
const test5 = getNestedProperty(testObj, 'the.nested');
|
||||
expect(typeof test5).toBe('object');
|
||||
expect(Object.keys(test5)).toStrictEqual(['value']);
|
||||
|
||||
const test6 = getNestedProperty(testObj, 'the.nested.vaLue');
|
||||
expect(typeof test6).toBe('undefined');
|
||||
|
||||
const test7 = getNestedProperty(testObj, 'the.nested.value');
|
||||
expect(typeof test7).toBe('string');
|
||||
expect(test7).toBe('the-nested-value');
|
||||
|
||||
const test8 = getNestedProperty(testObj, 'the.nested.value.doesntExist');
|
||||
expect(typeof test8).toBe('undefined');
|
||||
|
||||
const test9 = getNestedProperty(testObj, 'the.nested.value.doesntExist', 'the-default-value');
|
||||
expect(typeof test9).toBe('string');
|
||||
expect(test9).toBe('the-default-value');
|
||||
|
||||
const test10 = getNestedProperty(falseyObj, 'the.nested.value');
|
||||
expect(typeof test10).toBe('boolean');
|
||||
expect(test10).toBe(false);
|
||||
|
||||
const test11 = getNestedProperty(falseyObj, 'the.other_nested.value');
|
||||
expect(typeof test11).toBe('number');
|
||||
expect(test11).toBe(0);
|
||||
});
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// This is similar to lodash's get() except that it's TypeScript aware and is able to infer return types.
|
||||
// It splits the attribute key string and uses reduce with an idx check to access nested attributes.
|
||||
export const getNestedProperty = (
|
||||
obj: Record<string, any>,
|
||||
accessor: string,
|
||||
defaultValue?: any
|
||||
) => {
|
||||
const value = accessor.split('.').reduce((o, i) => o?.[i], obj);
|
||||
|
||||
if (value === undefined) return defaultValue;
|
||||
|
||||
return value;
|
||||
};
|
|
@ -66,6 +66,8 @@
|
|||
"@kbn/task-manager-plugin",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/repo-info",
|
||||
"@kbn/ml-url-state",
|
||||
"@kbn/ml-nested-property",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import { EuiComboBoxOptionOption } from '@elastic/eui';
|
||||
import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types';
|
||||
import { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { getNestedProperty } from '@kbn/ml-nested-property';
|
||||
|
||||
import { getNestedProperty } from '../../../../../../../common/utils/object_utils';
|
||||
import { removeKeywordPostfix } from '../../../../../../../common/utils/field_utils';
|
||||
|
||||
import { isRuntimeMappings } from '../../../../../../../common/shared_imports';
|
||||
|
|
|
@ -5,13 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isEqual } from 'lodash';
|
||||
import { merge } from 'lodash';
|
||||
import { numberValidator } from '@kbn/ml-agg-utils';
|
||||
|
||||
import { isEqual, merge } from 'lodash';
|
||||
import { useReducer } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { numberValidator } from '@kbn/ml-agg-utils';
|
||||
import { getNestedProperty, setNestedProperty } from '@kbn/ml-nested-property';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
|
||||
import { PostTransformsUpdateRequestSchema } from '../../../../../../common/api_schemas/update_transforms';
|
||||
|
@ -20,7 +19,6 @@ import {
|
|||
DEFAULT_TRANSFORM_SETTINGS_MAX_PAGE_SEARCH_SIZE,
|
||||
} from '../../../../../../common/constants';
|
||||
import { TransformConfigUnion } from '../../../../../../common/types/transform';
|
||||
import { getNestedProperty, setNestedProperty } from '../../../../../../common/utils/object_utils';
|
||||
|
||||
import {
|
||||
isValidFrequency,
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"@kbn/ml-string-hash",
|
||||
"@kbn/ui-theme",
|
||||
"@kbn/field-types",
|
||||
"@kbn/ml-nested-property",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -3741,7 +3741,7 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/ml-is-populated-object@link:x-pack/packages/ml/is_populated_object":
|
||||
"@kbn/ml-nested-property@link:x-pack/packages/ml/nested_property":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
|
@ -3749,6 +3749,14 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/ml-url-state@link:x-pack/packages/ml/url_state":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/ml-is-populated-object@link:x-pack/packages/ml/is_populated_object":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/monaco@link:packages/kbn-monaco":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue