From fe21356020ee9567e3164869bae768c1220db130 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Fri, 14 Feb 2020 14:14:39 -0500 Subject: [PATCH 01/65] [Endpoint] Policy List UI route and initial view (#56918) * Initial Policy List view * Add `endpoint/policy` route and displays Policy List * test cases (both unit and functional) Does not yet interact with API (Ingest). --- x-pack/plugins/endpoint/common/types.ts | 2 +- .../endpoint/components/truncate_text.ts | 13 + .../public/applications/endpoint/index.tsx | 2 + .../applications/endpoint/store/action.ts | 3 +- .../applications/endpoint/store/index.ts | 5 + .../endpoint/store/policy_list/action.ts | 27 ++ .../endpoint/store/policy_list/fake_data.ts | 53 ++++ .../endpoint/store/policy_list/index.test.ts | 74 ++++++ .../endpoint/store/policy_list/index.ts | 9 + .../endpoint/store/policy_list/middleware.ts | 45 ++++ .../endpoint/store/policy_list/reducer.ts | 48 ++++ .../endpoint/store/policy_list/selectors.ts | 17 ++ .../applications/endpoint/store/reducer.ts | 2 + .../endpoint/store/routing/action.ts | 7 +- .../public/applications/endpoint/types.ts | 29 +++ .../endpoint/view/policy/index.ts | 7 + .../endpoint/view/policy/policy_hooks.ts | 12 + .../endpoint/view/policy/policy_list.tsx | 232 ++++++++++++++++++ .../applications/endpoint/view/use_page_id.ts | 10 +- x-pack/test/functional/apps/endpoint/index.ts | 1 + .../functional/apps/endpoint/policy_list.ts | 47 ++++ .../functional/page_objects/endpoint_page.ts | 19 ++ 22 files changed, 660 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts create mode 100644 x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx create mode 100644 x-pack/test/functional/apps/endpoint/policy_list.ts diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index 0dc3fc29ca805..5ef9d22e4dd7b 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -118,4 +118,4 @@ export interface EndpointMetadata { /** * The PageId type is used for the payload when firing userNavigatedToPage actions */ -export type PageId = 'alertsPage' | 'managementPage'; +export type PageId = 'alertsPage' | 'managementPage' | 'policyListPage'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts b/x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts new file mode 100644 index 0000000000000..83f4bc1e79317 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/components/truncate_text.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import styled from 'styled-components'; + +export const TruncateText = styled.div` + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +`; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx index a86c647e771d4..7bb3b13525914 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx @@ -14,6 +14,7 @@ import { Store } from 'redux'; import { appStoreFactory } from './store'; import { AlertIndex } from './view/alerts'; import { ManagementList } from './view/managing'; +import { PolicyList } from './view/policy'; /** * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle. @@ -51,6 +52,7 @@ const AppRoot: React.FunctionComponent = React.memo(({ basename, st /> + ( diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts index 04c6cf7fc4634..d099c81317090 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/action.ts @@ -7,5 +7,6 @@ import { ManagementAction } from './managing'; import { AlertAction } from './alerts'; import { RoutingAction } from './routing'; +import { PolicyListAction } from './policy_list'; -export type AppAction = ManagementAction | AlertAction | RoutingAction; +export type AppAction = ManagementAction | AlertAction | RoutingAction | PolicyListAction; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts index 3bbcc3f25a6d8..8fe61ae01d319 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/index.ts @@ -17,6 +17,7 @@ import { CoreStart } from 'kibana/public'; import { appReducer } from './reducer'; import { alertMiddlewareFactory } from './alerts/middleware'; import { managementMiddlewareFactory } from './managing'; +import { policyListMiddlewareFactory } from './policy_list'; import { GlobalState } from '../types'; import { AppAction } from './action'; @@ -56,6 +57,10 @@ export const appStoreFactory = (coreStart: CoreStart): Store => { substateMiddlewareFactory( globalState => globalState.managementList, managementMiddlewareFactory(coreStart) + ), + substateMiddlewareFactory( + globalState => globalState.policyList, + policyListMiddlewareFactory(coreStart) ) ) ) diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts new file mode 100644 index 0000000000000..5ac2a4328b00a --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/action.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PolicyData } from '../../types'; + +interface ServerReturnedPolicyListData { + type: 'serverReturnedPolicyListData'; + payload: { + policyItems: PolicyData[]; + total: number; + pageSize: number; + pageIndex: number; + }; +} + +interface UserPaginatedPolicyListTable { + type: 'userPaginatedPolicyListTable'; + payload: { + pageSize: number; + pageIndex: number; + }; +} + +export type PolicyListAction = ServerReturnedPolicyListData | UserPaginatedPolicyListTable; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts new file mode 100644 index 0000000000000..62bdd28f30be1 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/fake_data.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +// !!!! Should be deleted when https://github.com/elastic/endpoint-app-team/issues/150 +// is implemented + +const dateOffsets = [ + 0, + 1000, + 300000, // 5 minutes + 3.6e6, // 1 hour + 86340000, // 23h, 59m + 9e7, // 25h + 9e7 * 5, // 5d +]; + +const randomNumbers = [5, 50, 500, 5000, 50000]; + +const getRandomDateIsoString = () => { + const randomIndex = Math.floor(Math.random() * Math.floor(dateOffsets.length)); + return new Date(Date.now() - dateOffsets[randomIndex]).toISOString(); +}; + +const getRandomNumber = () => { + const randomIndex = Math.floor(Math.random() * Math.floor(randomNumbers.length)); + return randomNumbers[randomIndex]; +}; + +export const getFakeDatasourceApiResponse = async (page: number, pageSize: number) => { + await new Promise(resolve => setTimeout(resolve, 500)); + + // Emulates the API response - see PR: + // https://github.com/elastic/kibana/pull/56567/files#diff-431549a8739efe0c56763f164c32caeeR25 + return { + items: Array.from({ length: pageSize }, (x, i) => ({ + name: `policy with some protections ${i + 1}`, + total: getRandomNumber(), + pending: getRandomNumber(), + failed: getRandomNumber(), + created_by: `admin ABC`, + created: getRandomDateIsoString(), + updated_by: 'admin 123', + updated: getRandomDateIsoString(), + })), + success: true, + total: pageSize * 10, + page, + perPage: pageSize, + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts new file mode 100644 index 0000000000000..ae4a0868a68fe --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PolicyListState } from '../../types'; +import { applyMiddleware, createStore, Dispatch, Store } from 'redux'; +import { AppAction } from '../action'; +import { policyListReducer } from './reducer'; +import { policyListMiddlewareFactory } from './middleware'; +import { coreMock } from '../../../../../../../../src/core/public/mocks'; +import { CoreStart } from 'kibana/public'; +import { selectIsLoading } from './selectors'; + +describe('policy list store concerns', () => { + const sleep = () => new Promise(resolve => setTimeout(resolve, 1000)); + let fakeCoreStart: jest.Mocked; + let store: Store; + let getState: typeof store['getState']; + let dispatch: Dispatch; + + beforeEach(() => { + fakeCoreStart = coreMock.createStart({ basePath: '/mock' }); + store = createStore( + policyListReducer, + applyMiddleware(policyListMiddlewareFactory(fakeCoreStart)) + ); + getState = store.getState; + dispatch = store.dispatch; + }); + + test('it sets `isLoading` when `userNavigatedToPage`', async () => { + expect(selectIsLoading(getState())).toBe(false); + dispatch({ type: 'userNavigatedToPage', payload: 'policyListPage' }); + expect(selectIsLoading(getState())).toBe(true); + await sleep(); + expect(selectIsLoading(getState())).toBe(false); + }); + + test('it sets `isLoading` when `userPaginatedPolicyListTable`', async () => { + expect(selectIsLoading(getState())).toBe(false); + dispatch({ + type: 'userPaginatedPolicyListTable', + payload: { + pageSize: 10, + pageIndex: 1, + }, + }); + expect(selectIsLoading(getState())).toBe(true); + await sleep(); + expect(selectIsLoading(getState())).toBe(false); + }); + + test('it resets state on `userNavigatedFromPage` action', async () => { + dispatch({ + type: 'serverReturnedPolicyListData', + payload: { + policyItems: [], + pageIndex: 20, + pageSize: 50, + total: 200, + }, + }); + dispatch({ type: 'userNavigatedFromPage', payload: 'policyListPage' }); + expect(getState()).toEqual({ + policyItems: [], + isLoading: false, + pageIndex: 0, + pageSize: 10, + total: 0, + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts new file mode 100644 index 0000000000000..8086acc41d2bd --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/index.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { policyListReducer } from './reducer'; +export { PolicyListAction } from './action'; +export { policyListMiddlewareFactory } from './middleware'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts new file mode 100644 index 0000000000000..f8e2b7d07c389 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/middleware.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MiddlewareFactory, PolicyListState } from '../../types'; + +export const policyListMiddlewareFactory: MiddlewareFactory = coreStart => { + return ({ getState, dispatch }) => next => async action => { + next(action); + + if ( + (action.type === 'userNavigatedToPage' && action.payload === 'policyListPage') || + action.type === 'userPaginatedPolicyListTable' + ) { + const state = getState(); + let pageSize: number; + let pageIndex: number; + + if (action.type === 'userPaginatedPolicyListTable') { + pageSize = action.payload.pageSize; + pageIndex = action.payload.pageIndex; + } else { + pageSize = state.pageSize; + pageIndex = state.pageIndex; + } + + // Need load data from API and remove fake data below + // Refactor tracked via: https://github.com/elastic/endpoint-app-team/issues/150 + const { getFakeDatasourceApiResponse } = await import('./fake_data'); + const { items: policyItems, total } = await getFakeDatasourceApiResponse(pageIndex, pageSize); + + dispatch({ + type: 'serverReturnedPolicyListData', + payload: { + policyItems, + pageIndex, + pageSize, + total, + }, + }); + } + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts new file mode 100644 index 0000000000000..77f536d413ae3 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/reducer.ts @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Reducer } from 'redux'; +import { PolicyListState } from '../../types'; +import { AppAction } from '../action'; + +const initialPolicyListState = (): PolicyListState => { + return { + policyItems: [], + isLoading: false, + pageIndex: 0, + pageSize: 10, + total: 0, + }; +}; + +export const policyListReducer: Reducer = ( + state = initialPolicyListState(), + action +) => { + if (action.type === 'serverReturnedPolicyListData') { + return { + ...state, + ...action.payload, + isLoading: false, + }; + } + + if ( + action.type === 'userPaginatedPolicyListTable' || + (action.type === 'userNavigatedToPage' && action.payload === 'policyListPage') + ) { + return { + ...state, + isLoading: true, + }; + } + + if (action.type === 'userNavigatedFromPage' && action.payload === 'policyListPage') { + return initialPolicyListState(); + } + + return state; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts new file mode 100644 index 0000000000000..b9c2edbf5d55b --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_list/selectors.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PolicyListState } from '../../types'; + +export const selectPolicyItems = (state: PolicyListState) => state.policyItems; + +export const selectPageIndex = (state: PolicyListState) => state.pageIndex; + +export const selectPageSize = (state: PolicyListState) => state.pageSize; + +export const selectTotal = (state: PolicyListState) => state.total; + +export const selectIsLoading = (state: PolicyListState) => state.isLoading; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts index 7d738c266fae0..3d9d21c0da9c3 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/reducer.ts @@ -8,8 +8,10 @@ import { managementListReducer } from './managing'; import { AppAction } from './action'; import { alertListReducer } from './alerts'; import { GlobalState } from '../types'; +import { policyListReducer } from './policy_list'; export const appReducer: Reducer = combineReducers({ managementList: managementListReducer, alertList: alertListReducer, + policyList: policyListReducer, }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts index 263a3f72d57d5..9080af8c91817 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/routing/action.ts @@ -11,4 +11,9 @@ interface UserNavigatedToPage { readonly payload: PageId; } -export type RoutingAction = UserNavigatedToPage; +interface UserNavigatedFromPage { + readonly type: 'userNavigatedFromPage'; + readonly payload: PageId; +} + +export type RoutingAction = UserNavigatedToPage | UserNavigatedFromPage; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts index 02a7793fc38b0..6b20012592fd9 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/types.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/types.ts @@ -29,9 +29,38 @@ export interface ManagementListPagination { pageSize: number; } +// REFACTOR to use Types from Ingest Manager - see: https://github.com/elastic/endpoint-app-team/issues/150 +export interface PolicyData { + name: string; + total: number; + pending: number; + failed: number; + created_by: string; + created: string; + updated_by: string; + updated: string; +} + +/** + * Policy list store state + */ +export interface PolicyListState { + /** Array of policy items */ + policyItems: PolicyData[]; + /** total number of policies */ + total: number; + /** Number of policies per page */ + pageSize: number; + /** page number (zero based) */ + pageIndex: number; + /** data is being retrieved from server */ + isLoading: boolean; +} + export interface GlobalState { readonly managementList: ManagementListState; readonly alertList: AlertListState; + readonly policyList: PolicyListState; } export type AlertListData = AlertResultList; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts new file mode 100644 index 0000000000000..d561da7574de0 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './policy_list'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts new file mode 100644 index 0000000000000..14558fb6504bb --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_hooks.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useSelector } from 'react-redux'; +import { GlobalState, PolicyListState } from '../../types'; + +export function usePolicyListSelector(selector: (state: PolicyListState) => TSelected) { + return useSelector((state: GlobalState) => selector(state.policyList)); +} diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx new file mode 100644 index 0000000000000..75ffa5e8806e9 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_list.tsx @@ -0,0 +1,232 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback, useMemo } from 'react'; +import { + EuiPage, + EuiPageBody, + EuiPageContent, + EuiPageContentBody, + EuiPageContentHeader, + EuiPageContentHeaderSection, + EuiTitle, + EuiBasicTable, + EuiText, + EuiTableFieldDataColumnType, + EuiToolTip, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { + FormattedMessage, + FormattedDate, + FormattedTime, + FormattedNumber, + FormattedRelative, +} from '@kbn/i18n/react'; +import { useDispatch } from 'react-redux'; +import styled from 'styled-components'; +import { usePageId } from '../use_page_id'; +import { + selectIsLoading, + selectPageIndex, + selectPageSize, + selectPolicyItems, + selectTotal, +} from '../../store/policy_list/selectors'; +import { usePolicyListSelector } from './policy_hooks'; +import { PolicyListAction } from '../../store/policy_list'; +import { PolicyData } from '../../types'; +import { TruncateText } from '../../components/truncate_text'; + +interface TableChangeCallbackArguments { + page: { index: number; size: number }; +} + +const TruncateTooltipText = styled(TruncateText)` + .euiToolTipAnchor { + display: block; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } +`; + +const FormattedDateAndTime: React.FC<{ date: Date }> = ({ date }) => { + // If date is greater than or equal to 24h (ago), then show it as a date + // else, show it as relative to "now" + return Date.now() - date.getTime() >= 8.64e7 ? ( + <> + + {' @'} + + + ) : ( + <> + + + ); +}; + +const renderDate = (date: string, _item: PolicyData) => ( + + + + + +); + +const renderFormattedNumber = (value: number, _item: PolicyData) => ( + + + +); + +export const PolicyList = React.memo(() => { + usePageId('policyListPage'); + + const dispatch = useDispatch<(action: PolicyListAction) => void>(); + const policyItems = usePolicyListSelector(selectPolicyItems); + const pageIndex = usePolicyListSelector(selectPageIndex); + const pageSize = usePolicyListSelector(selectPageSize); + const totalItemCount = usePolicyListSelector(selectTotal); + const loading = usePolicyListSelector(selectIsLoading); + + const paginationSetup = useMemo(() => { + return { + pageIndex, + pageSize, + totalItemCount, + pageSizeOptions: [10, 20, 50], + hidePerPageOptions: false, + }; + }, [pageIndex, pageSize, totalItemCount]); + + const handleTableChange = useCallback( + ({ page: { index, size } }: TableChangeCallbackArguments) => { + dispatch({ + type: 'userPaginatedPolicyListTable', + payload: { + pageIndex: index, + pageSize: size, + }, + }); + }, + [dispatch] + ); + + const columns: Array> = useMemo( + () => [ + { + field: 'name', + name: i18n.translate('xpack.endpoint.policyList.nameField', { + defaultMessage: 'Policy Name', + }), + truncateText: true, + }, + { + field: 'total', + name: i18n.translate('xpack.endpoint.policyList.totalField', { + defaultMessage: 'Total', + }), + render: renderFormattedNumber, + dataType: 'number', + truncateText: true, + width: '15ch', + }, + { + field: 'pending', + name: i18n.translate('xpack.endpoint.policyList.pendingField', { + defaultMessage: 'Pending', + }), + render: renderFormattedNumber, + dataType: 'number', + truncateText: true, + width: '15ch', + }, + { + field: 'failed', + name: i18n.translate('xpack.endpoint.policyList.failedField', { + defaultMessage: 'Failed', + }), + render: renderFormattedNumber, + dataType: 'number', + truncateText: true, + width: '15ch', + }, + { + field: 'created_by', + name: i18n.translate('xpack.endpoint.policyList.createdByField', { + defaultMessage: 'Created By', + }), + truncateText: true, + }, + { + field: 'created', + name: i18n.translate('xpack.endpoint.policyList.createdField', { + defaultMessage: 'Created', + }), + render: renderDate, + truncateText: true, + }, + { + field: 'updated_by', + name: i18n.translate('xpack.endpoint.policyList.updatedByField', { + defaultMessage: 'Last Updated By', + }), + truncateText: true, + }, + { + field: 'updated', + name: i18n.translate('xpack.endpoint.policyList.updatedField', { + defaultMessage: 'Last Updated', + }), + render: renderDate, + truncateText: true, + }, + ], + [] + ); + + return ( + + + + + + +

+ +

+
+

+ + + +

+
+
+ + + +
+
+
+ ); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts b/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts index 9e241af4c0445..49c39064c8d9a 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/use_page_id.ts @@ -10,11 +10,19 @@ import { PageId } from '../../../../common/types'; import { RoutingAction } from '../store/routing'; /** - * Dispatches a 'userNavigatedToPage' action with the given 'pageId' as the action payload + * Dispatches a 'userNavigatedToPage' action with the given 'pageId' as the action payload. + * When the component is un-mounted, a `userNavigatedFromPage` action will be dispatched + * with the given `pageId`. + * + * @param pageId A page id */ export function usePageId(pageId: PageId) { const dispatch: (action: RoutingAction) => unknown = useDispatch(); useEffect(() => { dispatch({ type: 'userNavigatedToPage', payload: pageId }); + + return () => { + dispatch({ type: 'userNavigatedFromPage', payload: pageId }); + }; }, [dispatch, pageId]); } diff --git a/x-pack/test/functional/apps/endpoint/index.ts b/x-pack/test/functional/apps/endpoint/index.ts index 5fdf54b98cda6..0ea9344a67aba 100644 --- a/x-pack/test/functional/apps/endpoint/index.ts +++ b/x-pack/test/functional/apps/endpoint/index.ts @@ -12,5 +12,6 @@ export default function({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./feature_controls')); loadTestFile(require.resolve('./landing_page')); loadTestFile(require.resolve('./management')); + loadTestFile(require.resolve('./policy_list')); }); } diff --git a/x-pack/test/functional/apps/endpoint/policy_list.ts b/x-pack/test/functional/apps/endpoint/policy_list.ts new file mode 100644 index 0000000000000..1fe2492bed5a0 --- /dev/null +++ b/x-pack/test/functional/apps/endpoint/policy_list.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getPageObjects, getService }: FtrProviderContext) { + const pageObjects = getPageObjects(['common', 'endpoint']); + const testSubjects = getService('testSubjects'); + + describe('Endpoint Policy List', function() { + this.tags(['ciGroup7']); + before(async () => { + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/policy'); + }); + + it('loads the Policy List Page', async () => { + await testSubjects.existOrFail('policyListPage'); + }); + it('displays page title', async () => { + const policyTitle = await testSubjects.getVisibleText('policyViewTitle'); + expect(policyTitle).to.equal('Policies'); + }); + it('shows policy count total', async () => { + const policyTotal = await testSubjects.getVisibleText('policyTotalCount'); + expect(policyTotal).to.equal('0 Policies'); + }); + it('includes policy list table', async () => { + await testSubjects.existOrFail('policyTable'); + }); + it('has correct table headers', async () => { + const allHeaderCells = await pageObjects.endpoint.tableHeaderVisibleText('policyTable'); + expect(allHeaderCells).to.eql([ + 'Policy Name', + 'Total', + 'Pending', + 'Failed', + 'Created By', + 'Created', + 'Last Updated By', + 'Last Updated', + ]); + }); + }); +} diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts index a306a855a83eb..54f537dd0e8c3 100644 --- a/x-pack/test/functional/page_objects/endpoint_page.ts +++ b/x-pack/test/functional/page_objects/endpoint_page.ts @@ -11,6 +11,25 @@ export function EndpointPageProvider({ getService }: FtrProviderContext) { const table = getService('table'); return { + /** + * Finds the Table with the given `selector` (test subject) and returns + * back an array containing the table's header column text + * + * @param selector + * @returns Promise + */ + async tableHeaderVisibleText(selector: string) { + const $ = await (await testSubjects.find('policyTable')).parseDomContent(); + return $('thead tr th') + .toArray() + .map(th => + $(th) + .text() + .replace(/ /g, '') + .trim() + ); + }, + async welcomeEndpointTitle() { return await testSubjects.getVisibleText('welcomeTitle'); }, From 52a566d095756883168fc54fe9afa502ff695af2 Mon Sep 17 00:00:00 2001 From: gchaps <33642766+gchaps@users.noreply.github.com> Date: Fri, 14 Feb 2020 11:33:25 -0800 Subject: [PATCH 02/65] [DOCS] Adds link to migration guide (#57702) --- docs/developer/plugin/development-plugin-resources.asciidoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/developer/plugin/development-plugin-resources.asciidoc b/docs/developer/plugin/development-plugin-resources.asciidoc index 71c442aaf52e8..a2fd0e23d0be4 100644 --- a/docs/developer/plugin/development-plugin-resources.asciidoc +++ b/docs/developer/plugin/development-plugin-resources.asciidoc @@ -66,3 +66,8 @@ To enable TypeScript support, create a `tsconfig.json` file at the root of your TypeScript code is automatically converted into JavaScript during development, but not in the distributable version of Kibana. If you use the {repo}blob/{branch}/packages/kbn-plugin-helpers[@kbn/plugin-helpers] to build your plugin, then your `.ts` and `.tsx` files will be permanently transpiled before your plugin is archived. If you have your own build process, make sure to run the TypeScript compiler on your source files and ship the compilation output so that your plugin will work with the distributable version of Kibana. + +==== {kib} platform migration guide + +{repo}blob/{branch}/src/core/MIGRATION.md#migrating-legacy-plugins-to-the-new-platform[This guide] +provides an action plan for moving a legacy plugin to the new platform. From 34ae99b516f7aede7e108cad7cb1fb8d82a7de0f Mon Sep 17 00:00:00 2001 From: Tim Sullivan Date: Fri, 14 Feb 2020 12:36:08 -0700 Subject: [PATCH 03/65] [Reporting/New Platform] Provide async access to server-side dependencies (#56824) * [Reporting/New Platform] Provide async access to server-side * consistent name for reportingPlugin * Prettier changes * simplify reporting usage collector setup * add more tests * extract internals access to separate core class * fix tests * fix imports for jest and build Co-authored-by: Elastic Machine --- .../execute_job/decrypt_job_headers.test.ts | 2 +- .../get_conditional_headers.test.ts | 59 ++- .../execute_job/get_custom_logo.test.ts | 29 +- .../common/execute_job/get_custom_logo.ts | 18 +- .../common/execute_job/get_full_urls.test.ts | 2 +- .../common/lib/screenshots/index.ts | 18 +- .../export_types/csv/server/create_job.ts | 9 +- .../csv/server/execute_job.test.js | 354 +++++++++++------- .../export_types/csv/server/execute_job.ts | 133 +++---- .../csv/server/lib/field_format_map.ts | 10 +- .../server/create_job/create_job.ts | 4 +- .../server/execute_job.ts | 8 +- .../server/lib/generate_csv.ts | 11 +- .../server/lib/generate_csv_search.ts | 47 +-- .../png/server/create_job/index.ts | 13 +- .../png/server/execute_job/index.test.js | 47 ++- .../png/server/execute_job/index.ts | 16 +- .../printable_pdf/server/create_job/index.ts | 11 +- .../server/execute_job/index.test.js | 60 +-- .../printable_pdf/server/execute_job/index.ts | 18 +- x-pack/legacy/plugins/reporting/index.ts | 2 +- .../legacy/plugins/reporting/reporting.d.ts | 2 +- .../legacy/plugins/reporting/server/core.ts | 122 ++++++ .../legacy/plugins/reporting/server/index.ts | 3 + .../legacy/plugins/reporting/server/legacy.ts | 9 +- .../reporting/server/lib/create_queue.ts | 31 +- .../server/lib/create_worker.test.ts | 38 +- .../reporting/server/lib/create_worker.ts | 36 +- .../reporting/server/lib/enqueue_job.ts | 21 +- .../plugins/reporting/server/lib/get_user.ts | 2 +- .../reporting/server/lib/validate/index.ts | 5 +- .../legacy/plugins/reporting/server/plugin.ts | 109 ++---- .../server/routes/generate_from_jobparams.ts | 2 +- .../routes/generate_from_savedobject.ts | 2 +- .../generate_from_savedobject_immediate.ts | 10 +- .../reporting/server/routes/generation.ts | 26 +- .../plugins/reporting/server/routes/index.ts | 16 +- .../reporting/server/routes/jobs.test.js | 128 +++++-- .../plugins/reporting/server/routes/jobs.ts | 6 +- .../routes/lib/authorized_user_pre_routing.ts | 2 +- .../lib/reporting_feature_pre_routing.ts | 2 +- .../routes/lib/route_config_factories.ts | 2 +- .../plugins/reporting/server/types.d.ts | 50 +++ .../server/usage/decorate_range_stats.ts | 2 +- .../usage/reporting_usage_collector.test.js | 230 +++++++----- .../server/usage/reporting_usage_collector.ts | 25 +- .../create_mock_reportingplugin.ts | 53 +++ .../test_helpers/create_mock_server.ts | 7 - .../plugins/reporting/test_helpers/index.ts | 8 + x-pack/legacy/plugins/reporting/types.d.ts | 36 +- 50 files changed, 1109 insertions(+), 747 deletions(-) create mode 100644 x-pack/legacy/plugins/reporting/server/core.ts create mode 100644 x-pack/legacy/plugins/reporting/server/types.d.ts create mode 100644 x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts create mode 100644 x-pack/legacy/plugins/reporting/test_helpers/index.ts diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts index 1b7ba3c90bab1..468caf93ec5dd 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.test.ts @@ -5,7 +5,7 @@ */ import { cryptoFactory } from '../../../server/lib/crypto'; -import { createMockServer } from '../../../test_helpers/create_mock_server'; +import { createMockServer } from '../../../test_helpers'; import { Logger } from '../../../types'; import { decryptJobHeaders } from './decrypt_job_headers'; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts index 070bdb4314af9..eedb742ad7597 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_conditional_headers.test.ts @@ -4,13 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; -import { getConditionalHeaders, getCustomLogo } from './index'; +import { createMockReportingCore, createMockServer } from '../../../test_helpers'; +import { ReportingCore } from '../../../server'; import { JobDocPayload } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { getConditionalHeaders, getCustomLogo } from './index'; +let mockReportingPlugin: ReportingCore; let mockServer: any; -beforeEach(() => { +beforeEach(async () => { + mockReportingPlugin = await createMockReportingCore(); mockServer = createMockServer(''); }); @@ -148,56 +151,76 @@ describe('conditions', () => { }); test('uses basePath from job when creating saved object service', async () => { + const mockGetSavedObjectsClient = jest.fn(); + mockReportingPlugin.getSavedObjectsClient = mockGetSavedObjectsClient; + const permittedHeaders = { foo: 'bar', baz: 'quix', }; - const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, }); - - const logo = 'custom-logo'; - mockServer.uiSettingsServiceFactory().get.mockReturnValue(logo); - const jobBasePath = '/sbp/s/marketing'; await getCustomLogo({ + reporting: mockReportingPlugin, job: { basePath: jobBasePath } as JobDocPayloadPDF, conditionalHeaders, server: mockServer, }); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.mock.calls[0][0].getBasePath()).toBe( - jobBasePath - ); + const getBasePath = mockGetSavedObjectsClient.mock.calls[0][0].getBasePath; + expect(getBasePath()).toBe(jobBasePath); }); test(`uses basePath from server if job doesn't have a basePath when creating saved object service`, async () => { + const mockGetSavedObjectsClient = jest.fn(); + mockReportingPlugin.getSavedObjectsClient = mockGetSavedObjectsClient; + const permittedHeaders = { foo: 'bar', baz: 'quix', }; - const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayload, filteredHeaders: permittedHeaders, server: mockServer, }); - const logo = 'custom-logo'; - mockServer.uiSettingsServiceFactory().get.mockReturnValue(logo); - await getCustomLogo({ + reporting: mockReportingPlugin, job: {} as JobDocPayloadPDF, conditionalHeaders, server: mockServer, }); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.mock.calls[0][0].getBasePath()).toBe( - '/sbp' - ); + const getBasePath = mockGetSavedObjectsClient.mock.calls[0][0].getBasePath; + expect(getBasePath()).toBe(`/sbp`); + expect(mockGetSavedObjectsClient.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "getBasePath": [Function], + "headers": Object { + "baz": "quix", + "foo": "bar", + }, + "path": "/", + "raw": Object { + "req": Object { + "url": "/", + }, + }, + "route": Object { + "settings": Object {}, + }, + "url": Object { + "href": "/", + }, + }, + ] + `); }); describe('config formatting', () => { diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts index ff2c44026315d..fa53f474dfba7 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.test.ts @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; -import { getConditionalHeaders, getCustomLogo } from './index'; +import { ReportingCore } from '../../../server'; +import { createMockReportingCore, createMockServer } from '../../../test_helpers'; +import { ServerFacade } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; +import { getConditionalHeaders, getCustomLogo } from './index'; -let mockServer: any; -beforeEach(() => { +let mockReportingPlugin: ReportingCore; +let mockServer: ServerFacade; +beforeEach(async () => { + mockReportingPlugin = await createMockReportingCore(); mockServer = createMockServer(''); }); @@ -19,6 +23,17 @@ test(`gets logo from uiSettings`, async () => { baz: 'quix', }; + const mockGet = jest.fn(); + mockGet.mockImplementationOnce((...args: any[]) => { + if (args[0] === 'xpackReporting:customPdfLogo') { + return 'purple pony'; + } + throw new Error('wrong caller args!'); + }); + mockReportingPlugin.getUiSettingsServiceFactory = jest.fn().mockResolvedValue({ + get: mockGet, + }); + const conditionalHeaders = await getConditionalHeaders({ job: {} as JobDocPayloadPDF, filteredHeaders: permittedHeaders, @@ -26,12 +41,12 @@ test(`gets logo from uiSettings`, async () => { }); const { logo } = await getCustomLogo({ + reporting: mockReportingPlugin, job: {} as JobDocPayloadPDF, conditionalHeaders, server: mockServer, }); - mockServer.uiSettingsServiceFactory().get.mockReturnValue(logo); - - expect(mockServer.uiSettingsServiceFactory().get).toBeCalledWith('xpackReporting:customPdfLogo'); + expect(mockGet).toBeCalledWith('xpackReporting:customPdfLogo'); + expect(logo).toBe('purple pony'); }); diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts index 0059276f6df71..7af5edab41ab7 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_custom_logo.ts @@ -5,14 +5,17 @@ */ import { UI_SETTINGS_CUSTOM_PDF_LOGO } from '../../../common/constants'; +import { ReportingCore } from '../../../server'; import { ConditionalHeaders, ServerFacade } from '../../../types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; // Logo is PDF only export const getCustomLogo = async ({ + reporting, server, job, conditionalHeaders, }: { + reporting: ReportingCore; server: ServerFacade; job: JobDocPayloadPDF; conditionalHeaders: ConditionalHeaders; @@ -27,19 +30,12 @@ export const getCustomLogo = async ({ getBasePath: () => job.basePath || serverBasePath, path: '/', route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, + url: { href: '/' }, + raw: { req: { url: '/' } }, }; - const savedObjects = server.savedObjects; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient(fakeRequest); - const uiSettings = server.uiSettingsServiceFactory({ savedObjectsClient }); + const savedObjectsClient = await reporting.getSavedObjectsClient(fakeRequest); + const uiSettings = await reporting.getUiSettingsServiceFactory(savedObjectsClient); const logo: string = await uiSettings.get(UI_SETTINGS_CUSTOM_PDF_LOGO); return { conditionalHeaders, logo }; }; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts index 9b2a065427f70..27e772195f726 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/get_full_urls.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createMockServer } from '../../../test_helpers/create_mock_server'; +import { createMockServer } from '../../../test_helpers'; import { ServerFacade } from '../../../types'; import { JobDocPayloadPNG } from '../../png/types'; import { JobDocPayloadPDF } from '../../printable_pdf/types'; diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts index 9fd3ee391ddbb..62b5e29e88ecf 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts @@ -5,19 +5,19 @@ */ import * as Rx from 'rxjs'; -import { first, concatMap, take, toArray, mergeMap } from 'rxjs/operators'; -import { ServerFacade, CaptureConfig, HeadlessChromiumDriverFactory } from '../../../../types'; -import { ScreenshotResults, ScreenshotObservableOpts } from './types'; -import { injectCustomCss } from './inject_css'; -import { openUrl } from './open_url'; -import { waitForRenderComplete } from './wait_for_render'; -import { getNumberOfItems } from './get_number_of_items'; -import { waitForElementsToBeInDOM } from './wait_for_dom_elements'; -import { getTimeRange } from './get_time_range'; +import { concatMap, first, mergeMap, take, toArray } from 'rxjs/operators'; +import { CaptureConfig, HeadlessChromiumDriverFactory, ServerFacade } from '../../../../types'; import { getElementPositionAndAttributes } from './get_element_position_data'; +import { getNumberOfItems } from './get_number_of_items'; import { getScreenshots } from './get_screenshots'; +import { getTimeRange } from './get_time_range'; +import { injectCustomCss } from './inject_css'; +import { openUrl } from './open_url'; import { scanPage } from './scan_page'; import { skipTelemetry } from './skip_telemetry'; +import { ScreenshotObservableOpts, ScreenshotResults } from './types'; +import { waitForElementsToBeInDOM } from './wait_for_dom_elements'; +import { waitForRenderComplete } from './wait_for_render'; export function screenshotsObservableFactory( server: ServerFacade, diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts index 063ac7f77704c..7ea67277015ab 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/create_job.ts @@ -4,19 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ +import { ReportingCore } from '../../../server'; import { cryptoFactory } from '../../../server/lib/crypto'; import { - CreateJobFactory, ConditionalHeaders, - ServerFacade, - RequestFacade, + CreateJobFactory, ESQueueCreateJobFn, + RequestFacade, + ServerFacade, } from '../../../types'; import { JobParamsDiscoverCsv } from '../types'; export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { +>> = function createJobFactoryFn(reporting: ReportingCore, server: ServerFacade) { const crypto = cryptoFactory(server); return async function createJob( diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js index b21d628332027..f12916b734dbf 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.js @@ -9,6 +9,7 @@ import sinon from 'sinon'; import nodeCrypto from '@elastic/node-crypto'; import { CancellationToken } from '../../../common/cancellation_token'; import { fieldFormats } from '../../../../../../../src/plugins/data/server'; +import { createMockReportingCore } from '../../../test_helpers'; import { LevelLogger } from '../../../server/lib/level_logger'; import { executeJobFactory } from './execute_job'; import { setFieldFormats } from '../../../server/services'; @@ -36,16 +37,19 @@ describe('CSV Execute Job', function() { let encryptedHeaders; let cancellationToken; + let mockReportingPlugin; let mockServer; let clusterStub; let callAsCurrentUserStub; - let uiSettingsGetStub; const mockElasticsearch = { dataClient: { asScoped: () => clusterStub, }, }; + const mockUiSettingsClient = { + get: sinon.stub(), + }; beforeAll(async function() { const crypto = nodeCrypto({ encryptionKey }); @@ -53,6 +57,8 @@ describe('CSV Execute Job', function() { }); beforeEach(async function() { + mockReportingPlugin = await createMockReportingCore(); + mockReportingPlugin.getUiSettingsServiceFactory = () => mockUiSettingsClient; cancellationToken = new CancellationToken(); defaultElasticsearchResponse = { @@ -70,9 +76,8 @@ describe('CSV Execute Job', function() { .resolves(defaultElasticsearchResponse); const configGetStub = sinon.stub(); - uiSettingsGetStub = sinon.stub(); - uiSettingsGetStub.withArgs('csv:separator').returns(','); - uiSettingsGetStub.withArgs('csv:quoteValues').returns(true); + mockUiSettingsClient.get.withArgs('csv:separator').returns(','); + mockUiSettingsClient.get.withArgs('csv:quoteValues').returns(true); setFieldFormats({ fieldFormatServiceFactory: function() { @@ -90,26 +95,11 @@ describe('CSV Execute Job', function() { }); mockServer = { - expose: function() {}, - plugins: { - elasticsearch: { - getCluster: function() { - return clusterStub; - }, - }, - }, config: function() { return { get: configGetStub, }; }, - savedObjects: { - getScopedSavedObjectsClient: sinon.stub(), - }, - uiSettingsServiceFactory: sinon.stub().returns({ - get: uiSettingsGetStub, - }), - log: function() {}, }; mockServer .config() @@ -125,83 +115,14 @@ describe('CSV Execute Job', function() { .returns({}); }); - describe('calls getScopedSavedObjectsClient with request', function() { - it('containing decrypted headers', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - await executeJob( - 'job456', - { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, - cancellationToken - ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].headers).toEqual( - headers - ); - }); - - it(`containing getBasePath() returning server's basePath if the job doesn't have one`, async function() { - const serverBasePath = '/foo-server/basePath/'; - mockServer - .config() - .get.withArgs('server.basePath') - .returns(serverBasePath); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - await executeJob( - 'job456', - { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, - cancellationToken - ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); - expect( - mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].getBasePath() - ).toEqual(serverBasePath); - }); - - it(`containing getBasePath() returning job's basePath if the job has one`, async function() { - const serverBasePath = '/foo-server/basePath/'; - mockServer - .config() - .get.withArgs('server.basePath') - .returns(serverBasePath); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - const jobBasePath = 'foo-job/basePath/'; - await executeJob( - 'job789', - { - headers: encryptedHeaders, - fields: [], - searchRequest: { index: null, body: null }, - basePath: jobBasePath, - }, - cancellationToken - ); - expect(mockServer.savedObjects.getScopedSavedObjectsClient.calledOnce).toBe(true); - expect( - mockServer.savedObjects.getScopedSavedObjectsClient.firstCall.args[0].getBasePath() - ).toEqual(jobBasePath); - }); - }); - - describe('uiSettings', function() { - it('passed scoped SavedObjectsClient to uiSettingsServiceFactory', async function() { - const returnValue = Symbol(); - mockServer.savedObjects.getScopedSavedObjectsClient.returns(returnValue); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); - await executeJob( - 'job456', - { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, - cancellationToken - ); - expect(mockServer.uiSettingsServiceFactory.calledOnce).toBe(true); - expect(mockServer.uiSettingsServiceFactory.firstCall.args[0].savedObjectsClient).toBe( - returnValue - ); - }); - }); - describe('basic Elasticsearch call behavior', function() { it('should decrypt encrypted headers and pass to callAsCurrentUser', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -217,7 +138,12 @@ describe('CSV Execute Job', function() { testBody: true, }; - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const job = { headers: encryptedHeaders, fields: [], @@ -244,7 +170,12 @@ describe('CSV Execute Job', function() { _scroll_id: scrollId, }); callAsCurrentUserStub.onSecondCall().resolves(defaultElasticsearchResponse); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -258,7 +189,12 @@ describe('CSV Execute Job', function() { }); it('should not execute scroll if there are no hits from the search', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -288,7 +224,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -323,7 +264,12 @@ describe('CSV Execute Job', function() { _scroll_id: lastScrollId, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); await executeJob( 'job456', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -351,7 +297,12 @@ describe('CSV Execute Job', function() { _scroll_id: lastScrollId, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -381,7 +332,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -409,7 +365,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['=SUM(A1:A2)', 'two'], @@ -437,7 +398,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -465,7 +431,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -485,7 +456,12 @@ describe('CSV Execute Job', function() { describe('Elasticsearch call errors', function() { it('should reject Promise if search call errors out', async function() { callAsCurrentUserStub.rejects(new Error()); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -504,7 +480,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); callAsCurrentUserStub.onSecondCall().rejects(new Error()); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -525,7 +506,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -546,7 +532,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -574,7 +565,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -602,7 +598,12 @@ describe('CSV Execute Job', function() { _scroll_id: undefined, }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: [], @@ -638,7 +639,12 @@ describe('CSV Execute Job', function() { }); it('should stop calling Elasticsearch when cancellationToken.cancel is called', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -653,7 +659,12 @@ describe('CSV Execute Job', function() { }); it(`shouldn't call clearScroll if it never got a scrollId`, async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -667,7 +678,12 @@ describe('CSV Execute Job', function() { }); it('should call clearScroll if it got a scrollId', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); executeJob( 'job345', { headers: encryptedHeaders, fields: [], searchRequest: { index: null, body: null } }, @@ -685,7 +701,12 @@ describe('CSV Execute Job', function() { describe('csv content', function() { it('should write column headers to output, even if there are no results', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -696,8 +717,13 @@ describe('CSV Execute Job', function() { }); it('should use custom uiSettings csv:separator for header', async function() { - uiSettingsGetStub.withArgs('csv:separator').returns(';'); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + mockUiSettingsClient.get.withArgs('csv:separator').returns(';'); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -708,8 +734,13 @@ describe('CSV Execute Job', function() { }); it('should escape column headers if uiSettings csv:quoteValues is true', async function() { - uiSettingsGetStub.withArgs('csv:quoteValues').returns(true); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + mockUiSettingsClient.get.withArgs('csv:quoteValues').returns(true); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], @@ -720,8 +751,13 @@ describe('CSV Execute Job', function() { }); it(`shouldn't escape column headers if uiSettings csv:quoteValues is false`, async function() { - uiSettingsGetStub.withArgs('csv:quoteValues').returns(false); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + mockUiSettingsClient.get.withArgs('csv:quoteValues').returns(false); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one and a half', 'two', 'three-and-four', 'five & six'], @@ -732,7 +768,12 @@ describe('CSV Execute Job', function() { }); it('should write column headers to output, when there are results', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ one: '1', two: '2' }], @@ -752,7 +793,12 @@ describe('CSV Execute Job', function() { }); it('should use comma separated values of non-nested fields from _source', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -773,7 +819,12 @@ describe('CSV Execute Job', function() { }); it('should concatenate the hits from multiple responses', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -801,7 +852,12 @@ describe('CSV Execute Job', function() { }); it('should use field formatters to format fields', async function() { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); callAsCurrentUserStub.onFirstCall().resolves({ hits: { hits: [{ _source: { one: 'foo', two: 'bar' } }], @@ -846,7 +902,12 @@ describe('CSV Execute Job', function() { .get.withArgs('xpack.reporting.csv.maxSizeBytes') .returns(1); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -879,7 +940,12 @@ describe('CSV Execute Job', function() { .get.withArgs('xpack.reporting.csv.maxSizeBytes') .returns(9); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -919,7 +985,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -948,6 +1019,7 @@ describe('CSV Execute Job', function() { let maxSizeReached; beforeEach(async function() { + mockReportingPlugin.getUiSettingsServiceFactory = () => mockUiSettingsClient; mockServer .config() .get.withArgs('xpack.reporting.csv.maxSizeBytes') @@ -960,7 +1032,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1000,7 +1077,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1029,7 +1111,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], @@ -1058,7 +1145,12 @@ describe('CSV Execute Job', function() { _scroll_id: 'scrollId', }); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, mockLogger); + const executeJob = await executeJobFactory( + mockReportingPlugin, + mockServer, + mockElasticsearch, + mockLogger + ); const jobParams = { headers: encryptedHeaders, fields: ['one', 'two'], diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts index 9f94a755cf655..1579985891053 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts @@ -4,20 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import Hapi from 'hapi'; import { i18n } from '@kbn/i18n'; -import { ElasticsearchServiceSetup, KibanaRequest } from '../../../../../../../src/core/server'; +import Hapi from 'hapi'; +import { + ElasticsearchServiceSetup, + IUiSettingsClient, + KibanaRequest, +} from '../../../../../../../src/core/server'; import { CSV_JOB_TYPE } from '../../../common/constants'; +import { ReportingCore } from '../../../server'; import { cryptoFactory } from '../../../server/lib'; +import { getFieldFormats } from '../../../server/services'; import { ESQueueWorkerExecuteFn, ExecuteJobFactory, Logger, ServerFacade } from '../../../types'; import { JobDocPayloadDiscoverCsv } from '../types'; import { fieldFormatMapFactory } from './lib/field_format_map'; import { createGenerateCsv } from './lib/generate_csv'; -import { getFieldFormats } from '../../../server/services'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn( +>> = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, parentLogger: Logger @@ -40,83 +46,78 @@ export const executeJobFactory: ExecuteJobFactory { + let decryptedHeaders; + try { + decryptedHeaders = await crypto.decrypt(headers); + } catch (err) { + logger.error(err); + throw new Error( + i18n.translate( + 'xpack.reporting.exportTypes.csv.executeJob.failedToDecryptReportJobDataErrorMessage', + { + defaultMessage: 'Failed to decrypt report job data. Please ensure that {encryptionKey} is set and re-generate this report. {err}', + values: { encryptionKey: 'xpack.reporting.encryptionKey', err: err.toString() }, + } + ) + ); // prettier-ignore + } + return decryptedHeaders; + }; - const fakeRequest = { - headers: decryptedHeaders, + const fakeRequest = KibanaRequest.from({ + headers: await decryptHeaders(), // This is used by the spaces SavedObjectClientWrapper to determine the existing space. // We use the basePath from the saved job, which we'll have post spaces being implemented; // or we use the server base path, which uses the default space getBasePath: () => basePath || serverBasePath, path: '/', route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - }; + url: { href: '/' }, + raw: { req: { url: '/' } }, + } as Hapi.Request); + + const { callAsCurrentUser } = elasticsearch.dataClient.asScoped(fakeRequest); + const callEndpoint = (endpoint: string, clientParams = {}, options = {}) => + callAsCurrentUser(endpoint, clientParams, options); - const { callAsCurrentUser } = elasticsearch.dataClient.asScoped( - KibanaRequest.from(fakeRequest as Hapi.Request) - ); - const callEndpoint = (endpoint: string, clientParams = {}, options = {}) => { - return callAsCurrentUser(endpoint, clientParams, options); + const savedObjectsClient = await reporting.getSavedObjectsClient(fakeRequest); + const uiSettingsClient = await reporting.getUiSettingsServiceFactory(savedObjectsClient); + + const getFormatsMap = async (client: IUiSettingsClient) => { + const fieldFormats = await getFieldFormats().fieldFormatServiceFactory(client); + return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats); }; - const savedObjects = server.savedObjects; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient( - (fakeRequest as unknown) as KibanaRequest - ); - const uiConfig = server.uiSettingsServiceFactory({ - savedObjectsClient, - }); + const getUiSettings = async (client: IUiSettingsClient) => { + const [separator, quoteValues, timezone] = await Promise.all([ + client.get('csv:separator'), + client.get('csv:quoteValues'), + client.get('dateFormat:tz'), + ]); - const [formatsMap, uiSettings] = await Promise.all([ - (async () => { - const fieldFormats = await getFieldFormats().fieldFormatServiceFactory(uiConfig); - return fieldFormatMapFactory(indexPatternSavedObject, fieldFormats); - })(), - (async () => { - const [separator, quoteValues, timezone] = await Promise.all([ - uiConfig.get('csv:separator'), - uiConfig.get('csv:quoteValues'), - uiConfig.get('dateFormat:tz'), - ]); + if (timezone === 'Browser') { + logger.warn( + i18n.translate('xpack.reporting.exportTypes.csv.executeJob.dateFormateSetting', { + defaultMessage: 'Kibana Advanced Setting "{dateFormatTimezone}" is set to "Browser". Dates will be formatted as UTC to avoid ambiguity.', + values: { dateFormatTimezone: 'dateFormat:tz' } + }) + ); // prettier-ignore + } - if (timezone === 'Browser') { - jobLogger.warn( - `Kibana Advanced Setting "dateFormat:tz" is set to "Browser". Dates will be formatted as UTC to avoid ambiguity.` - ); - } + return { + separator, + quoteValues, + timezone, + }; + }; - return { - separator, - quoteValues, - timezone, - }; - })(), + const [formatsMap, uiSettings] = await Promise.all([ + getFormatsMap(uiSettingsClient), + getUiSettings(uiSettingsClient), ]); const generateCsv = createGenerateCsv(jobLogger); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts index e1459e195d9f6..dac963635c469 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.ts @@ -9,15 +9,7 @@ import { FieldFormatConfig, IFieldFormatsRegistry, } from '../../../../../../../../src/plugins/data/server'; - -interface IndexPatternSavedObject { - attributes: { - fieldFormatMap: string; - }; - id: string; - type: string; - version: string; -} +import { IndexPatternSavedObject } from '../../../../types'; /** * Create a map of FieldFormat instances for index pattern fields diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts index ddef2aa0a6268..17072d311b35f 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/create_job/create_job.ts @@ -5,9 +5,10 @@ */ import { notFound, notImplemented } from 'boom'; -import { get } from 'lodash'; import { ElasticsearchServiceSetup } from 'kibana/server'; +import { get } from 'lodash'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../../common/constants'; +import { ReportingCore } from '../../../../server'; import { cryptoFactory } from '../../../../server/lib'; import { CreateJobFactory, @@ -37,6 +38,7 @@ interface VisData { export const createJobFactory: CreateJobFactory> = function createJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, parentLogger: Logger diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts index b1b7b7d818200..6bb3e73fcfe84 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/execute_job.ts @@ -7,6 +7,7 @@ import { i18n } from '@kbn/i18n'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { CONTENT_TYPE_CSV, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; +import { ReportingCore } from '../../../server'; import { cryptoFactory } from '../../../server/lib'; import { ExecuteJobFactory, @@ -22,13 +23,15 @@ import { createGenerateCsv } from './lib'; export const executeJobFactory: ExecuteJobFactory> = function executeJobFactoryFn( +>> = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, parentLogger: Logger ) { const crypto = cryptoFactory(server); const logger = parentLogger.clone([CSV_FROM_SAVEDOBJECT_JOB_TYPE, 'execute-job']); + const generateCsv = createGenerateCsv(reporting, server, elasticsearch, parentLogger); return async function executeJob( jobId: string | null, @@ -86,11 +89,8 @@ export const executeJobFactory: ExecuteJobFactory { +const getEsQueryConfig = async (config: IUiSettingsClient) => { const configs = await Promise.all([ config.get('query:allowLeadingWildcards'), config.get('query:queryString:options'), @@ -49,7 +53,7 @@ const getEsQueryConfig = async (config: any) => { } as EsQueryConfig; }; -const getUiSettings = async (config: any) => { +const getUiSettings = async (config: IUiSettingsClient) => { const configs = await Promise.all([config.get('csv:separator'), config.get('csv:quoteValues')]); const [separator, quoteValues] = configs; return { separator, quoteValues }; @@ -57,14 +61,14 @@ const getUiSettings = async (config: any) => { export async function generateCsvSearch( req: RequestFacade, + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, logger: Logger, searchPanel: SearchPanel, jobParams: JobParamsDiscoverCsv ): Promise { - const { savedObjects, uiSettingsServiceFactory } = server; - const savedObjectsClient = savedObjects.getScopedSavedObjectsClient( + const savedObjectsClient = await reporting.getSavedObjectsClient( KibanaRequest.from(req.getRawRequest()) ); const { indexPatternSavedObjectId, timerange } = searchPanel; @@ -73,7 +77,8 @@ export async function generateCsvSearch( savedObjectsClient, indexPatternSavedObjectId ); - const uiConfig = uiSettingsServiceFactory({ savedObjectsClient }); + + const uiConfig = await reporting.getUiSettingsServiceFactory(savedObjectsClient); const esQueryConfig = await getEsQueryConfig(uiConfig); const { diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts index 3f03246106d3e..a6911e1f14704 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts @@ -4,20 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validateUrls } from '../../../../common/validate_urls'; +import { ReportingCore } from '../../../../server'; +import { cryptoFactory } from '../../../../server/lib/crypto'; import { + ConditionalHeaders, CreateJobFactory, - ServerFacade, - RequestFacade, ESQueueCreateJobFn, - ConditionalHeaders, + RequestFacade, + ServerFacade, } from '../../../../types'; -import { validateUrls } from '../../../../common/validate_urls'; -import { cryptoFactory } from '../../../../server/lib/crypto'; import { JobParamsPNG } from '../../types'; export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { +>> = function createJobFactoryFn(reporting: ReportingCore, server: ServerFacade) { const crypto = cryptoFactory(server); return async function createJob( diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js index bb33ef9c19a1d..c0c21119e1d53 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js @@ -6,6 +6,7 @@ import * as Rx from 'rxjs'; import { memoize } from 'lodash'; +import { createMockReportingCore } from '../../../../test_helpers'; import { cryptoFactory } from '../../../../server/lib/crypto'; import { executeJobFactory } from './index'; import { generatePngObservableFactory } from '../lib/generate_png'; @@ -19,7 +20,11 @@ const cancellationToken = { let config; let mockServer; -beforeEach(() => { +let mockReporting; + +beforeEach(async () => { + mockReporting = await createMockReportingCore(); + config = { 'xpack.reporting.encryptionKey': 'testencryptionkey', 'server.basePath': '/sbp', @@ -27,18 +32,11 @@ beforeEach(() => { 'server.port': 5601, }; mockServer = { - expose: () => {}, // NOTE: this is for oncePerServer config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', }, - savedObjects: { - getScopedSavedObjectsClient: jest.fn(), - }, - uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }), - log: jest.fn(), }; - mockServer.config().get.mockImplementation(key => { return config[key]; }); @@ -67,9 +65,12 @@ test(`passes browserTimezone to generatePng`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(''))); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger() + ); const browserTimezone = 'UTC'; await executeJob( 'pngJobId', @@ -87,9 +88,15 @@ test(`passes browserTimezone to generatePng`, async () => { }); test(`returns content_type of application/png`, async () => { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger(), + { + browserDriverFactory: {}, + } + ); const encryptedHeaders = await encryptHeaders({}); const generatePngObservable = generatePngObservableFactory(); @@ -109,9 +116,15 @@ test(`returns content of generatePng getBuffer base64 encoded`, async () => { const generatePngObservable = generatePngObservableFactory(); generatePngObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger(), + { + browserDriverFactory: {}, + } + ); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob( 'pngJobId', diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts index c9f370197da66..5cde245080914 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.ts @@ -7,14 +7,9 @@ import * as Rx from 'rxjs'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; +import { ReportingCore } from '../../../../server'; import { PNG_JOB_TYPE } from '../../../../common/constants'; -import { - ServerFacade, - ExecuteJobFactory, - ESQueueWorkerExecuteFn, - HeadlessChromiumDriverFactory, - Logger, -} from '../../../../types'; +import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn, Logger } from '../../../../types'; import { decryptJobHeaders, omitBlacklistedHeaders, @@ -26,12 +21,13 @@ import { generatePngObservableFactory } from '../lib/generate_png'; type QueuedPngExecutorFactory = ExecuteJobFactory>; -export const executeJobFactory: QueuedPngExecutorFactory = function executeJobFactoryFn( +export const executeJobFactory: QueuedPngExecutorFactory = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - parentLogger: Logger, - { browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory } + parentLogger: Logger ) { + const browserDriverFactory = await reporting.getBrowserDriverFactory(); const generatePngObservable = generatePngObservableFactory(server, browserDriverFactory); const logger = parentLogger.clone([PNG_JOB_TYPE, 'execute']); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts index a8cc71175cffe..656c99991e1f6 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts @@ -4,20 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ +import { validateUrls } from '../../../../common/validate_urls'; +import { ReportingCore } from '../../../../server'; +import { cryptoFactory } from '../../../../server/lib/crypto'; import { + ConditionalHeaders, CreateJobFactory, ESQueueCreateJobFn, - ServerFacade, RequestFacade, - ConditionalHeaders, + ServerFacade, } from '../../../../types'; -import { validateUrls } from '../../../../common/validate_urls'; -import { cryptoFactory } from '../../../../server/lib/crypto'; import { JobParamsPDF } from '../../types'; export const createJobFactory: CreateJobFactory> = function createJobFactoryFn(server: ServerFacade) { +>> = function createJobFactoryFn(reporting: ReportingCore, server: ServerFacade) { const crypto = cryptoFactory(server); return async function createJobFn( diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js index c21d39f4922cb..cc6b298bebdc5 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.test.js @@ -6,6 +6,7 @@ import * as Rx from 'rxjs'; import { memoize } from 'lodash'; +import { createMockReportingCore } from '../../../../test_helpers'; import { cryptoFactory } from '../../../../server/lib/crypto'; import { executeJobFactory } from './index'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; @@ -19,7 +20,11 @@ const cancellationToken = { let config; let mockServer; -beforeEach(() => { +let mockReporting; + +beforeEach(async () => { + mockReporting = await createMockReportingCore(); + config = { 'xpack.reporting.encryptionKey': 'testencryptionkey', 'server.basePath': '/sbp', @@ -27,18 +32,11 @@ beforeEach(() => { 'server.port': 5601, }; mockServer = { - expose: jest.fn(), - log: jest.fn(), config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', }, - savedObjects: { - getScopedSavedObjectsClient: jest.fn(), - }, - uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }), }; - mockServer.config().get.mockImplementation(key => { return config[key]; }); @@ -60,38 +58,13 @@ const encryptHeaders = async headers => { return await crypto.encrypt(headers); }; -test(`passes browserTimezone to generatePdf`, async () => { - const encryptedHeaders = await encryptHeaders({}); - - const generatePdfObservable = generatePdfObservableFactory(); - generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(''))); - - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); - const browserTimezone = 'UTC'; - await executeJob( - 'pdfJobId', - { relativeUrls: [], browserTimezone, headers: encryptedHeaders }, - cancellationToken - ); - - expect(mockServer.uiSettingsServiceFactory().get).toBeCalledWith('xpackReporting:customPdfLogo'); - expect(generatePdfObservable).toBeCalledWith( - expect.any(LevelLogger), - undefined, - [], - browserTimezone, - expect.anything(), - undefined, - undefined - ); -}); - test(`returns content_type of application/pdf`, async () => { - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger() + ); const encryptedHeaders = await encryptHeaders({}); const generatePdfObservable = generatePdfObservableFactory(); @@ -111,9 +84,12 @@ test(`returns content of generatePdf getBuffer base64 encoded`, async () => { const generatePdfObservable = generatePdfObservableFactory(); generatePdfObservable.mockReturnValue(Rx.of(Buffer.from(testContent))); - const executeJob = executeJobFactory(mockServer, mockElasticsearch, getMockLogger(), { - browserDriverFactory: {}, - }); + const executeJob = await executeJobFactory( + mockReporting, + mockServer, + mockElasticsearch, + getMockLogger() + ); const encryptedHeaders = await encryptHeaders({}); const { content } = await executeJob( 'pdfJobId', diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts index 162376e31216e..e8461862bee82 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts @@ -7,13 +7,8 @@ import * as Rx from 'rxjs'; import { ElasticsearchServiceSetup } from 'kibana/server'; import { catchError, map, mergeMap, takeUntil } from 'rxjs/operators'; -import { - ServerFacade, - ExecuteJobFactory, - ESQueueWorkerExecuteFn, - HeadlessChromiumDriverFactory, - Logger, -} from '../../../../types'; +import { ReportingCore } from '../../../../server'; +import { ServerFacade, ExecuteJobFactory, ESQueueWorkerExecuteFn, Logger } from '../../../../types'; import { JobDocPayloadPDF } from '../../types'; import { PDF_JOB_TYPE } from '../../../../common/constants'; import { generatePdfObservableFactory } from '../lib/generate_pdf'; @@ -27,12 +22,13 @@ import { type QueuedPdfExecutorFactory = ExecuteJobFactory>; -export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFactoryFn( +export const executeJobFactory: QueuedPdfExecutorFactory = async function executeJobFactoryFn( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - parentLogger: Logger, - { browserDriverFactory }: { browserDriverFactory: HeadlessChromiumDriverFactory } + parentLogger: Logger ) { + const browserDriverFactory = await reporting.getBrowserDriverFactory(); const generatePdfObservable = generatePdfObservableFactory(server, browserDriverFactory); const logger = parentLogger.clone([PDF_JOB_TYPE, 'execute']); @@ -43,7 +39,7 @@ export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFa mergeMap(() => decryptJobHeaders({ server, job, logger })), map(decryptedHeaders => omitBlacklistedHeaders({ job, decryptedHeaders })), map(filteredHeaders => getConditionalHeaders({ server, job, filteredHeaders })), - mergeMap(conditionalHeaders => getCustomLogo({ server, job, conditionalHeaders })), + mergeMap(conditionalHeaders => getCustomLogo({ reporting, server, job, conditionalHeaders })), mergeMap(({ logo, conditionalHeaders }) => { const urls = getFullUrls({ server, job }); diff --git a/x-pack/legacy/plugins/reporting/index.ts b/x-pack/legacy/plugins/reporting/index.ts index cbafc4b1ecc4b..9ce4e807f8ef8 100644 --- a/x-pack/legacy/plugins/reporting/index.ts +++ b/x-pack/legacy/plugins/reporting/index.ts @@ -10,7 +10,7 @@ import { resolve } from 'path'; import { PLUGIN_ID, UI_SETTINGS_CUSTOM_PDF_LOGO } from './common/constants'; import { config as reportingConfig } from './config'; import { legacyInit } from './server/legacy'; -import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types.d'; +import { ReportingConfigOptions, ReportingPluginSpecOptions } from './types'; const kbToBase64Length = (kb: number) => { return Math.floor((kb * 1024 * 8) / 6); diff --git a/x-pack/legacy/plugins/reporting/reporting.d.ts b/x-pack/legacy/plugins/reporting/reporting.d.ts index d4a7943f6d067..ec65c15f53864 100644 --- a/x-pack/legacy/plugins/reporting/reporting.d.ts +++ b/x-pack/legacy/plugins/reporting/reporting.d.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ReportingPlugin } from './types'; +export { ReportingPlugin } from './server/plugin'; diff --git a/x-pack/legacy/plugins/reporting/server/core.ts b/x-pack/legacy/plugins/reporting/server/core.ts new file mode 100644 index 0000000000000..4506d41e4f5c3 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/core.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as Rx from 'rxjs'; +import { first, mapTo } from 'rxjs/operators'; +import { + IUiSettingsClient, + KibanaRequest, + SavedObjectsClient, + SavedObjectsServiceStart, + UiSettingsServiceStart, +} from 'src/core/server'; +// @ts-ignore no module definition +import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; +import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; +import { PLUGIN_ID } from '../common/constants'; +import { EnqueueJobFn, ESQueueInstance, ReportingPluginSpecOptions, ServerFacade } from '../types'; +import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory'; +import { checkLicenseFactory, getExportTypesRegistry, LevelLogger } from './lib'; +import { registerRoutes } from './routes'; +import { ReportingSetupDeps } from './types'; + +interface ReportingInternalSetup { + browserDriverFactory: HeadlessChromiumDriverFactory; +} +interface ReportingInternalStart { + savedObjects: SavedObjectsServiceStart; + uiSettings: UiSettingsServiceStart; + esqueue: ESQueueInstance; + enqueueJob: EnqueueJobFn; +} + +export class ReportingCore { + private pluginSetupDeps?: ReportingInternalSetup; + private pluginStartDeps?: ReportingInternalStart; + private readonly pluginSetup$ = new Rx.ReplaySubject(); + private readonly pluginStart$ = new Rx.ReplaySubject(); + private exportTypesRegistry = getExportTypesRegistry(); + + constructor(private logger: LevelLogger) {} + + legacySetup( + xpackMainPlugin: XPackMainPlugin, + reporting: ReportingPluginSpecOptions, + __LEGACY: ServerFacade, + plugins: ReportingSetupDeps + ) { + mirrorPluginStatus(xpackMainPlugin, reporting); + const checkLicense = checkLicenseFactory(this.exportTypesRegistry); + (xpackMainPlugin as any).status.once('green', () => { + // Register a function that is called whenever the xpack info changes, + // to re-compute the license check results for this plugin + xpackMainPlugin.info.feature(PLUGIN_ID).registerLicenseCheckResultsGenerator(checkLicense); + }); + // Reporting routes + registerRoutes(this, __LEGACY, plugins, this.logger); + } + + public pluginSetup(reportingSetupDeps: ReportingInternalSetup) { + this.pluginSetup$.next(reportingSetupDeps); + } + + public pluginStart(reportingStartDeps: ReportingInternalStart) { + this.pluginStart$.next(reportingStartDeps); + } + + public pluginHasStarted(): Promise { + return this.pluginStart$.pipe(first(), mapTo(true)).toPromise(); + } + + /* + * Internal module dependencies + */ + public getExportTypesRegistry() { + return this.exportTypesRegistry; + } + + public async getEsqueue(): Promise { + return (await this.getPluginStartDeps()).esqueue; + } + + public async getEnqueueJob(): Promise { + return (await this.getPluginStartDeps()).enqueueJob; + } + + public async getBrowserDriverFactory(): Promise { + return (await this.getPluginSetupDeps()).browserDriverFactory; + } + + /* + * Kibana core module dependencies + */ + private async getPluginSetupDeps() { + if (this.pluginSetupDeps) { + return this.pluginSetupDeps; + } + return await this.pluginSetup$.pipe(first()).toPromise(); + } + + private async getPluginStartDeps() { + if (this.pluginStartDeps) { + return this.pluginStartDeps; + } + return await this.pluginStart$.pipe(first()).toPromise(); + } + + public async getSavedObjectsClient(fakeRequest: KibanaRequest): Promise { + const { savedObjects } = await this.getPluginStartDeps(); + return savedObjects.getScopedClient(fakeRequest) as SavedObjectsClient; + } + + public async getUiSettingsServiceFactory( + savedObjectsClient: SavedObjectsClient + ): Promise { + const { uiSettings: uiSettingsService } = await this.getPluginStartDeps(); + const scopedUiSettingsService = uiSettingsService.asScopedToClient(savedObjectsClient); + return scopedUiSettingsService; + } +} diff --git a/x-pack/legacy/plugins/reporting/server/index.ts b/x-pack/legacy/plugins/reporting/server/index.ts index 438a3fd595a10..24e2a954415d9 100644 --- a/x-pack/legacy/plugins/reporting/server/index.ts +++ b/x-pack/legacy/plugins/reporting/server/index.ts @@ -10,3 +10,6 @@ import { ReportingPlugin as Plugin } from './plugin'; export const plugin = (context: PluginInitializerContext) => { return new Plugin(context); }; + +export { ReportingCore } from './core'; +export { ReportingPlugin } from './plugin'; diff --git a/x-pack/legacy/plugins/reporting/server/legacy.ts b/x-pack/legacy/plugins/reporting/server/legacy.ts index c80aef06cf270..336ff5f4d2ee7 100644 --- a/x-pack/legacy/plugins/reporting/server/legacy.ts +++ b/x-pack/legacy/plugins/reporting/server/legacy.ts @@ -8,7 +8,7 @@ import { PluginInitializerContext } from 'src/core/server'; import { SecurityPluginSetup } from '../../../../plugins/security/server'; import { ReportingPluginSpecOptions } from '../types'; import { plugin } from './index'; -import { LegacySetup, ReportingStartDeps } from './plugin'; +import { LegacySetup, ReportingStartDeps } from './types'; const buildLegacyDependencies = ( server: Legacy.Server, @@ -22,8 +22,6 @@ const buildLegacyDependencies = ( xpack_main: server.plugins.xpack_main, reporting: reportingPlugin, }, - savedObjects: server.savedObjects, - uiSettingsServiceFactory: server.uiSettingsServiceFactory, }); export const legacyInit = async ( @@ -33,17 +31,20 @@ export const legacyInit = async ( const coreSetup = server.newPlatform.setup.core; const pluginInstance = plugin(server.newPlatform.coreContext as PluginInitializerContext); + const __LEGACY = buildLegacyDependencies(server, reportingPlugin); await pluginInstance.setup(coreSetup, { elasticsearch: coreSetup.elasticsearch, security: server.newPlatform.setup.plugins.security as SecurityPluginSetup, usageCollection: server.newPlatform.setup.plugins.usageCollection, - __LEGACY: buildLegacyDependencies(server, reportingPlugin), + __LEGACY, }); // Schedule to call the "start" hook only after start dependencies are ready coreSetup.getStartServices().then(([core, plugins]) => pluginInstance.start(core, { + elasticsearch: coreSetup.elasticsearch, data: (plugins as ReportingStartDeps).data, + __LEGACY, }) ); }; diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts index c4e32b3ebcd99..d593e4625cdf4 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_queue.ts @@ -5,29 +5,19 @@ */ import { ElasticsearchServiceSetup } from 'kibana/server'; -import { - ServerFacade, - ExportTypesRegistry, - HeadlessChromiumDriverFactory, - QueueConfig, - Logger, -} from '../../types'; +import { ESQueueInstance, ServerFacade, QueueConfig, Logger } from '../../types'; +import { ReportingCore } from '../core'; // @ts-ignore import { Esqueue } from './esqueue'; import { createWorkerFactory } from './create_worker'; import { createTaggedLogger } from './create_tagged_logger'; // TODO remove createTaggedLogger once esqueue is removed -interface CreateQueueFactoryOpts { - exportTypesRegistry: ExportTypesRegistry; - browserDriverFactory: HeadlessChromiumDriverFactory; -} - -export function createQueueFactory( +export async function createQueueFactory( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: Logger, - { exportTypesRegistry, browserDriverFactory }: CreateQueueFactoryOpts -): Esqueue { + logger: Logger +): Promise { const queueConfig: QueueConfig = server.config().get('xpack.reporting.queue'); const index = server.config().get('xpack.reporting.index'); @@ -39,15 +29,12 @@ export function createQueueFactory( logger: createTaggedLogger(logger, ['esqueue', 'queue-worker']), }; - const queue: Esqueue = new Esqueue(index, queueOptions); + const queue: ESQueueInstance = new Esqueue(index, queueOptions); if (queueConfig.pollEnabled) { // create workers to poll the index for idle jobs waiting to be claimed and executed - const createWorker = createWorkerFactory(server, elasticsearch, logger, { - exportTypesRegistry, - browserDriverFactory, - }); - createWorker(queue); + const createWorker = createWorkerFactory(reporting, server, elasticsearch, logger); + await createWorker(queue); } else { logger.info( 'xpack.reporting.queue.pollEnabled is set to false. This Kibana instance ' + diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts index f5c42e5505cd1..d4d913243e18d 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.test.ts @@ -4,9 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as sinon from 'sinon'; import { ElasticsearchServiceSetup } from 'kibana/server'; -import { HeadlessChromiumDriverFactory, ServerFacade } from '../../types'; +import * as sinon from 'sinon'; +import { ReportingCore } from '../../server'; +import { createMockReportingCore } from '../../test_helpers'; +import { ServerFacade } from '../../types'; import { createWorkerFactory } from './create_worker'; // @ts-ignore import { Esqueue } from './esqueue'; @@ -33,34 +35,34 @@ const getMockLogger = jest.fn(); const getMockExportTypesRegistry = ( exportTypes: any[] = [{ executeJobFactory: executeJobFactoryStub }] -) => ({ - getAll: () => exportTypes, -}); +) => + ({ + getAll: () => exportTypes, + } as ExportTypesRegistry); describe('Create Worker', () => { let queue: Esqueue; let client: ClientMock; + let mockReporting: ReportingCore; - beforeEach(() => { + beforeEach(async () => { + mockReporting = await createMockReportingCore(); client = new ClientMock(); queue = new Esqueue('reporting-queue', { client }); executeJobFactoryStub.reset(); }); test('Creates a single Esqueue worker for Reporting', async () => { - const exportTypesRegistry = getMockExportTypesRegistry(); + mockReporting.getExportTypesRegistry = () => getMockExportTypesRegistry(); const createWorker = createWorkerFactory( + mockReporting, getMockServer(), {} as ElasticsearchServiceSetup, - getMockLogger(), - { - exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - } + getMockLogger() ); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); - createWorker(queue); + await createWorker(queue); sinon.assert.callCount(executeJobFactoryStub, 1); sinon.assert.callCount(registerWorkerSpy, 1); @@ -88,18 +90,16 @@ Object { { executeJobFactory: executeJobFactoryStub }, { executeJobFactory: executeJobFactoryStub }, ]); + mockReporting.getExportTypesRegistry = () => exportTypesRegistry; const createWorker = createWorkerFactory( + mockReporting, getMockServer(), {} as ElasticsearchServiceSetup, - getMockLogger(), - { - exportTypesRegistry: exportTypesRegistry as ExportTypesRegistry, - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - } + getMockLogger() ); const registerWorkerSpy = sinon.spy(queue, 'registerWorker'); - createWorker(queue); + await createWorker(queue); sinon.assert.callCount(executeJobFactoryStub, 5); sinon.assert.callCount(registerWorkerSpy, 1); diff --git a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts index 2ca638f641291..3567712367608 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/create_worker.ts @@ -5,34 +5,29 @@ */ import { ElasticsearchServiceSetup } from 'kibana/server'; -import { PLUGIN_ID } from '../../common/constants'; -import { ExportTypesRegistry, HeadlessChromiumDriverFactory } from '../../types'; import { CancellationToken } from '../../common/cancellation_token'; +import { PLUGIN_ID } from '../../common/constants'; import { ESQueueInstance, - QueueConfig, - ExportTypeDefinition, ESQueueWorkerExecuteFn, - JobDocPayload, + ExportTypeDefinition, ImmediateExecuteFn, + JobDocPayload, JobSource, + Logger, + QueueConfig, RequestFacade, ServerFacade, - Logger, } from '../../types'; +import { ReportingCore } from '../core'; // @ts-ignore untyped dependency import { events as esqueueEvents } from './esqueue'; -interface CreateWorkerFactoryOpts { - exportTypesRegistry: ExportTypesRegistry; - browserDriverFactory: HeadlessChromiumDriverFactory; -} - export function createWorkerFactory( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: Logger, - { exportTypesRegistry, browserDriverFactory }: CreateWorkerFactoryOpts + logger: Logger ) { type JobDocPayloadType = JobDocPayload; const config = server.config(); @@ -41,20 +36,23 @@ export function createWorkerFactory( const kibanaId: string = config.get('server.uuid'); // Once more document types are added, this will need to be passed in - return function createWorker(queue: ESQueueInstance) { + return async function createWorker(queue: ESQueueInstance) { // export type / execute job map const jobExecutors: Map< string, ImmediateExecuteFn | ESQueueWorkerExecuteFn > = new Map(); - for (const exportType of exportTypesRegistry.getAll() as Array< - ExportTypeDefinition + for (const exportType of reporting.getExportTypesRegistry().getAll() as Array< + ExportTypeDefinition >) { // TODO: the executeJobFn should be unwrapped in the register method of the export types registry - const jobExecutor = exportType.executeJobFactory(server, elasticsearch, logger, { - browserDriverFactory, - }); + const jobExecutor = await exportType.executeJobFactory( + reporting, + server, + elasticsearch, + logger + ); jobExecutors.set(exportType.jobType, jobExecutor); } diff --git a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts index 1da8a3795aacc..c215bdc398904 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/enqueue_job.ts @@ -16,11 +16,11 @@ import { ServerFacade, RequestFacade, Logger, - ExportTypesRegistry, CaptureConfig, QueueConfig, ConditionalHeaders, } from '../../types'; +import { ReportingCore } from '../core'; interface ConfirmedJob { id: string; @@ -29,16 +29,11 @@ interface ConfirmedJob { _primary_term: number; } -interface EnqueueJobFactoryOpts { - exportTypesRegistry: ExportTypesRegistry; - esqueue: any; -} - export function enqueueJobFactory( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - parentLogger: Logger, - { exportTypesRegistry, esqueue }: EnqueueJobFactoryOpts + parentLogger: Logger ): EnqueueJobFn { const logger = parentLogger.clone(['queue-job']); const config = server.config(); @@ -56,14 +51,20 @@ export function enqueueJobFactory( ): Promise { type CreateJobFn = ESQueueCreateJobFn | ImmediateCreateJobFn; - const exportType = exportTypesRegistry.getById(exportTypeId); + const esqueue = await reporting.getEsqueue(); + const exportType = reporting.getExportTypesRegistry().getById(exportTypeId); if (exportType == null) { throw new Error(`Export type ${exportTypeId} does not exist in the registry!`); } // TODO: the createJobFn should be unwrapped in the register method of the export types registry - const createJob = exportType.createJobFactory(server, elasticsearch, logger) as CreateJobFn; + const createJob = exportType.createJobFactory( + reporting, + server, + elasticsearch, + logger + ) as CreateJobFn; const payload = await createJob(jobParams, headers, request); const options = { diff --git a/x-pack/legacy/plugins/reporting/server/lib/get_user.ts b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts index ab02dfe0743f0..49d5c568c3981 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/get_user.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/get_user.ts @@ -7,7 +7,7 @@ import { Legacy } from 'kibana'; import { KibanaRequest } from '../../../../../../src/core/server'; import { ServerFacade } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps } from '../types'; export function getUserFactory(server: ServerFacade, security: ReportingSetupDeps['security']) { /* diff --git a/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts b/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts index 028d8fa143487..0fdbd858b8e3c 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/validate/index.ts @@ -16,8 +16,8 @@ import { validateServerHost } from './validate_server_host'; export async function runValidations( server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: Logger, - browserFactory: HeadlessChromiumDriverFactory + browserFactory: HeadlessChromiumDriverFactory, + logger: Logger ) { try { await Promise.all([ @@ -32,6 +32,7 @@ export async function runValidations( }) ); } catch (err) { + logger.error(err); logger.warning( i18n.translate('xpack.reporting.selfCheck.warning', { defaultMessage: `Reporting plugin self-check generated a warning: {err}`, diff --git a/x-pack/legacy/plugins/reporting/server/plugin.ts b/x-pack/legacy/plugins/reporting/server/plugin.ts index ef7b01f8e9c15..4f24cc16b2277 100644 --- a/x-pack/legacy/plugins/reporting/server/plugin.ts +++ b/x-pack/legacy/plugins/reporting/server/plugin.ts @@ -4,97 +4,66 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; -import { - CoreSetup, - CoreStart, - ElasticsearchServiceSetup, - Plugin, - PluginInitializerContext, -} from 'src/core/server'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server'; -import { SecurityPluginSetup } from '../../../../plugins/security/server'; -// @ts-ignore -import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; -import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; -import { PLUGIN_ID } from '../common/constants'; +import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/server'; import { logConfiguration } from '../log_configuration'; -import { ReportingPluginSpecOptions } from '../types.d'; import { createBrowserDriverFactory } from './browsers'; -import { checkLicenseFactory, getExportTypesRegistry, LevelLogger, runValidations } from './lib'; -import { registerRoutes } from './routes'; +import { ReportingCore } from './core'; +import { createQueueFactory, enqueueJobFactory, LevelLogger, runValidations } from './lib'; import { setFieldFormats } from './services'; +import { ReportingSetup, ReportingSetupDeps, ReportingStart, ReportingStartDeps } from './types'; import { registerReportingUsageCollector } from './usage'; +// @ts-ignore no module definition +import { mirrorPluginStatus } from '../../../server/lib/mirror_plugin_status'; -export interface ReportingSetupDeps { - elasticsearch: ElasticsearchServiceSetup; - usageCollection: UsageCollectionSetup; - security: SecurityPluginSetup; - __LEGACY: LegacySetup; -} - -export interface ReportingStartDeps { - data: DataPluginStart; -} - -export interface LegacySetup { - config: Legacy.Server['config']; - info: Legacy.Server['info']; - plugins: { - elasticsearch: Legacy.Server['plugins']['elasticsearch']; - xpack_main: XPackMainPlugin & { - status?: any; - }; - reporting: ReportingPluginSpecOptions; - }; - route: Legacy.Server['route']; - savedObjects: Legacy.Server['savedObjects']; - uiSettingsServiceFactory: Legacy.Server['uiSettingsServiceFactory']; -} +export class ReportingPlugin + implements Plugin { + private logger: LevelLogger; + private reportingCore: ReportingCore; -export class ReportingPlugin implements Plugin { - constructor(private context: PluginInitializerContext) {} + constructor(context: PluginInitializerContext) { + this.logger = new LevelLogger(context.logger.get('reporting')); + this.reportingCore = new ReportingCore(this.logger); + } public async setup(core: CoreSetup, plugins: ReportingSetupDeps) { const { elasticsearch, usageCollection, __LEGACY } = plugins; - const exportTypesRegistry = getExportTypesRegistry(); - let isCollectorReady = false; + const browserDriverFactory = await createBrowserDriverFactory(__LEGACY, this.logger); // required for validations :( + runValidations(__LEGACY, elasticsearch, browserDriverFactory, this.logger); // this must run early, as it sets up config defaults + + const { xpack_main: xpackMainLegacy, reporting: reportingLegacy } = __LEGACY.plugins; + this.reportingCore.legacySetup(xpackMainLegacy, reportingLegacy, __LEGACY, plugins); // Register a function with server to manage the collection of usage stats - registerReportingUsageCollector( - usageCollection, - __LEGACY, - () => isCollectorReady, - exportTypesRegistry - ); + registerReportingUsageCollector(this.reportingCore, __LEGACY, usageCollection); - const logger = new LevelLogger(this.context.logger.get('reporting')); - const browserDriverFactory = await createBrowserDriverFactory(__LEGACY, logger); + // regsister setup internals + this.reportingCore.pluginSetup({ browserDriverFactory }); - logConfiguration(__LEGACY, logger); - runValidations(__LEGACY, elasticsearch, logger, browserDriverFactory); + return {}; + } - const { xpack_main: xpackMainPlugin, reporting } = __LEGACY.plugins; - mirrorPluginStatus(xpackMainPlugin, reporting); + public async start(core: CoreStart, plugins: ReportingStartDeps) { + const { reportingCore, logger } = this; + const { elasticsearch, __LEGACY } = plugins; - const checkLicense = checkLicenseFactory(exportTypesRegistry); + const esqueue = await createQueueFactory(reportingCore, __LEGACY, elasticsearch, logger); + const enqueueJob = enqueueJobFactory(reportingCore, __LEGACY, elasticsearch, logger); - (xpackMainPlugin as any).status.once('green', () => { - // Register a function that is called whenever the xpack info changes, - // to re-compute the license check results for this plugin - xpackMainPlugin.info.feature(PLUGIN_ID).registerLicenseCheckResultsGenerator(checkLicense); + this.reportingCore.pluginStart({ + savedObjects: core.savedObjects, + uiSettings: core.uiSettings, + esqueue, + enqueueJob, }); - // Post initialization of the above code, the collector is now ready to fetch its data - isCollectorReady = true; + setFieldFormats(plugins.data.fieldFormats); + logConfiguration(__LEGACY, this.logger); - // Reporting routes - registerRoutes(__LEGACY, plugins, exportTypesRegistry, browserDriverFactory, logger); + return {}; } - public start(core: CoreStart, plugins: ReportingStartDeps) { - setFieldFormats(plugins.data.fieldFormats); + public getReportingCore() { + return this.reportingCore; } } diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts index ed761b1e684ae..49868bb7ad5d5 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_jobparams.ts @@ -10,7 +10,7 @@ import { Legacy } from 'kibana'; import rison from 'rison-node'; import { API_BASE_URL } from '../../common/constants'; import { Logger, ReportingResponseToolkit, ServerFacade } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps } from '../types'; import { makeRequestFacade } from './lib/make_request_facade'; import { GetRouteConfigFactoryFn, diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts index 8696f36a45c62..415b6b7d64366 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject.ts @@ -9,7 +9,7 @@ import { get } from 'lodash'; import { API_BASE_GENERATE_V1, CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../common/constants'; import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; import { Logger, ReportingResponseToolkit, ServerFacade } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps } from '../types'; import { makeRequestFacade } from './lib/make_request_facade'; import { getRouteOptionsCsv } from './lib/route_config_factories'; import { HandlerErrorFunction, HandlerFunction, QueuedJobPayload } from './types'; diff --git a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts index fd1d85fef0f21..5d17fa2e82b8c 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generate_from_savedobject_immediate.ts @@ -10,14 +10,13 @@ import { createJobFactory, executeJobFactory } from '../../export_types/csv_from import { getJobParamsFromRequest } from '../../export_types/csv_from_savedobject/server/lib/get_job_params_from_request'; import { JobDocPayloadPanelCsv } from '../../export_types/csv_from_savedobject/types'; import { - HeadlessChromiumDriverFactory, JobDocOutput, Logger, ReportingResponseToolkit, ResponseFacade, ServerFacade, } from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps, ReportingCore } from '../types'; import { makeRequestFacade } from './lib/make_request_facade'; import { getRouteOptionsCsv } from './lib/route_config_factories'; @@ -31,6 +30,7 @@ import { getRouteOptionsCsv } from './lib/route_config_factories'; * - local (transient) changes the user made to the saved object */ export function registerGenerateCsvFromSavedObjectImmediate( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, parentLogger: Logger @@ -58,10 +58,8 @@ export function registerGenerateCsvFromSavedObjectImmediate( * * Calling an execute job factory requires passing a browserDriverFactory option, so we should not call the factory from here */ - const createJobFn = createJobFactory(server, elasticsearch, logger); - const executeJobFn = executeJobFactory(server, elasticsearch, logger, { - browserDriverFactory: {} as HeadlessChromiumDriverFactory, - }); + const createJobFn = createJobFactory(reporting, server, elasticsearch, logger); + const executeJobFn = await executeJobFactory(reporting, server, elasticsearch, logger); const jobDocPayload: JobDocPayloadPanelCsv = await createJobFn( jobParams, request.headers, diff --git a/x-pack/legacy/plugins/reporting/server/routes/generation.ts b/x-pack/legacy/plugins/reporting/server/routes/generation.ts index 02a9541484bc6..096ba84b63d1a 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/generation.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/generation.ts @@ -8,15 +8,8 @@ import boom from 'boom'; import { errors as elasticsearchErrors } from 'elasticsearch'; import { Legacy } from 'kibana'; import { API_BASE_URL } from '../../common/constants'; -import { - ExportTypesRegistry, - HeadlessChromiumDriverFactory, - Logger, - ReportingResponseToolkit, - ServerFacade, -} from '../../types'; -import { createQueueFactory, enqueueJobFactory } from '../lib'; -import { ReportingSetupDeps } from '../plugin'; +import { Logger, ReportingResponseToolkit, ServerFacade } from '../../types'; +import { ReportingSetupDeps, ReportingCore } from '../types'; import { registerGenerateFromJobParams } from './generate_from_jobparams'; import { registerGenerateCsvFromSavedObject } from './generate_from_savedobject'; import { registerGenerateCsvFromSavedObjectImmediate } from './generate_from_savedobject_immediate'; @@ -25,23 +18,13 @@ import { makeRequestFacade } from './lib/make_request_facade'; const esErrors = elasticsearchErrors as Record; export function registerJobGenerationRoutes( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, - exportTypesRegistry: ExportTypesRegistry, - browserDriverFactory: HeadlessChromiumDriverFactory, logger: Logger ) { const config = server.config(); const DOWNLOAD_BASE_URL = config.get('server.basePath') + `${API_BASE_URL}/jobs/download`; - const { elasticsearch } = plugins; - const esqueue = createQueueFactory(server, elasticsearch, logger, { - exportTypesRegistry, - browserDriverFactory, - }); - const enqueueJob = enqueueJobFactory(server, elasticsearch, logger, { - exportTypesRegistry, - esqueue, - }); /* * Generates enqueued job details to use in responses @@ -56,6 +39,7 @@ export function registerJobGenerationRoutes( const user = request.pre.user; const headers = request.headers; + const enqueueJob = await reporting.getEnqueueJob(); const job = await enqueueJob(exportTypeId, jobParams, user, headers, request); // return the queue's job information @@ -87,6 +71,6 @@ export function registerJobGenerationRoutes( // Register beta panel-action download-related API's if (config.get('xpack.reporting.csv.enablePanelActionDownload')) { registerGenerateCsvFromSavedObject(server, plugins, handler, handleError, logger); - registerGenerateCsvFromSavedObjectImmediate(server, plugins, logger); + registerGenerateCsvFromSavedObjectImmediate(reporting, server, plugins, logger); } } diff --git a/x-pack/legacy/plugins/reporting/server/routes/index.ts b/x-pack/legacy/plugins/reporting/server/routes/index.ts index 4cfa9dd465eab..610ab4907d369 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/index.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/index.ts @@ -4,23 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - ExportTypesRegistry, - HeadlessChromiumDriverFactory, - Logger, - ServerFacade, -} from '../../types'; -import { ReportingSetupDeps } from '../plugin'; +import { Logger, ServerFacade } from '../../types'; +import { ReportingCore, ReportingSetupDeps } from '../types'; import { registerJobGenerationRoutes } from './generation'; import { registerJobInfoRoutes } from './jobs'; export function registerRoutes( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, - exportTypesRegistry: ExportTypesRegistry, - browserDriverFactory: HeadlessChromiumDriverFactory, logger: Logger ) { - registerJobGenerationRoutes(server, plugins, exportTypesRegistry, browserDriverFactory, logger); - registerJobInfoRoutes(server, plugins, exportTypesRegistry, logger); + registerJobGenerationRoutes(reporting, server, plugins, logger); + registerJobInfoRoutes(reporting, server, plugins, logger); } diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js index 811c81c502b81..071b401d2321b 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.test.js @@ -5,30 +5,30 @@ */ import Hapi from 'hapi'; -import { difference, memoize } from 'lodash'; -import { registerJobInfoRoutes } from './jobs'; +import { memoize } from 'lodash'; +import { createMockReportingCore } from '../../test_helpers'; import { ExportTypesRegistry } from '../lib/export_types_registry'; -jest.mock('./lib/authorized_user_pre_routing', () => { - return { - authorizedUserPreRoutingFactory: () => () => ({}), - }; -}); -jest.mock('./lib/reporting_feature_pre_routing', () => { - return { - reportingFeaturePreRoutingFactory: () => () => () => ({ - jobTypes: ['unencodedJobType', 'base64EncodedJobType'], - }), - }; -}); + +jest.mock('./lib/authorized_user_pre_routing', () => ({ + authorizedUserPreRoutingFactory: () => () => ({}), +})); +jest.mock('./lib/reporting_feature_pre_routing', () => ({ + reportingFeaturePreRoutingFactory: () => () => () => ({ + jobTypes: ['unencodedJobType', 'base64EncodedJobType'], + }), +})); + +import { registerJobInfoRoutes } from './jobs'; let mockServer; let exportTypesRegistry; +let mockReportingPlugin; const mockLogger = { error: jest.fn(), debug: jest.fn(), }; -beforeEach(() => { +beforeEach(async () => { mockServer = new Hapi.Server({ debug: false, port: 8080, routes: { log: { collect: true } } }); mockServer.config = memoize(() => ({ get: jest.fn() })); exportTypesRegistry = new ExportTypesRegistry(); @@ -43,6 +43,8 @@ beforeEach(() => { jobContentEncoding: 'base64', jobContentExtension: 'pdf', }); + mockReportingPlugin = await createMockReportingCore(); + mockReportingPlugin.getExportTypesRegistry = () => exportTypesRegistry; }); const mockPlugins = { @@ -60,12 +62,15 @@ const getHits = (...sources) => { }; }; +const getErrorsFromRequest = request => + request.logs.filter(log => log.tags.includes('error')).map(log => log.error); + test(`returns 404 if job not found`, async () => { mockPlugins.elasticsearch.adminClient = { callAsInternalUser: jest.fn().mockReturnValue(Promise.resolve(getHits())), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -84,7 +89,7 @@ test(`returns 401 if not valid job type`, async () => { .mockReturnValue(Promise.resolve(getHits({ jobtype: 'invalidJobType' }))), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -105,7 +110,7 @@ describe(`when job is incomplete`, () => { ), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -147,7 +152,7 @@ describe(`when job is failed`, () => { callAsInternalUser: jest.fn().mockReturnValue(Promise.resolve(hits)), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -192,7 +197,7 @@ describe(`when job is completed`, () => { callAsInternalUser: jest.fn().mockReturnValue(Promise.resolve(hits)), }; - registerJobInfoRoutes(mockServer, mockPlugins, exportTypesRegistry, mockLogger); + registerJobInfoRoutes(mockReportingPlugin, mockServer, mockPlugins, mockLogger); const request = { method: 'GET', @@ -203,72 +208,115 @@ describe(`when job is completed`, () => { }; test(`sets statusCode to 200`, async () => { - const { statusCode } = await getCompletedResponse(); + const { statusCode, request } = await getCompletedResponse(); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(statusCode).toBe(200); }); test(`doesn't encode output content for not-specified jobTypes`, async () => { - const { payload } = await getCompletedResponse({ + const { payload, request } = await getCompletedResponse({ jobType: 'unencodedJobType', outputContent: 'test', }); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); + expect(payload).toBe('test'); }); test(`base64 encodes output content for configured jobTypes`, async () => { - const { payload } = await getCompletedResponse({ + const { payload, request } = await getCompletedResponse({ jobType: 'base64EncodedJobType', outputContent: 'test', }); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); + expect(payload).toBe(Buffer.from('test', 'base64').toString()); }); test(`specifies text/csv; charset=utf-8 contentType header from the job output`, async () => { - const { headers } = await getCompletedResponse({ outputContentType: 'text/csv' }); + const { headers, request } = await getCompletedResponse({ outputContentType: 'text/csv' }); + + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); + expect(headers['content-type']).toBe('text/csv; charset=utf-8'); }); test(`specifies default filename in content-disposition header if no title`, async () => { - const { headers } = await getCompletedResponse({}); + const { headers, request } = await getCompletedResponse({}); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-disposition']).toBe('inline; filename="report.csv"'); }); test(`specifies payload title in content-disposition header`, async () => { - const { headers } = await getCompletedResponse({ title: 'something' }); + const { headers, request } = await getCompletedResponse({ title: 'something' }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-disposition']).toBe('inline; filename="something.csv"'); }); test(`specifies jobContentExtension in content-disposition header`, async () => { - const { headers } = await getCompletedResponse({ jobType: 'base64EncodedJobType' }); + const { headers, request } = await getCompletedResponse({ jobType: 'base64EncodedJobType' }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-disposition']).toBe('inline; filename="report.pdf"'); }); test(`specifies application/pdf contentType header from the job output`, async () => { - const { headers } = await getCompletedResponse({ outputContentType: 'application/pdf' }); + const { headers, request } = await getCompletedResponse({ + outputContentType: 'application/pdf', + }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toEqual([]); expect(headers['content-type']).toBe('application/pdf'); }); describe(`when non-whitelisted contentType specified in job output`, () => { test(`sets statusCode to 500`, async () => { - const { statusCode } = await getCompletedResponse({ outputContentType: 'application/html' }); + const { statusCode, request } = await getCompletedResponse({ + outputContentType: 'application/html', + }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: Unsupported content-type of application/html specified by job output], + [Error: Unsupported content-type of application/html specified by job output], + ] + `); expect(statusCode).toBe(500); }); test(`doesn't include job output content in payload`, async () => { - const { payload } = await getCompletedResponse({ outputContentType: 'application/html' }); - expect(payload).not.toMatch(/job output content/); + const { payload, request } = await getCompletedResponse({ + outputContentType: 'application/html', + }); + expect(payload).toMatchInlineSnapshot( + `"{\\"statusCode\\":500,\\"error\\":\\"Internal Server Error\\",\\"message\\":\\"An internal server error occurred\\"}"` + ); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: Unsupported content-type of application/html specified by job output], + [Error: Unsupported content-type of application/html specified by job output], + ] + `); }); test(`logs error message about invalid content type`, async () => { - const { - request: { logs }, - } = await getCompletedResponse({ outputContentType: 'application/html' }); - const errorLogs = logs.filter( - log => difference(['internal', 'implementation', 'error'], log.tags).length === 0 - ); - expect(errorLogs).toHaveLength(1); - expect(errorLogs[0].error).toBeInstanceOf(Error); - expect(errorLogs[0].error.message).toMatch(/Unsupported content-type of application\/html/); + const { request } = await getCompletedResponse({ outputContentType: 'application/html' }); + const errorLogs = getErrorsFromRequest(request); + expect(errorLogs).toMatchInlineSnapshot(` + Array [ + [Error: Unsupported content-type of application/html specified by job output], + [Error: Unsupported content-type of application/html specified by job output], + ] + `); }); }); }); diff --git a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts index daabc2cf22f4e..2de420e6577c3 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/jobs.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/jobs.ts @@ -9,7 +9,6 @@ import { ResponseObject } from 'hapi'; import { Legacy } from 'kibana'; import { API_BASE_URL } from '../../common/constants'; import { - ExportTypesRegistry, JobDocOutput, JobSource, ListQuery, @@ -18,7 +17,7 @@ import { ServerFacade, } from '../../types'; import { jobsQueryFactory } from '../lib/jobs_query'; -import { ReportingSetupDeps } from '../plugin'; +import { ReportingSetupDeps, ReportingCore } from '../types'; import { jobResponseHandlerFactory } from './lib/job_response_handler'; import { makeRequestFacade } from './lib/make_request_facade'; import { @@ -33,9 +32,9 @@ function isResponse(response: Boom | ResponseObject): response is Response } export function registerJobInfoRoutes( + reporting: ReportingCore, server: ServerFacade, plugins: ReportingSetupDeps, - exportTypesRegistry: ExportTypesRegistry, logger: Logger ) { const { elasticsearch } = plugins; @@ -138,6 +137,7 @@ export function registerJobInfoRoutes( }); // trigger a download of the output from a job + const exportTypesRegistry = reporting.getExportTypesRegistry(); const jobResponseHandler = jobResponseHandlerFactory(server, elasticsearch, exportTypesRegistry); server.route({ path: `${MAIN_ENTRY}/download/{docId}`, diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts index 57c3fcee222da..c5f8c78016f61 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/authorized_user_pre_routing.ts @@ -9,7 +9,7 @@ import { Legacy } from 'kibana'; import { AuthenticatedUser } from '../../../../../../plugins/security/server'; import { Logger, ServerFacade } from '../../../types'; import { getUserFactory } from '../../lib/get_user'; -import { ReportingSetupDeps } from '../../plugin'; +import { ReportingSetupDeps } from '../../types'; const superuserRole = 'superuser'; diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts index 7367fceb50857..9e618ff1fe40a 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/reporting_feature_pre_routing.ts @@ -7,7 +7,7 @@ import Boom from 'boom'; import { Legacy } from 'kibana'; import { Logger, ServerFacade } from '../../../types'; -import { ReportingSetupDeps } from '../../plugin'; +import { ReportingSetupDeps } from '../../types'; export type GetReportingFeatureIdFn = (request: Legacy.Request) => string; diff --git a/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts b/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts index 931f642397bf8..82ba9ba22c706 100644 --- a/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts +++ b/x-pack/legacy/plugins/reporting/server/routes/lib/route_config_factories.ts @@ -7,7 +7,7 @@ import Joi from 'joi'; import { CSV_FROM_SAVEDOBJECT_JOB_TYPE } from '../../../common/constants'; import { Logger, ServerFacade } from '../../../types'; -import { ReportingSetupDeps } from '../../plugin'; +import { ReportingSetupDeps } from '../../types'; import { authorizedUserPreRoutingFactory } from './authorized_user_pre_routing'; import { GetReportingFeatureIdFn, diff --git a/x-pack/legacy/plugins/reporting/server/types.d.ts b/x-pack/legacy/plugins/reporting/server/types.d.ts new file mode 100644 index 0000000000000..20673423aa448 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/server/types.d.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Legacy } from 'kibana'; +import { + ElasticsearchServiceSetup, + SavedObjectsServiceStart, + UiSettingsServiceStart, +} from 'src/core/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { PluginStart as DataPluginStart } from '../../../../../src/plugins/data/server'; +import { SecurityPluginSetup } from '../../../../plugins/security/server'; +import { XPackMainPlugin } from '../../xpack_main/server/xpack_main'; +import { EnqueueJobFn, ESQueueInstance, ReportingPluginSpecOptions } from '../types'; +import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory'; + +export interface ReportingSetupDeps { + elasticsearch: ElasticsearchServiceSetup; + security: SecurityPluginSetup; + usageCollection: UsageCollectionSetup; + __LEGACY: LegacySetup; +} + +export interface ReportingStartDeps { + elasticsearch: ElasticsearchServiceSetup; + data: DataPluginStart; + __LEGACY: LegacySetup; +} + +export type ReportingSetup = object; + +export type ReportingStart = object; + +export interface LegacySetup { + config: Legacy.Server['config']; + info: Legacy.Server['info']; + plugins: { + elasticsearch: Legacy.Server['plugins']['elasticsearch']; + xpack_main: XPackMainPlugin & { + status?: any; + }; + reporting: ReportingPluginSpecOptions; + }; + route: Legacy.Server['route']; +} + +export { ReportingCore } from './core'; diff --git a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts index 0118dea38d985..359bcc45230c3 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/decorate_range_stats.ts @@ -6,7 +6,7 @@ import { uniq } from 'lodash'; import { CSV_JOB_TYPE, PDF_JOB_TYPE, PNG_JOB_TYPE } from '../../common/constants'; -import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './types.d'; +import { AvailableTotal, FeatureAvailabilityMap, RangeStats, ExportType } from './types'; function getForFeature( range: Partial, diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js index f761f0d2d270b..a6d753f9b107a 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.test.js @@ -3,9 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + import sinon from 'sinon'; +import { createMockReportingCore } from '../../test_helpers'; import { getExportTypesRegistry } from '../lib/export_types_registry'; -import { getReportingUsageCollector } from './reporting_usage_collector'; +import { + registerReportingUsageCollector, + getReportingUsageCollector, +} from './reporting_usage_collector'; const exportTypesRegistry = getExportTypesRegistry(); @@ -70,9 +75,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithBasicLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -101,9 +105,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithNoLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -132,9 +135,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve(getResponseMock())); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithPlatinumLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -163,9 +165,8 @@ describe('license checks', () => { const callClusterMock = jest.fn(() => Promise.resolve({})); const usageCollection = getMockUsageCollection(); const { fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithBasicLicenseMock, - () => {}, + usageCollection, exportTypesRegistry ); usageStats = await getReportingUsage(callClusterMock, exportTypesRegistry); @@ -190,9 +191,8 @@ describe('data modeling', () => { .stub() .returns('platinum'); ({ fetch: getReportingUsage } = getReportingUsageCollector( - usageCollection, serverWithPlatinumLicenseMock, - () => {}, + usageCollection, exportTypesRegistry )); }); @@ -322,94 +322,124 @@ describe('data modeling', () => { const usageStats = await getReportingUsage(callClusterMock); expect(usageStats).toMatchInlineSnapshot(` -Object { - "PNG": Object { - "available": true, - "total": 4, - }, - "_all": 54, - "available": true, - "browser_type": undefined, - "csv": Object { - "available": true, - "total": 27, - }, - "enabled": true, - "last7Days": Object { - "PNG": Object { - "available": true, - "total": 4, - }, - "_all": 27, - "csv": Object { - "available": true, - "total": 10, - }, - "printable_pdf": Object { - "app": Object { - "dashboard": 13, - "visualization": 0, - }, - "available": true, - "layout": Object { - "preserve_layout": 3, - "print": 10, - }, - "total": 13, - }, - "status": Object { - "completed": 0, - "failed": 0, - "pending": 27, - }, - }, - "lastDay": Object { - "PNG": Object { - "available": true, - "total": 4, - }, - "_all": 11, - "csv": Object { - "available": true, - "total": 5, - }, - "printable_pdf": Object { - "app": Object { - "dashboard": 2, - "visualization": 0, - }, - "available": true, - "layout": Object { - "preserve_layout": 0, - "print": 2, - }, - "total": 2, - }, - "status": Object { - "completed": 0, - "failed": 0, - "pending": 11, - }, - }, - "printable_pdf": Object { - "app": Object { - "dashboard": 23, - "visualization": 0, - }, - "available": true, - "layout": Object { - "preserve_layout": 13, - "print": 10, - }, - "total": 23, - }, - "status": Object { - "completed": 20, - "failed": 0, - "pending": 33, - "processing": 1, - }, -} -`); + Object { + "PNG": Object { + "available": true, + "total": 4, + }, + "_all": 54, + "available": true, + "browser_type": undefined, + "csv": Object { + "available": true, + "total": 27, + }, + "enabled": true, + "last7Days": Object { + "PNG": Object { + "available": true, + "total": 4, + }, + "_all": 27, + "csv": Object { + "available": true, + "total": 10, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 13, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 3, + "print": 10, + }, + "total": 13, + }, + "status": Object { + "completed": 0, + "failed": 0, + "pending": 27, + }, + }, + "lastDay": Object { + "PNG": Object { + "available": true, + "total": 4, + }, + "_all": 11, + "csv": Object { + "available": true, + "total": 5, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 2, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 0, + "print": 2, + }, + "total": 2, + }, + "status": Object { + "completed": 0, + "failed": 0, + "pending": 11, + }, + }, + "printable_pdf": Object { + "app": Object { + "dashboard": 23, + "visualization": 0, + }, + "available": true, + "layout": Object { + "preserve_layout": 13, + "print": 10, + }, + "total": 23, + }, + "status": Object { + "completed": 20, + "failed": 0, + "pending": 33, + "processing": 1, + }, + } + `); + }); +}); + +describe('Ready for collection observable', () => { + let mockReporting; + + beforeEach(async () => { + mockReporting = await createMockReportingCore(); + }); + + test('converts observable to promise', async () => { + const serverWithBasicLicenseMock = getServerMock(); + const makeCollectorSpy = sinon.spy(); + const usageCollection = { + makeUsageCollector: makeCollectorSpy, + registerCollector: sinon.stub(), + }; + registerReportingUsageCollector(mockReporting, serverWithBasicLicenseMock, usageCollection); + + const [args] = makeCollectorSpy.firstCall.args; + expect(args).toMatchInlineSnapshot(` + Object { + "fetch": [Function], + "formatForBulkUpload": [Function], + "isReady": [Function], + "type": "reporting", + } + `); + + await expect(args.isReady()).resolves.toBe(true); }); }); diff --git a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts index 567838391d2e7..14202530fb6c7 100644 --- a/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts +++ b/x-pack/legacy/plugins/reporting/server/usage/reporting_usage_collector.ts @@ -5,8 +5,9 @@ */ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { ServerFacade, ExportTypesRegistry, ESCallCluster } from '../../types'; import { KIBANA_REPORTING_TYPE } from '../../common/constants'; +import { ReportingCore } from '../../server'; +import { ESCallCluster, ExportTypesRegistry, ServerFacade } from '../../types'; import { getReportingUsage } from './get_reporting_usage'; import { RangeStats } from './types'; @@ -18,16 +19,16 @@ const METATYPE = 'kibana_stats'; * @return {Object} kibana usage stats type collection object */ export function getReportingUsageCollector( - usageCollection: UsageCollectionSetup, server: ServerFacade, - isReady: () => boolean, - exportTypesRegistry: ExportTypesRegistry + usageCollection: UsageCollectionSetup, + exportTypesRegistry: ExportTypesRegistry, + isReady: () => Promise ) { return usageCollection.makeUsageCollector({ type: KIBANA_REPORTING_TYPE, - isReady, fetch: (callCluster: ESCallCluster) => getReportingUsage(server, callCluster, exportTypesRegistry), + isReady, /* * Format the response data into a model for internal upload @@ -50,16 +51,18 @@ export function getReportingUsageCollector( } export function registerReportingUsageCollector( - usageCollection: UsageCollectionSetup, + reporting: ReportingCore, server: ServerFacade, - isReady: () => boolean, - exportTypesRegistry: ExportTypesRegistry + usageCollection: UsageCollectionSetup ) { + const exportTypesRegistry = reporting.getExportTypesRegistry(); + const collectionIsReady = reporting.pluginHasStarted.bind(reporting); + const collector = getReportingUsageCollector( - usageCollection, server, - isReady, - exportTypesRegistry + usageCollection, + exportTypesRegistry, + collectionIsReady ); usageCollection.registerCollector(collector); } diff --git a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts new file mode 100644 index 0000000000000..2cd129d47b3f9 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_reportingplugin.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('../server/routes'); +jest.mock('../server/usage'); +jest.mock('../server/browsers'); +jest.mock('../server/browsers'); +jest.mock('../server/lib/create_queue'); +jest.mock('../server/lib/enqueue_job'); +jest.mock('../server/lib/validate'); +jest.mock('../log_configuration'); + +import { EventEmitter } from 'events'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { coreMock } from 'src/core/server/mocks'; +import { ReportingPlugin, ReportingCore } from '../server'; +import { ReportingSetupDeps, ReportingStartDeps } from '../server/types'; + +export const createMockSetupDeps = (setupMock?: any): ReportingSetupDeps => ({ + elasticsearch: setupMock.elasticsearch, + security: setupMock.security, + usageCollection: {} as any, + __LEGACY: { plugins: { xpack_main: { status: new EventEmitter() } } } as any, +}); + +export const createMockStartDeps = (startMock?: any): ReportingStartDeps => ({ + data: startMock.data, + elasticsearch: startMock.elasticsearch, + __LEGACY: {} as any, +}); + +const createMockReportingPlugin = async (config = {}): Promise => { + const plugin = new ReportingPlugin(coreMock.createPluginInitializerContext(config)); + const setupMock = coreMock.createSetup(); + const coreStartMock = coreMock.createStart(); + const startMock = { + ...coreStartMock, + data: { fieldFormats: {} }, + }; + + await plugin.setup(setupMock, createMockSetupDeps(setupMock)); + await plugin.start(startMock, createMockStartDeps(startMock)); + + return plugin; +}; + +export const createMockReportingCore = async (config = {}): Promise => { + const plugin = await createMockReportingPlugin(config); + return plugin.getReportingCore(); +}; diff --git a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts index 226355f5edc61..bb7851ba036a9 100644 --- a/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts +++ b/x-pack/legacy/plugins/reporting/test_helpers/create_mock_server.ts @@ -8,9 +8,6 @@ import { ServerFacade } from '../types'; export const createMockServer = ({ settings = {} }: any): ServerFacade => { const mockServer = { - expose: () => { - ' '; - }, config: memoize(() => ({ get: jest.fn() })), info: { protocol: 'http', @@ -24,10 +21,6 @@ export const createMockServer = ({ settings = {} }: any): ServerFacade => { }), }, }, - savedObjects: { - getScopedSavedObjectsClient: jest.fn(), - }, - uiSettingsServiceFactory: jest.fn().mockReturnValue({ get: jest.fn() }), }; const defaultSettings: any = { diff --git a/x-pack/legacy/plugins/reporting/test_helpers/index.ts b/x-pack/legacy/plugins/reporting/test_helpers/index.ts new file mode 100644 index 0000000000000..7fbc5661d5211 --- /dev/null +++ b/x-pack/legacy/plugins/reporting/test_helpers/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { createMockServer } from './create_mock_server'; +export { createMockReportingCore } from './create_mock_reportingplugin'; diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index a4ff39b23747d..1549c173b3d6e 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -6,16 +6,15 @@ import { EventEmitter } from 'events'; import { ResponseObject } from 'hapi'; -import { ElasticsearchServiceSetup } from 'kibana/server'; import { Legacy } from 'kibana'; +import { ElasticsearchServiceSetup } from 'kibana/server'; import { CallCluster } from '../../../../src/legacy/core_plugins/elasticsearch'; import { CancellationToken } from './common/cancellation_token'; import { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; import { BrowserType } from './server/browsers/types'; import { LevelLogger } from './server/lib/level_logger'; -import { LegacySetup, ReportingSetupDeps } from './server/plugin'; - -export type ReportingPlugin = object; // For Plugin contract +import { ReportingCore } from './server/core'; +import { LegacySetup, ReportingStartDeps, ReportingSetup, ReportingStart } from './server/types'; export type Job = EventEmitter & { id: string; @@ -65,6 +64,7 @@ interface GenerateExportTypePayload { /* * Legacy System + * TODO: move to server/types */ export type ServerFacade = LegacySetup; @@ -179,6 +179,15 @@ export interface CryptoFactory { decrypt: (headers?: string) => any; } +export interface IndexPatternSavedObject { + attributes: { + fieldFormatMap: string; + }; + id: string; + type: string; + version: string; +} + export interface TimeRangeParams { timezone: string; min: Date | string | number; @@ -214,10 +223,6 @@ export interface JobDocOutput { size: number; } -export interface ESQueue { - addJob: (type: string, payload: object, options: object) => Job; -} - export interface ESQueueWorker { on: (event: string, handler: any) => void; } @@ -267,8 +272,9 @@ type GenericWorkerFn = ( ...workerRestArgs: any[] ) => void | Promise; -export interface ESQueueInstance { - registerWorker: ( +export interface ESQueueInstance { + addJob: (type: string, payload: unknown, options: object) => Job; + registerWorker: ( pluginId: string, workerFn: GenericWorkerFn, workerOptions: ESQueueWorkerOptions @@ -276,18 +282,17 @@ export interface ESQueueInstance { } export type CreateJobFactory = ( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, logger: LevelLogger ) => CreateJobFnType; export type ExecuteJobFactory = ( + reporting: ReportingCore, server: ServerFacade, elasticsearch: ElasticsearchServiceSetup, - logger: LevelLogger, - opts: { - browserDriverFactory: HeadlessChromiumDriverFactory; - } -) => ExecuteJobFnType; + logger: LevelLogger +) => Promise; export interface ExportTypeDefinition< JobParamsType, @@ -309,7 +314,6 @@ export { CancellationToken } from './common/cancellation_token'; export { HeadlessChromiumDriver } from './server/browsers/chromium/driver'; export { HeadlessChromiumDriverFactory } from './server/browsers/chromium/driver_factory'; export { ExportTypesRegistry } from './server/lib/export_types_registry'; - // Prefer to import this type using: `import { LevelLogger } from 'relative/path/server/lib';` export { LevelLogger as Logger }; From 918c0dec9f1f24ca9b347f33f1b832e132d44218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Fri, 14 Feb 2020 15:38:45 -0500 Subject: [PATCH 04/65] Increase stability when initializing the Elasticsearch index for the event log (#57465) * Fix ILM policy creation * Handle errors thrown in scenario multiple Kibana instances are started at the same time * Fix tests and cleanup * Start adding tests * Refactor tests, add index template failure test * Create cluster client adapter to facilitate testing and isolation * Fix places calling callEs still Co-authored-by: Elastic Machine --- .../server/es/cluster_client_adapter.mock.ts | 24 +++ .../server/es/cluster_client_adapter.test.ts | 196 ++++++++++++++++++ .../server/es/cluster_client_adapter.ts | 126 +++++++++++ .../event_log/server/es/context.mock.ts | 60 ++---- x-pack/plugins/event_log/server/es/context.ts | 30 +-- .../event_log/server/es/documents.test.ts | 14 +- .../plugins/event_log/server/es/documents.ts | 7 +- .../plugins/event_log/server/es/init.test.ts | 64 ++++++ x-pack/plugins/event_log/server/es/init.ts | 121 +++-------- .../plugins/event_log/server/es/names.mock.ts | 23 ++ .../server/event_log_service.test.ts | 8 +- .../event_log/server/event_logger.test.ts | 5 +- .../plugins/event_log/server/event_logger.ts | 2 +- 13 files changed, 495 insertions(+), 185 deletions(-) create mode 100644 x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts create mode 100644 x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts create mode 100644 x-pack/plugins/event_log/server/es/cluster_client_adapter.ts create mode 100644 x-pack/plugins/event_log/server/es/init.test.ts create mode 100644 x-pack/plugins/event_log/server/es/names.mock.ts diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts new file mode 100644 index 0000000000000..87e8fb0f521a9 --- /dev/null +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IClusterClientAdapter } from './cluster_client_adapter'; + +const createClusterClientMock = () => { + const mock: jest.Mocked = { + indexDocument: jest.fn(), + doesIlmPolicyExist: jest.fn(), + createIlmPolicy: jest.fn(), + doesIndexTemplateExist: jest.fn(), + createIndexTemplate: jest.fn(), + doesAliasExist: jest.fn(), + createIndex: jest.fn(), + }; + return mock; +}; + +export const clusterClientAdapterMock = { + create: createClusterClientMock, +}; diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts new file mode 100644 index 0000000000000..ecefd4bfa271e --- /dev/null +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -0,0 +1,196 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ClusterClient, Logger } from '../../../../../src/core/server'; +import { elasticsearchServiceMock, loggingServiceMock } from '../../../../../src/core/server/mocks'; +import { ClusterClientAdapter, IClusterClientAdapter } from './cluster_client_adapter'; + +type EsClusterClient = Pick, 'callAsInternalUser' | 'asScoped'>; + +let logger: Logger; +let clusterClient: EsClusterClient; +let clusterClientAdapter: IClusterClientAdapter; + +beforeEach(() => { + logger = loggingServiceMock.createLogger(); + clusterClient = elasticsearchServiceMock.createClusterClient(); + clusterClientAdapter = new ClusterClientAdapter({ + logger, + clusterClient, + }); +}); + +describe('indexDocument', () => { + test('should call cluster client with given doc', async () => { + await clusterClientAdapter.indexDocument({ args: true }); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('index', { + args: true, + }); + }); + + test('should throw error when cluster client throws an error', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(new Error('Fail')); + await expect( + clusterClientAdapter.indexDocument({ args: true }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); + }); +}); + +describe('doesIlmPolicyExist', () => { + const notFoundError = new Error('Not found') as any; + notFoundError.statusCode = 404; + + test('should call cluster with proper arguments', async () => { + await clusterClientAdapter.doesIlmPolicyExist('foo'); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('transport.request', { + method: 'GET', + path: '_ilm/policy/foo', + }); + }); + + test('should return false when 404 error is returned by Elasticsearch', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(notFoundError); + await expect(clusterClientAdapter.doesIlmPolicyExist('foo')).resolves.toEqual(false); + }); + + test('should throw error when error is not 404', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(new Error('Fail')); + await expect( + clusterClientAdapter.doesIlmPolicyExist('foo') + ).rejects.toThrowErrorMatchingInlineSnapshot(`"error checking existance of ilm policy: Fail"`); + }); + + test('should return true when no error is thrown', async () => { + await expect(clusterClientAdapter.doesIlmPolicyExist('foo')).resolves.toEqual(true); + }); +}); + +describe('createIlmPolicy', () => { + test('should call cluster client with given policy', async () => { + clusterClient.callAsInternalUser.mockResolvedValue({ success: true }); + await clusterClientAdapter.createIlmPolicy('foo', { args: true }); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('transport.request', { + method: 'PUT', + path: '_ilm/policy/foo', + body: { args: true }, + }); + }); + + test('should throw error when call cluster client throws', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(new Error('Fail')); + await expect( + clusterClientAdapter.createIlmPolicy('foo', { args: true }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"error creating ilm policy: Fail"`); + }); +}); + +describe('doesIndexTemplateExist', () => { + test('should call cluster with proper arguments', async () => { + await clusterClientAdapter.doesIndexTemplateExist('foo'); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.existsTemplate', { + name: 'foo', + }); + }); + + test('should return true when call cluster returns true', async () => { + clusterClient.callAsInternalUser.mockResolvedValue(true); + await expect(clusterClientAdapter.doesIndexTemplateExist('foo')).resolves.toEqual(true); + }); + + test('should return false when call cluster returns false', async () => { + clusterClient.callAsInternalUser.mockResolvedValue(false); + await expect(clusterClientAdapter.doesIndexTemplateExist('foo')).resolves.toEqual(false); + }); + + test('should throw error when call cluster throws an error', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(new Error('Fail')); + await expect( + clusterClientAdapter.doesIndexTemplateExist('foo') + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"error checking existance of index template: Fail"` + ); + }); +}); + +describe('createIndexTemplate', () => { + test('should call cluster with given template', async () => { + await clusterClientAdapter.createIndexTemplate('foo', { args: true }); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.putTemplate', { + name: 'foo', + create: true, + body: { args: true }, + }); + }); + + test(`should throw error if index template still doesn't exist after error is thrown`, async () => { + clusterClient.callAsInternalUser.mockRejectedValueOnce(new Error('Fail')); + clusterClient.callAsInternalUser.mockResolvedValueOnce(false); + await expect( + clusterClientAdapter.createIndexTemplate('foo', { args: true }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"error creating index template: Fail"`); + }); + + test('should not throw error if index template exists after error is thrown', async () => { + clusterClient.callAsInternalUser.mockRejectedValueOnce(new Error('Fail')); + clusterClient.callAsInternalUser.mockResolvedValueOnce(true); + await clusterClientAdapter.createIndexTemplate('foo', { args: true }); + }); +}); + +describe('doesAliasExist', () => { + test('should call cluster with proper arguments', async () => { + await clusterClientAdapter.doesAliasExist('foo'); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.existsAlias', { + name: 'foo', + }); + }); + + test('should return true when call cluster returns true', async () => { + clusterClient.callAsInternalUser.mockResolvedValueOnce(true); + await expect(clusterClientAdapter.doesAliasExist('foo')).resolves.toEqual(true); + }); + + test('should return false when call cluster returns false', async () => { + clusterClient.callAsInternalUser.mockResolvedValueOnce(false); + await expect(clusterClientAdapter.doesAliasExist('foo')).resolves.toEqual(false); + }); + + test('should throw error when call cluster throws an error', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(new Error('Fail')); + await expect( + clusterClientAdapter.doesAliasExist('foo') + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"error checking existance of initial index: Fail"` + ); + }); +}); + +describe('createIndex', () => { + test('should call cluster with proper arguments', async () => { + await clusterClientAdapter.createIndex('foo'); + expect(clusterClient.callAsInternalUser).toHaveBeenCalledWith('indices.create', { + index: 'foo', + }); + }); + + test('should throw error when not getting an error of type resource_already_exists_exception', async () => { + clusterClient.callAsInternalUser.mockRejectedValue(new Error('Fail')); + await expect( + clusterClientAdapter.createIndex('foo') + ).rejects.toThrowErrorMatchingInlineSnapshot(`"error creating initial index: Fail"`); + }); + + test(`shouldn't throw when an error of type resource_already_exists_exception is thrown`, async () => { + const err = new Error('Already exists') as any; + err.body = { + error: { + type: 'resource_already_exists_exception', + }, + }; + clusterClient.callAsInternalUser.mockRejectedValue(err); + await clusterClientAdapter.createIndex('foo'); + }); +}); diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts new file mode 100644 index 0000000000000..c74eeacc9bb19 --- /dev/null +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -0,0 +1,126 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Logger, ClusterClient } from '../../../../../src/core/server'; + +export type EsClusterClient = Pick; +export type IClusterClientAdapter = PublicMethodsOf; + +export interface ConstructorOpts { + logger: Logger; + clusterClient: EsClusterClient; +} + +export class ClusterClientAdapter { + private readonly logger: Logger; + private readonly clusterClient: EsClusterClient; + + constructor(opts: ConstructorOpts) { + this.logger = opts.logger; + this.clusterClient = opts.clusterClient; + } + + public async indexDocument(doc: any): Promise { + await this.callEs('index', doc); + } + + public async doesIlmPolicyExist(policyName: string): Promise { + const request = { + method: 'GET', + path: `_ilm/policy/${policyName}`, + }; + try { + await this.callEs('transport.request', request); + } catch (err) { + if (err.statusCode === 404) return false; + throw new Error(`error checking existance of ilm policy: ${err.message}`); + } + return true; + } + + public async createIlmPolicy(policyName: string, policy: any): Promise { + const request = { + method: 'PUT', + path: `_ilm/policy/${policyName}`, + body: policy, + }; + try { + await this.callEs('transport.request', request); + } catch (err) { + throw new Error(`error creating ilm policy: ${err.message}`); + } + } + + public async doesIndexTemplateExist(name: string): Promise { + let result; + try { + result = await this.callEs('indices.existsTemplate', { name }); + } catch (err) { + throw new Error(`error checking existance of index template: ${err.message}`); + } + return result as boolean; + } + + public async createIndexTemplate(name: string, template: any): Promise { + const addTemplateParams = { + name, + create: true, + body: template, + }; + try { + await this.callEs('indices.putTemplate', addTemplateParams); + } catch (err) { + // The error message doesn't have a type attribute we can look to guarantee it's due + // to the template already existing (only long message) so we'll check ourselves to see + // if the template now exists. This scenario would happen if you startup multiple Kibana + // instances at the same time. + const existsNow = await this.doesIndexTemplateExist(name); + if (!existsNow) { + throw new Error(`error creating index template: ${err.message}`); + } + } + } + + public async doesAliasExist(name: string): Promise { + let result; + try { + result = await this.callEs('indices.existsAlias', { name }); + } catch (err) { + throw new Error(`error checking existance of initial index: ${err.message}`); + } + return result as boolean; + } + + public async createIndex(name: string): Promise { + try { + await this.callEs('indices.create', { index: name }); + } catch (err) { + if (err.body?.error?.type !== 'resource_already_exists_exception') { + throw new Error(`error creating initial index: ${err.message}`); + } + } + } + + private async callEs(operation: string, body?: any): Promise { + try { + this.debug(`callEs(${operation}) calls:`, body); + const result = await this.clusterClient.callAsInternalUser(operation, body); + this.debug(`callEs(${operation}) result:`, result); + return result; + } catch (err) { + this.debug(`callEs(${operation}) error:`, { + message: err.message, + statusCode: err.statusCode, + }); + throw err; + } + } + + private debug(message: string, object?: any) { + const objectString = object == null ? '' : JSON.stringify(object); + this.logger.debug(`esContext: ${message} ${objectString}`); + } +} diff --git a/x-pack/plugins/event_log/server/es/context.mock.ts b/x-pack/plugins/event_log/server/es/context.mock.ts index fb894ce6e7787..6581cd689e43d 100644 --- a/x-pack/plugins/event_log/server/es/context.mock.ts +++ b/x-pack/plugins/event_log/server/es/context.mock.ts @@ -4,43 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, ClusterClient } from '../../../../../src/core/server'; import { EsContext } from './context'; - -import { EsNames } from './names'; - -export type EsClusterClient = Pick; - -export interface EsError { - readonly statusCode: number; - readonly message: string; -} - -interface CreateMockEsContextParams { - logger: Logger; - esNames: EsNames; -} - -export function createMockEsContext(params: CreateMockEsContextParams): EsContext { - return new EsContextMock(params); -} - -class EsContextMock implements EsContext { - public logger: Logger; - public esNames: EsNames; - - constructor(params: CreateMockEsContextParams) { - this.logger = params.logger; - this.esNames = params.esNames; - } - - initialize() {} - - async waitTillReady(): Promise { - return true; - } - - async callEs(operation: string, body?: any): Promise { - return {}; - } -} +import { namesMock } from './names.mock'; +import { IClusterClientAdapter } from './cluster_client_adapter'; +import { loggingServiceMock } from '../../../../../src/core/server/mocks'; +import { clusterClientAdapterMock } from './cluster_client_adapter.mock'; + +const createContextMock = () => { + const mock: jest.Mocked & { + esAdapter: jest.Mocked; + } = { + logger: loggingServiceMock.createLogger(), + esNames: namesMock.create(), + initialize: jest.fn(), + waitTillReady: jest.fn(), + esAdapter: clusterClientAdapterMock.create(), + }; + return mock; +}; + +export const contextMock = { + create: createContextMock, +}; diff --git a/x-pack/plugins/event_log/server/es/context.ts b/x-pack/plugins/event_log/server/es/context.ts index b93c1892d0206..144f44ac8e5ea 100644 --- a/x-pack/plugins/event_log/server/es/context.ts +++ b/x-pack/plugins/event_log/server/es/context.ts @@ -8,6 +8,7 @@ import { Logger, ClusterClient } from 'src/core/server'; import { EsNames, getEsNames } from './names'; import { initializeEs } from './init'; +import { ClusterClientAdapter, IClusterClientAdapter } from './cluster_client_adapter'; import { createReadySignal, ReadySignal } from '../lib/ready_signal'; export type EsClusterClient = Pick; @@ -15,9 +16,9 @@ export type EsClusterClient = Pick; - callEs(operation: string, body?: any): Promise; } export interface EsError { @@ -38,16 +39,19 @@ export interface EsContextCtorParams { class EsContextImpl implements EsContext { public readonly logger: Logger; public readonly esNames: EsNames; - private readonly clusterClient: EsClusterClient; + public esAdapter: IClusterClientAdapter; private readonly readySignal: ReadySignal; private initialized: boolean; constructor(params: EsContextCtorParams) { this.logger = params.logger; this.esNames = getEsNames(params.indexNameRoot); - this.clusterClient = params.clusterClient; this.readySignal = createReadySignal(); this.initialized = false; + this.esAdapter = new ClusterClientAdapter({ + logger: params.logger, + clusterClient: params.clusterClient, + }); } initialize() { @@ -73,27 +77,7 @@ class EsContextImpl implements EsContext { return await this.readySignal.wait(); } - async callEs(operation: string, body?: any): Promise { - try { - this.debug(`callEs(${operation}) calls:`, body); - const result = await this.clusterClient.callAsInternalUser(operation, body); - this.debug(`callEs(${operation}) result:`, result); - return result; - } catch (err) { - this.debug(`callEs(${operation}) error:`, { - message: err.message, - statusCode: err.statusCode, - }); - throw err; - } - } - private async _initialize() { await initializeEs(this); } - - private debug(message: string, object?: any) { - const objectString = object == null ? '' : JSON.stringify(object); - this.logger.debug(`esContext: ${message} ${objectString}`); - } } diff --git a/x-pack/plugins/event_log/server/es/documents.test.ts b/x-pack/plugins/event_log/server/es/documents.test.ts index 2dec23c61de2f..7edca4b3943a6 100644 --- a/x-pack/plugins/event_log/server/es/documents.test.ts +++ b/x-pack/plugins/event_log/server/es/documents.test.ts @@ -21,23 +21,13 @@ describe('getIndexTemplate()', () => { const esNames = getEsNames('XYZ'); test('returns the correct details of the index template', () => { - const indexTemplate = getIndexTemplate(esNames, true); + const indexTemplate = getIndexTemplate(esNames); expect(indexTemplate.index_patterns).toEqual([esNames.indexPattern]); expect(indexTemplate.aliases[esNames.alias]).toEqual({}); expect(indexTemplate.settings.number_of_shards).toBeGreaterThanOrEqual(0); expect(indexTemplate.settings.number_of_replicas).toBeGreaterThanOrEqual(0); - expect(indexTemplate.mappings).toMatchObject({}); - }); - - test('returns correct index template bits for ilm when ilm is supported', () => { - const indexTemplate = getIndexTemplate(esNames, true); expect(indexTemplate.settings['index.lifecycle.name']).toBe(esNames.ilmPolicy); expect(indexTemplate.settings['index.lifecycle.rollover_alias']).toBe(esNames.alias); - }); - - test('returns correct index template bits for ilm when ilm is not supported', () => { - const indexTemplate = getIndexTemplate(esNames, false); - expect(indexTemplate.settings['index.lifecycle.name']).toBeUndefined(); - expect(indexTemplate.settings['index.lifecycle.rollover_alias']).toBeUndefined(); + expect(indexTemplate.mappings).toMatchObject({}); }); }); diff --git a/x-pack/plugins/event_log/server/es/documents.ts b/x-pack/plugins/event_log/server/es/documents.ts index dfc544f8a41cb..09dd7383c4c5e 100644 --- a/x-pack/plugins/event_log/server/es/documents.ts +++ b/x-pack/plugins/event_log/server/es/documents.ts @@ -8,7 +8,7 @@ import { EsNames } from './names'; import mappings from '../../generated/mappings.json'; // returns the body of an index template used in an ES indices.putTemplate call -export function getIndexTemplate(esNames: EsNames, ilmExists: boolean) { +export function getIndexTemplate(esNames: EsNames) { const indexTemplateBody: any = { index_patterns: [esNames.indexPattern], aliases: { @@ -23,11 +23,6 @@ export function getIndexTemplate(esNames: EsNames, ilmExists: boolean) { mappings, }; - if (!ilmExists) { - delete indexTemplateBody.settings['index.lifecycle.name']; - delete indexTemplateBody.settings['index.lifecycle.rollover_alias']; - } - return indexTemplateBody; } diff --git a/x-pack/plugins/event_log/server/es/init.test.ts b/x-pack/plugins/event_log/server/es/init.test.ts new file mode 100644 index 0000000000000..ad237e522c0a5 --- /dev/null +++ b/x-pack/plugins/event_log/server/es/init.test.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { contextMock } from './context.mock'; +import { initializeEs } from './init'; + +describe('initializeEs', () => { + let esContext = contextMock.create(); + + beforeEach(() => { + esContext = contextMock.create(); + }); + + test(`should create ILM policy if it doesn't exist`, async () => { + esContext.esAdapter.doesIlmPolicyExist.mockResolvedValue(false); + + await initializeEs(esContext); + expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); + expect(esContext.esAdapter.createIlmPolicy).toHaveBeenCalled(); + }); + + test(`shouldn't create ILM policy if it exists`, async () => { + esContext.esAdapter.doesIlmPolicyExist.mockResolvedValue(true); + + await initializeEs(esContext); + expect(esContext.esAdapter.doesIlmPolicyExist).toHaveBeenCalled(); + expect(esContext.esAdapter.createIlmPolicy).not.toHaveBeenCalled(); + }); + + test(`should create index template if it doesn't exist`, async () => { + esContext.esAdapter.doesIndexTemplateExist.mockResolvedValue(false); + + await initializeEs(esContext); + expect(esContext.esAdapter.doesIndexTemplateExist).toHaveBeenCalled(); + expect(esContext.esAdapter.createIndexTemplate).toHaveBeenCalled(); + }); + + test(`shouldn't create index template if it already exists`, async () => { + esContext.esAdapter.doesIndexTemplateExist.mockResolvedValue(true); + + await initializeEs(esContext); + expect(esContext.esAdapter.doesIndexTemplateExist).toHaveBeenCalled(); + expect(esContext.esAdapter.createIndexTemplate).not.toHaveBeenCalled(); + }); + + test(`should create initial index if it doesn't exist`, async () => { + esContext.esAdapter.doesAliasExist.mockResolvedValue(false); + + await initializeEs(esContext); + expect(esContext.esAdapter.doesAliasExist).toHaveBeenCalled(); + expect(esContext.esAdapter.createIndex).toHaveBeenCalled(); + }); + + test(`shouldn't create initial index if it already exists`, async () => { + esContext.esAdapter.doesAliasExist.mockResolvedValue(true); + + await initializeEs(esContext); + expect(esContext.esAdapter.doesAliasExist).toHaveBeenCalled(); + expect(esContext.esAdapter.createIndex).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/event_log/server/es/init.ts b/x-pack/plugins/event_log/server/es/init.ts index d87f5bce03475..7094277f7aa9f 100644 --- a/x-pack/plugins/event_log/server/es/init.ts +++ b/x-pack/plugins/event_log/server/es/init.ts @@ -23,25 +23,10 @@ export async function initializeEs(esContext: EsContext): Promise { async function initializeEsResources(esContext: EsContext) { const steps = new EsInitializationSteps(esContext); - let ilmExists: boolean; - // create the ilm policy, if required - ilmExists = await steps.doesIlmPolicyExist(); - if (!ilmExists) { - ilmExists = await steps.createIlmPolicy(); - } - - if (!(await steps.doesIndexTemplateExist())) { - await steps.createIndexTemplate({ ilmExists }); - } - - if (!(await steps.doesInitialIndexExist())) { - await steps.createInitialIndex(); - } -} - -interface AddTemplateOpts { - ilmExists: boolean; + await steps.createIlmPolicyIfNotExists(); + await steps.createIndexTemplateIfNotExists(); + await steps.createInitialIndexIfNotExists(); } class EsInitializationSteps { @@ -49,89 +34,35 @@ class EsInitializationSteps { this.esContext = esContext; } - async doesIlmPolicyExist(): Promise { - const request = { - method: 'GET', - path: `_ilm/policy/${this.esContext.esNames.ilmPolicy}`, - }; - try { - await this.esContext.callEs('transport.request', request); - } catch (err) { - if (err.statusCode === 404) return false; - // TODO: remove following once kibana user can access ilm - if (err.statusCode === 403) return false; - - throw new Error(`error checking existance of ilm policy: ${err.message}`); - } - return true; - } - - async createIlmPolicy(): Promise { - const request = { - method: 'PUT', - path: `_ilm/policy/${this.esContext.esNames.ilmPolicy}`, - body: getIlmPolicy(), - }; - try { - await this.esContext.callEs('transport.request', request); - } catch (err) { - // TODO: remove following once kibana user can access ilm - if (err.statusCode === 403) return false; - throw new Error(`error creating ilm policy: ${err.message}`); + async createIlmPolicyIfNotExists(): Promise { + const exists = await this.esContext.esAdapter.doesIlmPolicyExist( + this.esContext.esNames.ilmPolicy + ); + if (!exists) { + await this.esContext.esAdapter.createIlmPolicy( + this.esContext.esNames.ilmPolicy, + getIlmPolicy() + ); } - return true; } - async doesIndexTemplateExist(): Promise { - const name = this.esContext.esNames.indexTemplate; - let result; - try { - result = await this.esContext.callEs('indices.existsTemplate', { name }); - } catch (err) { - throw new Error(`error checking existance of index template: ${err.message}`); + async createIndexTemplateIfNotExists(): Promise { + const exists = await this.esContext.esAdapter.doesIndexTemplateExist( + this.esContext.esNames.indexTemplate + ); + if (!exists) { + const templateBody = getIndexTemplate(this.esContext.esNames); + await this.esContext.esAdapter.createIndexTemplate( + this.esContext.esNames.indexTemplate, + templateBody + ); } - return result as boolean; } - async createIndexTemplate(opts: AddTemplateOpts): Promise { - const templateBody = getIndexTemplate(this.esContext.esNames, opts.ilmExists); - const addTemplateParams = { - create: true, - name: this.esContext.esNames.indexTemplate, - body: templateBody, - }; - try { - await this.esContext.callEs('indices.putTemplate', addTemplateParams); - } catch (err) { - throw new Error(`error creating index template: ${err.message}`); + async createInitialIndexIfNotExists(): Promise { + const exists = await this.esContext.esAdapter.doesAliasExist(this.esContext.esNames.alias); + if (!exists) { + await this.esContext.esAdapter.createIndex(this.esContext.esNames.initialIndex); } } - - async doesInitialIndexExist(): Promise { - const name = this.esContext.esNames.alias; - let result; - try { - result = await this.esContext.callEs('indices.existsAlias', { name }); - } catch (err) { - throw new Error(`error checking existance of initial index: ${err.message}`); - } - return result as boolean; - } - - async createInitialIndex(): Promise { - const index = this.esContext.esNames.initialIndex; - try { - await this.esContext.callEs('indices.create', { index }); - } catch (err) { - throw new Error(`error creating initial index: ${err.message}`); - } - } - - debug(message: string) { - this.esContext.logger.debug(message); - } - - warn(message: string) { - this.esContext.logger.warn(message); - } } diff --git a/x-pack/plugins/event_log/server/es/names.mock.ts b/x-pack/plugins/event_log/server/es/names.mock.ts new file mode 100644 index 0000000000000..7b013a0d263da --- /dev/null +++ b/x-pack/plugins/event_log/server/es/names.mock.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EsNames } from './names'; + +const createNamesMock = () => { + const mock: jest.Mocked = { + base: '.kibana', + alias: '.kibana-event-log', + ilmPolicy: '.kibana-event-log-policy', + indexPattern: '.kibana-event-log-*', + initialIndex: '.kibana-event-log-000001', + indexTemplate: '.kibana-event-log-template', + }; + return mock; +}; + +export const namesMock = { + create: createNamesMock, +}; diff --git a/x-pack/plugins/event_log/server/event_log_service.test.ts b/x-pack/plugins/event_log/server/event_log_service.test.ts index c7e752d1a652b..3b250b7462009 100644 --- a/x-pack/plugins/event_log/server/event_log_service.test.ts +++ b/x-pack/plugins/event_log/server/event_log_service.test.ts @@ -6,18 +6,14 @@ import { IEventLogConfig } from './types'; import { EventLogService } from './event_log_service'; -import { getEsNames } from './es/names'; -import { createMockEsContext } from './es/context.mock'; +import { contextMock } from './es/context.mock'; import { loggingServiceMock } from '../../../../src/core/server/logging/logging_service.mock'; const loggingService = loggingServiceMock.create(); const systemLogger = loggingService.get(); describe('EventLogService', () => { - const esContext = createMockEsContext({ - esNames: getEsNames('ABC'), - logger: systemLogger, - }); + const esContext = contextMock.create(); function getService(config: IEventLogConfig) { const { enabled, logEntries, indexEntries } = config; diff --git a/x-pack/plugins/event_log/server/event_logger.test.ts b/x-pack/plugins/event_log/server/event_logger.test.ts index 97e52ad04dd08..673bac4f396e1 100644 --- a/x-pack/plugins/event_log/server/event_logger.test.ts +++ b/x-pack/plugins/event_log/server/event_logger.test.ts @@ -7,9 +7,8 @@ import { IEvent, IEventLogger, IEventLogService } from './index'; import { ECS_VERSION } from './types'; import { EventLogService } from './event_log_service'; -import { getEsNames } from './es/names'; import { EsContext } from './es/context'; -import { createMockEsContext } from './es/context.mock'; +import { contextMock } from './es/context.mock'; import { loggerMock, MockedLogger } from '../../../../src/core/server/logging/logger.mock'; import { delay } from './lib/delay'; import { EVENT_LOGGED_PREFIX } from './event_logger'; @@ -24,7 +23,7 @@ describe('EventLogger', () => { beforeEach(() => { systemLogger = loggerMock.create(); - esContext = createMockEsContext({ esNames: getEsNames('ABC'), logger: systemLogger }); + esContext = contextMock.create(); service = new EventLogService({ esContext, systemLogger, diff --git a/x-pack/plugins/event_log/server/event_logger.ts b/x-pack/plugins/event_log/server/event_logger.ts index 891abda947fc8..f5149da069953 100644 --- a/x-pack/plugins/event_log/server/event_logger.ts +++ b/x-pack/plugins/event_log/server/event_logger.ts @@ -171,7 +171,7 @@ function indexEventDoc(esContext: EsContext, doc: Doc): void { async function indexLogEventDoc(esContext: EsContext, doc: any) { esContext.logger.debug(`writing to event log: ${JSON.stringify(doc)}`); await esContext.waitTillReady(); - await esContext.callEs('index', doc); + await esContext.esAdapter.indexDocument(doc); esContext.logger.debug(`writing to event log complete`); } From e319269e18aab3be72c13138efa062102a7454ae Mon Sep 17 00:00:00 2001 From: Chris Roberson Date: Fri, 14 Feb 2020 16:07:44 -0500 Subject: [PATCH 05/65] [Monitoring] Support shipping directly to the monitoring cluster (#57022) * Support shipping directly to the monitoring cluster * Add timestamp * PR feedback * Use utc --- .../plugins/monitoring/common/constants.ts | 2 +- x-pack/legacy/plugins/monitoring/config.js | 41 +++++++++ x-pack/legacy/plugins/monitoring/index.ts | 7 +- .../es_client/parse_elasticsearch_config.ts | 9 +- .../__tests__/bulk_uploader.js | 92 +++++++++++++++++++ .../server/kibana_monitoring/bulk_uploader.js | 29 +++++- .../lib/send_bulk_payload.js | 55 ++++++++++- .../plugins/monitoring/server/plugin.js | 16 ++-- 8 files changed, 233 insertions(+), 18 deletions(-) diff --git a/x-pack/legacy/plugins/monitoring/common/constants.ts b/x-pack/legacy/plugins/monitoring/common/constants.ts index 1fc322a0de395..1fb6acdb915b8 100644 --- a/x-pack/legacy/plugins/monitoring/common/constants.ts +++ b/x-pack/legacy/plugins/monitoring/common/constants.ts @@ -17,7 +17,7 @@ export const KIBANA_MONITORING_LOGGING_TAG = 'kibana-monitoring'; * The Monitoring API version is the expected API format that we export and expect to import. * @type {string} */ -export const MONITORING_SYSTEM_API_VERSION = '6'; +export const MONITORING_SYSTEM_API_VERSION = '7'; /** * The type name used within the Monitoring index to publish Kibana ops stats. * @type {string} diff --git a/x-pack/legacy/plugins/monitoring/config.js b/x-pack/legacy/plugins/monitoring/config.js index ec4c00ccbea11..bd35d5271132d 100644 --- a/x-pack/legacy/plugins/monitoring/config.js +++ b/x-pack/legacy/plugins/monitoring/config.js @@ -84,6 +84,47 @@ export const config = Joi => { interval: Joi.number().default(10000), // op status metrics get buffered at `ops.interval` and flushed to the bulk endpoint at this interval }).default(), }).default(), + elasticsearch: Joi.object({ + customHeaders: Joi.object().default({}), + logQueries: Joi.boolean().default(false), + requestHeadersWhitelist: Joi.array() + .items() + .single() + .default(DEFAULT_REQUEST_HEADERS), + sniffOnStart: Joi.boolean().default(false), + sniffInterval: Joi.number() + .allow(false) + .default(false), + sniffOnConnectionFault: Joi.boolean().default(false), + hosts: Joi.array() + .items(Joi.string().uri({ scheme: ['http', 'https'] })) + .single(), // if empty, use Kibana's connection config + username: Joi.string(), + password: Joi.string(), + requestTimeout: Joi.number().default(30000), + pingTimeout: Joi.number().default(30000), + ssl: Joi.object({ + verificationMode: Joi.string() + .valid('none', 'certificate', 'full') + .default('full'), + certificateAuthorities: Joi.array() + .single() + .items(Joi.string()), + certificate: Joi.string(), + key: Joi.string(), + keyPassphrase: Joi.string(), + keystore: Joi.object({ + path: Joi.string(), + password: Joi.string(), + }).default(), + truststore: Joi.object({ + path: Joi.string(), + password: Joi.string(), + }).default(), + alwaysPresentCertificate: Joi.boolean().default(false), + }).default(), + apiVersion: Joi.string().default('master'), + }).default(), cluster_alerts: Joi.object({ enabled: Joi.boolean().default(true), email_notifications: Joi.object({ diff --git a/x-pack/legacy/plugins/monitoring/index.ts b/x-pack/legacy/plugins/monitoring/index.ts index c596beb117971..1186fde52dc46 100644 --- a/x-pack/legacy/plugins/monitoring/index.ts +++ b/x-pack/legacy/plugins/monitoring/index.ts @@ -44,6 +44,8 @@ const validConfigOptions: string[] = [ 'monitoring.ui.container.logstash.enabled', 'monitoring.tests.cloud_detector.enabled', 'monitoring.kibana.collection.interval', + 'monitoring.elasticsearch.hosts', + 'monitoring.elasticsearch', 'monitoring.ui.elasticsearch.hosts', 'monitoring.ui.elasticsearch', 'monitoring.xpack_api_polling_frequency_millis', @@ -77,7 +79,7 @@ export const monitoring = (kibana: LegacyPluginApi): LegacyPluginSpec => { uiExports: getUiExports(), deprecations, - init(server: Server) { + async init(server: Server) { const serverConfig = server.config(); const { getOSInfo, plugins, injectUiAppVars } = server as typeof server & { getOSInfo?: any }; const log = (...args: Parameters) => server.log(...args); @@ -127,7 +129,8 @@ export const monitoring = (kibana: LegacyPluginApi): LegacyPluginSpec => { }, }; - new Plugin().setup(coreSetup, pluginsSetup, __LEGACY); + const plugin = new Plugin(); + await plugin.setup(coreSetup, pluginsSetup, __LEGACY); }, postInit(server: Server) { diff --git a/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts index 728b3433bf06c..87b225e48c158 100644 --- a/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts +++ b/x-pack/legacy/plugins/monitoring/server/es_client/parse_elasticsearch_config.ts @@ -16,10 +16,13 @@ const KEY = 'monitoring.ui.elasticsearch'; * TODO: this code can be removed when this plugin is migrated to the Kibana Platform, * at that point the ElasticsearchClient and ElasticsearchConfig should be used instead */ -export const parseElasticsearchConfig = (config: any) => { - const es = config.get(KEY); +export const parseElasticsearchConfig = (config: any, configKey: string = KEY) => { + const es = config.get(configKey); + if (!es) { + return {}; + } - const errorPrefix = `[config validation of [${KEY}].ssl]`; + const errorPrefix = `[config validation of [${configKey}].ssl]`; if (es.ssl?.key && es.ssl?.keystore?.path) { throw new Error(`${errorPrefix}: cannot use [key] when [keystore.path] is specified`); } diff --git a/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js b/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js index ef7d3f1224fab..2fefdbd4f0943 100644 --- a/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js +++ b/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/__tests__/bulk_uploader.js @@ -6,8 +6,10 @@ import { noop } from 'lodash'; import sinon from 'sinon'; +import moment from 'moment'; import expect from '@kbn/expect'; import { BulkUploader } from '../bulk_uploader'; +import { MONITORING_SYSTEM_API_VERSION } from '../../../common/constants'; const FETCH_INTERVAL = 300; const CHECK_DELAY = 500; @@ -52,6 +54,9 @@ describe('BulkUploader', () => { server = { log: sinon.spy(), + config: { + get: sinon.spy(), + }, elasticsearchPlugin: { createCluster: () => cluster, getCluster: () => cluster, @@ -307,5 +312,92 @@ describe('BulkUploader', () => { done(); }, CHECK_DELAY); }); + + it('uses a direct connection to the monitoring cluster, when configured', done => { + const dateInIndex = '2020.02.10'; + const oldNow = moment.now; + moment.now = () => 1581310800000; + const prodClusterUuid = '1sdfd5'; + const prodCluster = { + callWithInternalUser: sinon + .stub() + .withArgs('monitoring.bulk') + .callsFake(arg => { + let resolution = null; + if (arg === 'info') { + resolution = { cluster_uuid: prodClusterUuid }; + } + return new Promise(resolve => resolve(resolution)); + }), + }; + const monitoringCluster = { + callWithInternalUser: sinon + .stub() + .withArgs('bulk') + .callsFake(() => { + return new Promise(resolve => setTimeout(resolve, CHECK_DELAY + 1)); + }), + }; + + const collectorFetch = sinon.stub().returns({ + type: 'kibana_stats', + result: { type: 'kibana_stats', payload: { testData: 12345 } }, + }); + + const collectors = new MockCollectorSet(server, [ + { + fetch: collectorFetch, + isReady: () => true, + formatForBulkUpload: result => result, + isUsageCollector: false, + }, + ]); + const customServer = { + ...server, + elasticsearchPlugin: { + createCluster: () => monitoringCluster, + getCluster: name => { + if (name === 'admin' || name === 'data') { + return prodCluster; + } + return monitoringCluster; + }, + }, + config: { + get: key => { + if (key === 'monitoring.elasticsearch') { + return { + hosts: ['http://localhost:9200'], + username: 'tester', + password: 'testing', + ssl: {}, + }; + } + return null; + }, + }, + }; + const kbnServerStatus = { toJSON: () => ({ overall: { state: 'green' } }) }; + const kbnServerVersion = 'master'; + const uploader = new BulkUploader({ + ...customServer, + interval: FETCH_INTERVAL, + kbnServerStatus, + kbnServerVersion, + }); + uploader.start(collectors); + setTimeout(() => { + uploader.stop(); + const firstCallArgs = monitoringCluster.callWithInternalUser.firstCall.args; + expect(firstCallArgs[0]).to.be('bulk'); + expect(firstCallArgs[1].body[0].index._index).to.be( + `.monitoring-kibana-${MONITORING_SYSTEM_API_VERSION}-${dateInIndex}` + ); + expect(firstCallArgs[1].body[1].type).to.be('kibana_stats'); + expect(firstCallArgs[1].body[1].cluster_uuid).to.be(prodClusterUuid); + moment.now = oldNow; + done(); + }, CHECK_DELAY); + }); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/bulk_uploader.js b/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/bulk_uploader.js index cf68ec073bebc..7417e6ca804d9 100644 --- a/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/bulk_uploader.js +++ b/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/bulk_uploader.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { defaultsDeep, uniq, compact } from 'lodash'; +import { defaultsDeep, uniq, compact, get } from 'lodash'; import { callClusterFactory } from '../../../xpack_main'; import { @@ -14,6 +14,8 @@ import { } from '../../common/constants'; import { sendBulkPayload, monitoringBulk, getKibanaInfoForStats } from './lib'; +import { parseElasticsearchConfig } from '../es_client/parse_elasticsearch_config'; +import { hasMonitoringCluster } from '../es_client/instantiate_client'; const LOGGING_TAGS = [LOGGING_TAG, KIBANA_MONITORING_LOGGING_TAG]; @@ -39,6 +41,8 @@ export class BulkUploader { throw new Error('interval number of milliseconds is required'); } + this._hasDirectConnectionToMonitoringCluster = false; + this._productionClusterUuid = null; this._timer = null; // Hold sending and fetching usage until monitoring.bulk is successful. This means that we // send usage data on the second tick. But would save a lot of bandwidth fetching usage on @@ -60,6 +64,19 @@ export class BulkUploader { plugins: [monitoringBulk], }); + const directConfig = parseElasticsearchConfig(config, 'monitoring.elasticsearch'); + if (hasMonitoringCluster(directConfig)) { + this._log.info(`Detected direct connection to monitoring cluster`); + this._hasDirectConnectionToMonitoringCluster = true; + this._cluster = elasticsearchPlugin.createCluster('monitoring-direct', directConfig); + elasticsearchPlugin + .getCluster('admin') + .callWithInternalUser('info') + .then(data => { + this._productionClusterUuid = get(data, 'cluster_uuid'); + }); + } + this._callClusterWithInternalUser = callClusterFactory({ plugins: { elasticsearch: elasticsearchPlugin }, }).getCallClusterInternal(); @@ -151,7 +168,6 @@ export class BulkUploader { const data = await usageCollection.bulkFetch(this._callClusterWithInternalUser); const payload = this.toBulkUploadFormat(compact(data), usageCollection); - if (payload) { try { this._log.debug(`Uploading bulk stats payload to the local cluster`); @@ -182,7 +198,14 @@ export class BulkUploader { } async _onPayload(payload) { - return await sendBulkPayload(this._cluster, this._interval, payload); + return await sendBulkPayload( + this._cluster, + this._interval, + payload, + this._log, + this._hasDirectConnectionToMonitoringCluster, + this._productionClusterUuid + ); } /* diff --git a/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/lib/send_bulk_payload.js b/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/lib/send_bulk_payload.js index 3e5c64905da0d..c378c0ad0fa08 100644 --- a/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/lib/send_bulk_payload.js +++ b/x-pack/legacy/plugins/monitoring/server/kibana_monitoring/lib/send_bulk_payload.js @@ -3,13 +3,64 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import moment from 'moment'; +import { chunk, get } from 'lodash'; +import { + MONITORING_SYSTEM_API_VERSION, + KIBANA_SYSTEM_ID, + KIBANA_STATS_TYPE_MONITORING, + KIBANA_SETTINGS_TYPE, +} from '../../../common/constants'; -import { MONITORING_SYSTEM_API_VERSION, KIBANA_SYSTEM_ID } from '../../../common/constants'; +const SUPPORTED_TYPES = [KIBANA_STATS_TYPE_MONITORING, KIBANA_SETTINGS_TYPE]; +export function formatForNormalBulkEndpoint(payload, productionClusterUuid) { + const dateSuffix = moment.utc().format('YYYY.MM.DD'); + return chunk(payload, 2).reduce((accum, chunk) => { + const type = get(chunk[0], 'index._type'); + if (!type || !SUPPORTED_TYPES.includes(type)) { + return accum; + } + + const { timestamp } = chunk[1]; + + accum.push({ + index: { + _index: `.monitoring-kibana-${MONITORING_SYSTEM_API_VERSION}-${dateSuffix}`, + }, + }); + accum.push({ + [type]: chunk[1], + type, + timestamp, + cluster_uuid: productionClusterUuid, + }); + return accum; + }, []); +} /* * Send the Kibana usage data to the ES Monitoring Bulk endpoint */ -export function sendBulkPayload(cluster, interval, payload) { +export async function sendBulkPayload( + cluster, + interval, + payload, + log, + hasDirectConnectionToMonitoringCluster = false, + productionClusterUuid = null +) { + if (hasDirectConnectionToMonitoringCluster) { + if (productionClusterUuid === null) { + log.warn( + `Unable to determine production cluster uuid to use for shipping monitoring data. Kibana monitoring data will appear in a standalone cluster in the Stack Monitoring UI.` + ); + } + const formattedPayload = formatForNormalBulkEndpoint(payload, productionClusterUuid); + return await cluster.callWithInternalUser('bulk', { + body: formattedPayload, + }); + } + return cluster.callWithInternalUser('monitoring.bulk', { system_id: KIBANA_SYSTEM_ID, system_api_version: MONITORING_SYSTEM_API_VERSION, diff --git a/x-pack/legacy/plugins/monitoring/server/plugin.js b/x-pack/legacy/plugins/monitoring/server/plugin.js index c2aed7365f3af..304d2c08a1688 100644 --- a/x-pack/legacy/plugins/monitoring/server/plugin.js +++ b/x-pack/legacy/plugins/monitoring/server/plugin.js @@ -19,7 +19,7 @@ import { getLicenseExpiration } from './alerts/license_expiration'; import { parseElasticsearchConfig } from './es_client/parse_elasticsearch_config'; export class Plugin { - setup(_coreSetup, pluginsSetup, __LEGACY) { + async setup(_coreSetup, pluginsSetup, __LEGACY) { const { plugins, _kbnServer: kbnServer, @@ -59,6 +59,14 @@ export class Plugin { */ const elasticsearchConfig = parseElasticsearchConfig(config); + // Create the dedicated client + await instantiateClient({ + log, + events, + elasticsearchConfig, + elasticsearchPlugin: plugins.elasticsearch, + }); + xpackMainPlugin.status.once('green', async () => { // first time xpack_main turns green /* @@ -67,12 +75,6 @@ export class Plugin { const uiEnabled = config.get('monitoring.ui.enabled'); if (uiEnabled) { - await instantiateClient({ - log, - events, - elasticsearchConfig, - elasticsearchPlugin: plugins.elasticsearch, - }); // Instantiate the dedicated ES client await initMonitoringXpackInfo({ config, log, From 78bff535e03806bc8a452ba69642a55a33ce3066 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Fri, 14 Feb 2020 18:16:50 -0500 Subject: [PATCH 06/65] moving visualize/utils to new platform (#56650) --- .../public/search/aggs/buckets/date_range.ts | 21 +-- .../public/search/aggs/buckets/ip_range.ts | 16 +- .../search/aggs/buckets/lib/date_range.ts | 36 +++++ .../search/aggs/buckets/lib/ip_range.ts | 32 ++++ .../components/metric_vis_component.test.tsx | 10 ++ .../public/metric_vis_type.test.ts | 3 + .../vis_type_vislib/public/legacy_imports.ts | 3 +- .../vislib/components/legend/legend.tsx | 14 +- .../loader/pipeline_helpers/utilities.ts | 149 +----------------- .../field_formats/field_formats_registry.ts | 9 +- .../data/common/field_formats/index.ts | 2 +- .../data/common/field_formats/utils/index.ts | 6 + .../common/field_formats/utils/serialize.ts | 53 +++++++ .../field_formats/field_formats_service.ts | 10 +- .../public/field_formats/utils/deserialize.ts | 129 +++++++++++++++ src/plugins/data/public/index.ts | 4 +- .../index_patterns/index_pattern.test.ts | 5 +- src/plugins/data/public/mocks.ts | 1 + .../query_string_input.test.tsx.snap | 6 + src/plugins/data/server/index.ts | 3 + src/test_utils/public/stub_field_formats.ts | 7 +- .../translations/translations/ja-JP.json | 3 +- .../translations/translations/zh-CN.json | 3 +- 23 files changed, 336 insertions(+), 189 deletions(-) create mode 100644 src/legacy/core_plugins/data/public/search/aggs/buckets/lib/date_range.ts create mode 100644 src/legacy/core_plugins/data/public/search/aggs/buckets/lib/ip_range.ts create mode 100644 src/plugins/data/common/field_formats/utils/serialize.ts create mode 100644 src/plugins/data/public/field_formats/utils/deserialize.ts diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/date_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/date_range.ts index 40ef7329dee6b..1dc24ca80035c 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/date_range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/date_range.ts @@ -20,21 +20,19 @@ import { get } from 'lodash'; import moment from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; +import { convertDateRangeToString, DateRangeKey } from './lib/date_range'; import { BUCKET_TYPES } from './bucket_agg_types'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; import { KBN_FIELD_TYPES, fieldFormats } from '../../../../../../../plugins/data/public'; +export { convertDateRangeToString, DateRangeKey }; + const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', { defaultMessage: 'Date Range', }); -export interface DateRangeKey { - from: number; - to: number; -} - export const dateRangeBucketAgg = new BucketAggType({ name: BUCKET_TYPES.DATE_RANGE, title: dateRangeTitle, @@ -106,16 +104,3 @@ export const dateRangeBucketAgg = new BucketAggType({ }, ], }); - -export const convertDateRangeToString = ( - { from, to }: DateRangeKey, - format: (val: any) => string -) => { - if (!from) { - return 'Before ' + format(to); - } else if (!to) { - return 'After ' + format(from); - } else { - return format(from) + ' to ' + format(to); - } -}; diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts index e5497bef49165..91bdf53e7f809 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/ip_range.ts @@ -20,21 +20,19 @@ import { noop, map, omit, isNull } from 'lodash'; import { i18n } from '@kbn/i18n'; import { npStart } from 'ui/new_platform'; +import { IpRangeKey, convertIPRangeToString } from './lib/ip_range'; import { BucketAggType } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; // @ts-ignore import { createFilterIpRange } from './create_filter/ip_range'; import { KBN_FIELD_TYPES, fieldFormats } from '../../../../../../../plugins/data/public'; +export { IpRangeKey, convertIPRangeToString }; const ipRangeTitle = i18n.translate('data.search.aggs.buckets.ipRangeTitle', { defaultMessage: 'IPv4 Range', }); -export type IpRangeKey = - | { type: 'mask'; mask: string } - | { type: 'range'; from: string; to: string }; - export const ipRangeBucketAgg = new BucketAggType({ name: BUCKET_TYPES.IP_RANGE, title: ipRangeTitle, @@ -97,13 +95,3 @@ export const ipRangeBucketAgg = new BucketAggType({ }, ], }); - -export const convertIPRangeToString = (range: IpRangeKey, format: (val: any) => string) => { - if (range.type === 'mask') { - return format(range.mask); - } - const from = range.from ? format(range.from) : '-Infinity'; - const to = range.to ? format(range.to) : 'Infinity'; - - return `${from} to ${to}`; -}; diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/date_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/date_range.ts new file mode 100644 index 0000000000000..01b853fc669b5 --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/date_range.ts @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export interface DateRangeKey { + from: number; + to: number; +} + +export const convertDateRangeToString = ( + { from, to }: DateRangeKey, + format: (val: any) => string +) => { + if (!from) { + return 'Before ' + format(to); + } else if (!to) { + return 'After ' + format(from); + } else { + return format(from) + ' to ' + format(to); + } +}; diff --git a/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/ip_range.ts b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/ip_range.ts new file mode 100644 index 0000000000000..be1ac28934c7c --- /dev/null +++ b/src/legacy/core_plugins/data/public/search/aggs/buckets/lib/ip_range.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export type IpRangeKey = + | { type: 'mask'; mask: string } + | { type: 'range'; from: string; to: string }; + +export const convertIPRangeToString = (range: IpRangeKey, format: (val: any) => string) => { + if (range.type === 'mask') { + return format(range.mask); + } + const from = range.from ? format(range.from) : '-Infinity'; + const to = range.to ? format(range.to) : 'Infinity'; + + return `${from} to ${to}`; +}; diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx index 32c99f68a066e..6a466c9cd0211 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_component.test.tsx @@ -22,6 +22,10 @@ import { shallow } from 'enzyme'; import { Vis } from 'src/legacy/core_plugins/visualizations/public'; import { MetricVisComponent, MetricVisComponentProps } from './metric_vis_component'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { npStart } from 'ui/new_platform'; +import { fieldFormats } from '../../../../../plugins/data/public'; +import { identity } from 'lodash'; jest.mock('ui/new_platform'); @@ -62,6 +66,12 @@ describe('MetricVisComponent', function() { return shallow(); }; + beforeAll(() => { + (npStart.plugins.data.fieldFormats.deserialize as jest.Mock).mockImplementation(() => { + return new (fieldFormats.FieldFormat.from(identity))(); + }); + }); + it('should render component', () => { expect(getComponent().exists()).toBe(true); }); diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts index 28565e0181b84..15cd920b79e4a 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts @@ -44,6 +44,9 @@ describe('metric_vis - createMetricVisTypeDefinition', () => { (npStart.plugins.data.fieldFormats.getType as jest.Mock).mockImplementation(() => { return fieldFormats.UrlFormat; }); + (npStart.plugins.data.fieldFormats.deserialize as jest.Mock).mockImplementation(mapping => { + return new fieldFormats.UrlFormat(mapping ? mapping.params : {}); + }); }); const setup = () => { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts index 3d04c04f9b1a6..9c79be98a320c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/legacy_imports.ts @@ -18,10 +18,11 @@ */ export { AggType, AggGroupNames, IAggConfig, IAggType, Schemas } from 'ui/agg_types'; -export { getFormat, getTableAggs } from 'ui/visualize/loader/pipeline_helpers/utilities'; +export { getFormat } from 'ui/visualize/loader/pipeline_helpers/utilities'; // @ts-ignore export { tabifyAggResponse } from 'ui/agg_response/tabify'; // @ts-ignore export { buildHierarchicalData } from 'ui/agg_response/hierarchical/build_hierarchical_data'; // @ts-ignore export { buildPointSeriesData } from 'ui/agg_response/point_series/point_series'; +export { tabifyGetColumns } from '../../../ui/public/agg_response/tabify/_get_columns'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx index a170af33583df..c1563625c3b8c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/components/legend/legend.tsx @@ -22,14 +22,24 @@ import { compact, uniq, map } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiPopoverProps, EuiIcon, keyCodes, htmlIdGenerator } from '@elastic/eui'; +import { IAggConfig } from '../../../../../data/public'; // @ts-ignore -// eslint-disable-next-line @kbn/eslint/no-restricted-paths import { createFiltersFromEvent } from '../../../../../data/public/actions/filters/create_filters_from_event'; import { CUSTOM_LEGEND_VIS_TYPES, LegendItem } from './models'; import { VisLegendItem } from './legend_item'; import { getPieNames } from './pie_utils'; -import { getTableAggs } from '../../../legacy_imports'; + +import { Vis } from '../../../../../visualizations/public'; +import { tabifyGetColumns } from '../../../legacy_imports'; + +const getTableAggs = (vis: Vis): IAggConfig[] => { + if (!vis.aggs || !vis.aggs.getResponseAggs) { + return []; + } + const columns = tabifyGetColumns(vis.aggs.getResponseAggs(), !vis.isHierarchical()); + return columns.map(c => c.aggConfig); +}; export interface VisLegendProps { vis: any; diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts index 7228e506accb9..cb25c66dfd2fe 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts @@ -17,150 +17,15 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; -import { identity } from 'lodash'; -import { IAggConfig } from 'ui/agg_types'; import { npStart } from 'ui/new_platform'; -import { SerializedFieldFormat } from 'src/plugins/expressions/public'; -import { - fieldFormats, - IFieldFormat, - FieldFormatId, - FieldFormatsContentType, -} from '../../../../../../plugins/data/public'; -import { Vis } from '../../../../../core_plugins/visualizations/public'; +import { fieldFormats, IFieldFormat } from '../../../../../../plugins/data/public'; +import { SerializedFieldFormat } from '../../../../../../plugins/expressions/common/types'; -import { tabifyGetColumns } from '../../../agg_response/tabify/_get_columns'; -import { DateRangeKey, convertDateRangeToString } from '../../../agg_types'; -import { IpRangeKey, convertIPRangeToString } from '../../../agg_types'; +type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; -interface TermsFieldFormatParams { - otherBucketLabel: string; - missingBucketLabel: string; - id: string; -} - -function isTermsFieldFormat( - serializedFieldFormat: SerializedFieldFormat -): serializedFieldFormat is SerializedFieldFormat { - return serializedFieldFormat.id === 'terms'; -} - -const getConfig = (key: string, defaultOverride?: any): any => - npStart.core.uiSettings.get(key, defaultOverride); -const DefaultFieldFormat = fieldFormats.FieldFormat.from(identity); - -const getFieldFormat = (id?: FieldFormatId, params: object = {}): IFieldFormat => { - const fieldFormatsService = npStart.plugins.data.fieldFormats; - - if (id) { - const Format = fieldFormatsService.getType(id); - - if (Format) { - return new Format(params, getConfig); - } - } - - return new DefaultFieldFormat(); -}; - -export const createFormat = (agg: IAggConfig): SerializedFieldFormat => { - const format: SerializedFieldFormat = agg.params.field ? agg.params.field.format.toJSON() : {}; - const formats: Record SerializedFieldFormat> = { - date_range: () => ({ id: 'date_range', params: format }), - ip_range: () => ({ id: 'ip_range', params: format }), - percentile_ranks: () => ({ id: 'percent' }), - count: () => ({ id: 'number' }), - cardinality: () => ({ id: 'number' }), - date_histogram: () => ({ - id: 'date', - params: { - pattern: (agg as any).buckets.getScaledDateFormat(), - }, - }), - terms: () => ({ - id: 'terms', - params: { - id: format.id, - otherBucketLabel: agg.params.otherBucketLabel, - missingBucketLabel: agg.params.missingBucketLabel, - ...format.params, - }, - }), - range: () => ({ - id: 'range', - params: { id: format.id, ...format.params }, - }), - }; - - return formats[agg.type.name] ? formats[agg.type.name]() : format; -}; - -export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; - -export const getFormat: FormatFactory = mapping => { - if (!mapping) { - return new DefaultFieldFormat(); - } - const { id } = mapping; - if (id === 'range') { - const RangeFormat = fieldFormats.FieldFormat.from((range: any) => { - const format = getFieldFormat(id, mapping.params); - const gte = '\u2265'; - const lt = '\u003c'; - return i18n.translate('common.ui.aggTypes.rangesFormatMessage', { - defaultMessage: '{gte} {from} and {lt} {to}', - values: { - gte, - from: format.convert(range.gte), - lt, - to: format.convert(range.lt), - }, - }); - }); - return new RangeFormat(); - } else if (id === 'date_range') { - const nestedFormatter = mapping.params as SerializedFieldFormat; - const DateRangeFormat = fieldFormats.FieldFormat.from((range: DateRangeKey) => { - const format = getFieldFormat(nestedFormatter.id, nestedFormatter.params); - return convertDateRangeToString(range, format.convert.bind(format)); - }); - return new DateRangeFormat(); - } else if (id === 'ip_range') { - const nestedFormatter = mapping.params as SerializedFieldFormat; - const IpRangeFormat = fieldFormats.FieldFormat.from((range: IpRangeKey) => { - const format = getFieldFormat(nestedFormatter.id, nestedFormatter.params); - return convertIPRangeToString(range, format.convert.bind(format)); - }); - return new IpRangeFormat(); - } else if (isTermsFieldFormat(mapping) && mapping.params) { - const { params } = mapping; - const convert = (val: string, type: FieldFormatsContentType) => { - const format = getFieldFormat(params.id, mapping.params); - - if (val === '__other__') { - return params.otherBucketLabel; - } - if (val === '__missing__') { - return params.missingBucketLabel; - } - - return format.convert(val, type); - }; - - return { - convert, - getConverterFor: (type: FieldFormatsContentType) => (val: string) => convert(val, type), - } as IFieldFormat; - } else { - return getFieldFormat(id, mapping.params); - } +const createFormat = fieldFormats.serialize; +const getFormat: FormatFactory = (mapping?) => { + return npStart.plugins.data.fieldFormats.deserialize(mapping as any); }; -export const getTableAggs = (vis: Vis): IAggConfig[] => { - if (!vis.aggs || !vis.aggs.getResponseAggs) { - return []; - } - const columns = tabifyGetColumns(vis.aggs.getResponseAggs(), !vis.isHierarchical()); - return columns.map(c => c.aggConfig); -}; +export { getFormat, createFormat, FormatFactory }; diff --git a/src/plugins/data/common/field_formats/field_formats_registry.ts b/src/plugins/data/common/field_formats/field_formats_registry.ts index 6e4880a221c46..9fe9a31307b6a 100644 --- a/src/plugins/data/common/field_formats/field_formats_registry.ts +++ b/src/plugins/data/common/field_formats/field_formats_registry.ts @@ -18,9 +18,9 @@ */ // eslint-disable-next-line max-classes-per-file -import { forOwn, isFunction, memoize } from 'lodash'; +import { forOwn, isFunction, memoize, identity } from 'lodash'; -import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../common'; +import { ES_FIELD_TYPES, IFieldFormat, KBN_FIELD_TYPES } from '../../common'; import { FieldFormatsGetConfigFn, @@ -32,12 +32,17 @@ import { } from './types'; import { baseFormatters } from './constants/base_formatters'; import { FieldFormat } from './field_format'; +import { SerializedFieldFormat } from '../../../expressions/common/types'; export class FieldFormatsRegistry { protected fieldFormats: Map = new Map(); protected defaultMap: Record = {}; protected metaParamsOptions: Record = {}; protected getConfig?: FieldFormatsGetConfigFn; + // overriden on the public contract + public deserialize: (mapping: SerializedFieldFormat) => IFieldFormat = () => { + return new (FieldFormat.from(identity))(); + }; init( getConfig: FieldFormatsGetConfigFn, diff --git a/src/plugins/data/common/field_formats/index.ts b/src/plugins/data/common/field_formats/index.ts index 0847ac0db745f..d7858966f2620 100644 --- a/src/plugins/data/common/field_formats/index.ts +++ b/src/plugins/data/common/field_formats/index.ts @@ -41,7 +41,7 @@ export { TruncateFormat, } from './converters'; -export { getHighlightRequest } from './utils'; +export { getHighlightRequest, serializeFieldFormat } from './utils'; export { DEFAULT_CONVERTER_COLOR } from './constants/color_default'; export { FIELD_FORMAT_IDS } from './types'; diff --git a/src/plugins/data/common/field_formats/utils/index.ts b/src/plugins/data/common/field_formats/utils/index.ts index 81b33115a44bb..3832c941ffad7 100644 --- a/src/plugins/data/common/field_formats/utils/index.ts +++ b/src/plugins/data/common/field_formats/utils/index.ts @@ -17,5 +17,11 @@ * under the License. */ +import { SerializedFieldFormat } from '../../../../expressions/common/types'; +import { IFieldFormat } from '../index'; + export { asPrettyString } from './as_pretty_string'; export { getHighlightHtml, getHighlightRequest } from './highlight'; +export { serializeFieldFormat } from './serialize'; + +export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat; diff --git a/src/plugins/data/common/field_formats/utils/serialize.ts b/src/plugins/data/common/field_formats/utils/serialize.ts new file mode 100644 index 0000000000000..9931f55c30a9e --- /dev/null +++ b/src/plugins/data/common/field_formats/utils/serialize.ts @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { IAggConfig } from '../../../../../legacy/ui/public/agg_types'; +import { SerializedFieldFormat } from '../../../../expressions/common/types'; + +export const serializeFieldFormat = (agg: IAggConfig): SerializedFieldFormat => { + const format: SerializedFieldFormat = agg.params.field ? agg.params.field.format.toJSON() : {}; + const formats: Record SerializedFieldFormat> = { + date_range: () => ({ id: 'date_range', params: format }), + ip_range: () => ({ id: 'ip_range', params: format }), + percentile_ranks: () => ({ id: 'percent' }), + count: () => ({ id: 'number' }), + cardinality: () => ({ id: 'number' }), + date_histogram: () => ({ + id: 'date', + params: { + pattern: (agg as any).buckets.getScaledDateFormat(), + }, + }), + terms: () => ({ + id: 'terms', + params: { + id: format.id, + otherBucketLabel: agg.params.otherBucketLabel, + missingBucketLabel: agg.params.missingBucketLabel, + ...format.params, + }, + }), + range: () => ({ + id: 'range', + params: { id: format.id, ...format.params }, + }), + }; + + return formats[agg.type.name] ? formats[agg.type.name]() : format; +}; diff --git a/src/plugins/data/public/field_formats/field_formats_service.ts b/src/plugins/data/public/field_formats/field_formats_service.ts index 1c43ce2198645..785bedf9b35d3 100644 --- a/src/plugins/data/public/field_formats/field_formats_service.ts +++ b/src/plugins/data/public/field_formats/field_formats_service.ts @@ -19,6 +19,8 @@ import { CoreSetup } from 'src/core/public'; import { FieldFormatsRegistry } from '../../common/field_formats'; +import { deserializeFieldFormat } from './utils/deserialize'; +import { FormatFactory } from '../../common/field_formats/utils'; export class FieldFormatsService { private readonly fieldFormatsRegistry: FieldFormatsRegistry = new FieldFormatsRegistry(); @@ -44,6 +46,10 @@ export class FieldFormatsService { } public start() { + this.fieldFormatsRegistry.deserialize = deserializeFieldFormat.bind( + this.fieldFormatsRegistry as FieldFormatsStart + ); + return this.fieldFormatsRegistry as FieldFormatsStart; } } @@ -52,4 +58,6 @@ export class FieldFormatsService { export type FieldFormatsSetup = Pick; /** @public */ -export type FieldFormatsStart = Omit; +export type FieldFormatsStart = Omit & { + deserialize: FormatFactory; +}; diff --git a/src/plugins/data/public/field_formats/utils/deserialize.ts b/src/plugins/data/public/field_formats/utils/deserialize.ts new file mode 100644 index 0000000000000..c10ebfbe3eb1e --- /dev/null +++ b/src/plugins/data/public/field_formats/utils/deserialize.ts @@ -0,0 +1,129 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { identity } from 'lodash'; +import { i18n } from '@kbn/i18n'; +import { + convertDateRangeToString, + DateRangeKey, +} from '../../../../../legacy/core_plugins/data/public/search/aggs/buckets/lib/date_range'; +import { + convertIPRangeToString, + IpRangeKey, +} from '../../../../../legacy/core_plugins/data/public/search/aggs/buckets/lib/ip_range'; +import { SerializedFieldFormat } from '../../../../expressions/common/types'; +import { FieldFormatId, FieldFormatsContentType, IFieldFormat } from '../..'; +import { FieldFormat } from '../../../common'; +import { DataPublicPluginStart } from '../../../public'; +import { getUiSettings } from '../../../public/services'; +import { FormatFactory } from '../../../common/field_formats/utils'; + +interface TermsFieldFormatParams { + otherBucketLabel: string; + missingBucketLabel: string; + id: string; +} + +function isTermsFieldFormat( + serializedFieldFormat: SerializedFieldFormat +): serializedFieldFormat is SerializedFieldFormat { + return serializedFieldFormat.id === 'terms'; +} + +const getConfig = (key: string, defaultOverride?: any): any => + getUiSettings().get(key, defaultOverride); +const DefaultFieldFormat = FieldFormat.from(identity); + +const getFieldFormat = ( + fieldFormatsService: DataPublicPluginStart['fieldFormats'], + id?: FieldFormatId, + params: object = {} +): IFieldFormat => { + if (id) { + const Format = fieldFormatsService.getType(id); + + if (Format) { + return new Format(params, getConfig); + } + } + + return new DefaultFieldFormat(); +}; + +export const deserializeFieldFormat: FormatFactory = function( + this: DataPublicPluginStart['fieldFormats'], + mapping?: SerializedFieldFormat +) { + if (!mapping) { + return new DefaultFieldFormat(); + } + const { id } = mapping; + if (id === 'range') { + const RangeFormat = FieldFormat.from((range: any) => { + const format = getFieldFormat(this, id, mapping.params); + const gte = '\u2265'; + const lt = '\u003c'; + return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessage', { + defaultMessage: '{gte} {from} and {lt} {to}', + values: { + gte, + from: format.convert(range.gte), + lt, + to: format.convert(range.lt), + }, + }); + }); + return new RangeFormat(); + } else if (id === 'date_range') { + const nestedFormatter = mapping.params as SerializedFieldFormat; + const DateRangeFormat = FieldFormat.from((range: DateRangeKey) => { + const format = getFieldFormat(this, nestedFormatter.id, nestedFormatter.params); + return convertDateRangeToString(range, format.convert.bind(format)); + }); + return new DateRangeFormat(); + } else if (id === 'ip_range') { + const nestedFormatter = mapping.params as SerializedFieldFormat; + const IpRangeFormat = FieldFormat.from((range: IpRangeKey) => { + const format = getFieldFormat(this, nestedFormatter.id, nestedFormatter.params); + return convertIPRangeToString(range, format.convert.bind(format)); + }); + return new IpRangeFormat(); + } else if (isTermsFieldFormat(mapping) && mapping.params) { + const { params } = mapping; + const convert = (val: string, type: FieldFormatsContentType) => { + const format = getFieldFormat(this, params.id, mapping.params); + + if (val === '__other__') { + return params.otherBucketLabel; + } + if (val === '__missing__') { + return params.missingBucketLabel; + } + + return format.convert(val, type); + }; + + return { + convert, + getConverterFor: (type: FieldFormatsContentType) => (val: string) => convert(val, type), + } as IFieldFormat; + } else { + return getFieldFormat(this, id, mapping.params); + } +}; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 8704ca08ae905..cdc4167f545af 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -168,6 +168,7 @@ import { UrlFormat, StringFormat, TruncateFormat, + serializeFieldFormat, } from '../common/field_formats'; // Field formats helpers namespace: @@ -175,6 +176,8 @@ export const fieldFormats = { FieldFormat, FieldFormatsRegistry, // exported only for tests. Consider mock. + serialize: serializeFieldFormat, + DEFAULT_CONVERTER_COLOR, HTML_CONTEXT_TYPE, TEXT_CONTEXT_TYPE, @@ -305,7 +308,6 @@ export { TimeHistoryContract, } from './query'; export * from './ui'; - export { // kbn field types castEsToKbnFieldTypeName, diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts index 103f9d385b3f9..b6ca91169a933 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_pattern.test.ts @@ -31,7 +31,7 @@ import { setNotifications, setFieldFormats } from '../../services'; // Temporary disable eslint, will be removed after moving to new platform folder // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { notificationServiceMock } from '../../../../../core/public/notifications/notifications_service.mock'; -import { FieldFormatsRegistry } from '../../../common/field_formats'; +import { FieldFormatsStart } from '../../field_formats'; jest.mock('../../../../kibana_utils/public', () => { const originalModule = jest.requireActual('../../../../kibana_utils/public'); @@ -125,7 +125,8 @@ describe('IndexPattern', () => { setNotifications(notifications); setFieldFormats(({ getDefaultInstance: jest.fn(), - } as unknown) as FieldFormatsRegistry); + deserialize: jest.fn() as any, + } as unknown) as FieldFormatsStart); return create(indexPatternId).then((pattern: IndexPattern) => { indexPattern = pattern; diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index 0a093644c3939..c723c34cd0a22 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -49,6 +49,7 @@ const fieldFormatsMock: IFieldFormatsRegistry = { init: jest.fn(), register: jest.fn(), parseDefaultTypeMap: jest.fn(), + deserialize: jest.fn(), }; const createSetupContract = (): Setup => { diff --git a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap index eebbc63f6f1e4..990386687bade 100644 --- a/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap +++ b/src/plugins/data/public/ui/query_string_input/__snapshots__/query_string_input.test.tsx.snap @@ -173,6 +173,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA "hasQuerySuggestions": [MockFunction], }, "fieldFormats": Object { + "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], "getDefaultInstance": [MockFunction], @@ -825,6 +826,7 @@ exports[`QueryStringInput Should disable autoFocus on EuiFieldText when disableA "hasQuerySuggestions": [MockFunction], }, "fieldFormats": Object { + "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], "getDefaultInstance": [MockFunction], @@ -1459,6 +1461,7 @@ exports[`QueryStringInput Should pass the query language to the language switche "hasQuerySuggestions": [MockFunction], }, "fieldFormats": Object { + "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], "getDefaultInstance": [MockFunction], @@ -2108,6 +2111,7 @@ exports[`QueryStringInput Should pass the query language to the language switche "hasQuerySuggestions": [MockFunction], }, "fieldFormats": Object { + "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], "getDefaultInstance": [MockFunction], @@ -2742,6 +2746,7 @@ exports[`QueryStringInput Should render the given query 1`] = ` "hasQuerySuggestions": [MockFunction], }, "fieldFormats": Object { + "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], "getDefaultInstance": [MockFunction], @@ -3391,6 +3396,7 @@ exports[`QueryStringInput Should render the given query 1`] = ` "hasQuerySuggestions": [MockFunction], }, "fieldFormats": Object { + "deserialize": [MockFunction], "getByFieldType": [MockFunction], "getDefaultConfig": [MockFunction], "getDefaultInstance": [MockFunction], diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index e8f422d94909f..3ee98a318de35 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -95,12 +95,15 @@ import { UrlFormat, StringFormat, TruncateFormat, + serializeFieldFormat, } from '../common/field_formats'; export const fieldFormats = { FieldFormatsRegistry, FieldFormat, + serializeFieldFormat, + BoolFormat, BytesFormat, ColorFormat, diff --git a/src/test_utils/public/stub_field_formats.ts b/src/test_utils/public/stub_field_formats.ts index 32bdca1eea258..5a20823134ebd 100644 --- a/src/test_utils/public/stub_field_formats.ts +++ b/src/test_utils/public/stub_field_formats.ts @@ -17,7 +17,8 @@ * under the License. */ import { CoreSetup } from 'kibana/public'; -import { fieldFormats } from '../../plugins/data/public'; +import { DataPublicPluginStart, fieldFormats } from '../../plugins/data/public'; +import { deserializeFieldFormat } from '../../plugins/data/public/field_formats/utils/deserialize'; export const getFieldFormatsRegistry = (core: CoreSetup) => { const fieldFormatsRegistry = new fieldFormats.FieldFormatsRegistry(); @@ -25,5 +26,9 @@ export const getFieldFormatsRegistry = (core: CoreSetup) => { fieldFormatsRegistry.init(getConfig, {}); + fieldFormatsRegistry.deserialize = deserializeFieldFormat.bind( + fieldFormatsRegistry as DataPublicPluginStart['fieldFormats'] + ); + return fieldFormatsRegistry; }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 79b826bc4524f..ee2abeff74496 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -77,7 +77,6 @@ }, "messages": { "common.ui.aggResponse.allDocsTitle": "すべてのドキュメント", - "common.ui.aggTypes.rangesFormatMessage": "{gte} {from} と {lt} {to}", "common.ui.directives.paginate.size.allDropDownOptionLabel": "すべて", "common.ui.dualRangeControl.mustSetBothErrorMessage": "下と上の値の両方を設定する必要があります", "common.ui.dualRangeControl.outsideOfRangeErrorMessage": "値は {min} と {max} の間でなければなりません", @@ -13176,4 +13175,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "フィールドを選択してください。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ce1c713adc4bc..3210c619d3e52 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -77,7 +77,6 @@ }, "messages": { "common.ui.aggResponse.allDocsTitle": "所有文档", - "common.ui.aggTypes.rangesFormatMessage": "{gte} {from} 且 {lt} {to}", "common.ui.directives.paginate.size.allDropDownOptionLabel": "全部", "common.ui.dualRangeControl.mustSetBothErrorMessage": "下限值和上限值都须设置", "common.ui.dualRangeControl.outsideOfRangeErrorMessage": "值必须是在 {min} 到 {max} 的范围内", @@ -13175,4 +13174,4 @@ "xpack.watcher.watchEdit.thresholdWatchExpression.aggType.fieldIsRequiredValidationMessage": "此字段必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。" } -} \ No newline at end of file +} From 5d99576ab1a75956c3a77363781c61718840d46f Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 14 Feb 2020 16:17:07 -0700 Subject: [PATCH 07/65] [kbn/optimizer] throw errors into stream on invalid completion (#57735) --- .../kbn-optimizer/src/common/rxjs_helpers.ts | 6 +- .../handle_optimizer_completion.test.ts | 104 ++++++++++++++++++ .../optimizer/handle_optimizer_completion.ts | 56 ++++++++++ packages/kbn-optimizer/src/optimizer/index.ts | 1 + packages/kbn-optimizer/src/run_optimizer.ts | 4 +- 5 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts create mode 100644 packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts diff --git a/packages/kbn-optimizer/src/common/rxjs_helpers.ts b/packages/kbn-optimizer/src/common/rxjs_helpers.ts index 1114f65bacb19..f37bebb49efe9 100644 --- a/packages/kbn-optimizer/src/common/rxjs_helpers.ts +++ b/packages/kbn-optimizer/src/common/rxjs_helpers.ts @@ -24,9 +24,9 @@ type Operator = (source: Rx.Observable) => Rx.Observable; type MapFn = (item: T1, index: number) => T2; /** - * Wrap an operator chain in a closure so that is can have some local - * state. The `fn` is called each time the final observable is - * subscribed so the pipeline/closure is setup for each subscription. + * Wrap an operator chain in a closure so that it can have some local + * state. The `fn` is called each time the returned observable is + * subscribed; the closure is recreated for each subscription. */ export const pipeClosure = (fn: Operator): Operator => { return (source: Rx.Observable) => { diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts new file mode 100644 index 0000000000000..7a8575a6c91fe --- /dev/null +++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.test.ts @@ -0,0 +1,104 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as Rx from 'rxjs'; +import { REPO_ROOT } from '@kbn/dev-utils'; + +import { Update } from '../common'; + +import { OptimizerState } from './optimizer_reducer'; +import { OptimizerConfig } from './optimizer_config'; +import { handleOptimizerCompletion } from './handle_optimizer_completion'; +import { toArray } from 'rxjs/operators'; + +const createUpdate$ = (phase: OptimizerState['phase']) => + Rx.of>({ + state: { + phase, + compilerStates: [], + durSec: 0, + offlineBundles: [], + onlineBundles: [], + startTime: Date.now(), + }, + }); + +const config = (watch?: boolean) => + OptimizerConfig.create({ + repoRoot: REPO_ROOT, + watch, + }); +const collect = (stream: Rx.Observable): Promise => stream.pipe(toArray()).toPromise(); + +it('errors if the optimizer completes when in watch mode', async () => { + const update$ = createUpdate$('success'); + + await expect( + collect(update$.pipe(handleOptimizerCompletion(config(true)))) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"optimizer unexpectedly completed when in watch mode"` + ); +}); + +it('errors if the optimizer completes in phase "issue"', async () => { + const update$ = createUpdate$('issue'); + + await expect( + collect(update$.pipe(handleOptimizerCompletion(config()))) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"webpack issue"`); +}); + +it('errors if the optimizer completes in phase "initializing"', async () => { + const update$ = createUpdate$('initializing'); + + await expect( + collect(update$.pipe(handleOptimizerCompletion(config()))) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"optimizer unexpectedly exit in phase \\"initializing\\""` + ); +}); + +it('errors if the optimizer completes in phase "reallocating"', async () => { + const update$ = createUpdate$('reallocating'); + + await expect( + collect(update$.pipe(handleOptimizerCompletion(config()))) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"optimizer unexpectedly exit in phase \\"reallocating\\""` + ); +}); + +it('errors if the optimizer completes in phase "running"', async () => { + const update$ = createUpdate$('running'); + + await expect( + collect(update$.pipe(handleOptimizerCompletion(config()))) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"optimizer unexpectedly exit in phase \\"running\\""` + ); +}); + +it('passes through errors on the source stream', async () => { + const error = new Error('foo'); + const update$ = Rx.throwError(error); + + await expect(collect(update$.pipe(handleOptimizerCompletion(config())))).rejects.toThrowError( + error + ); +}); diff --git a/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts new file mode 100644 index 0000000000000..fe2fa320818a2 --- /dev/null +++ b/packages/kbn-optimizer/src/optimizer/handle_optimizer_completion.ts @@ -0,0 +1,56 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as Rx from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { createFailError } from '@kbn/dev-utils'; + +import { pipeClosure, Update } from '../common'; + +import { OptimizerState } from './optimizer_reducer'; +import { OptimizerConfig } from './optimizer_config'; + +export function handleOptimizerCompletion(config: OptimizerConfig) { + return pipeClosure((source$: Rx.Observable>) => { + let prevState: OptimizerState | undefined; + + return source$.pipe( + tap({ + next: update => { + prevState = update.state; + }, + complete: () => { + if (config.watch) { + throw new Error('optimizer unexpectedly completed when in watch mode'); + } + + if (prevState?.phase === 'success') { + return; + } + + if (prevState?.phase === 'issue') { + throw createFailError('webpack issue'); + } + + throw new Error(`optimizer unexpectedly exit in phase "${prevState?.phase}"`); + }, + }) + ); + }); +} diff --git a/packages/kbn-optimizer/src/optimizer/index.ts b/packages/kbn-optimizer/src/optimizer/index.ts index b7f14cf3c517f..3df8ed9302668 100644 --- a/packages/kbn-optimizer/src/optimizer/index.ts +++ b/packages/kbn-optimizer/src/optimizer/index.ts @@ -24,3 +24,4 @@ export * from './cache_keys'; export * from './watch_bundles_for_changes'; export * from './run_workers'; export * from './bundle_cache'; +export * from './handle_optimizer_completion'; diff --git a/packages/kbn-optimizer/src/run_optimizer.ts b/packages/kbn-optimizer/src/run_optimizer.ts index e6cce8d306e35..d2daa79feab7e 100644 --- a/packages/kbn-optimizer/src/run_optimizer.ts +++ b/packages/kbn-optimizer/src/run_optimizer.ts @@ -32,6 +32,7 @@ import { runWorkers, OptimizerInitializedEvent, createOptimizerReducer, + handleOptimizerCompletion, } from './optimizer'; export type OptimizerUpdate = Update; @@ -77,6 +78,7 @@ export function runOptimizer(config: OptimizerConfig) { }, createOptimizerReducer(config) ); - }) + }), + handleOptimizerCompletion(config) ); } From 96c39a5a14fbc867f43f46fe3d7d9acb37a0ded6 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Fri, 14 Feb 2020 18:29:50 -0500 Subject: [PATCH 08/65] change slack action to only report on whitelisted host name (#57582) Previously, when using the slack action with a url which was not whitelisted, the entire URL was reported in the error. With this change, only the hostname is reported in the error. --- .../server/builtin_action_types/slack.test.ts | 12 ++++++--- .../server/builtin_action_types/slack.ts | 12 ++++++++- .../actions/builtin_action_types/slack.ts | 26 +++++++++++++++++-- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts b/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts index d33574666cebd..919f0800c291c 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/slack.test.ts @@ -74,6 +74,12 @@ describe('validateActionTypeSecrets()', () => { }).toThrowErrorMatchingInlineSnapshot( `"error validating action type secrets: [webhookUrl]: expected value of type [string] but got [number]"` ); + + expect(() => { + validateSecrets(actionType, { webhookUrl: 'fee-fi-fo-fum' }); + }).toThrowErrorMatchingInlineSnapshot( + `"error validating action type secrets: error configuring slack action: unable to parse host name from webhookUrl"` + ); }); test('should validate and pass when the slack webhookUrl is whitelisted', () => { @@ -95,8 +101,8 @@ describe('validateActionTypeSecrets()', () => { actionType = getActionType({ configurationUtilities: { ...configUtilsMock, - ensureWhitelistedUri: url => { - throw new Error(`target url is not whitelisted`); + ensureWhitelistedHostname: url => { + throw new Error(`target hostname is not whitelisted`); }, }, }); @@ -104,7 +110,7 @@ describe('validateActionTypeSecrets()', () => { expect(() => { validateSecrets(actionType, { webhookUrl: 'https://api.slack.com/' }); }).toThrowErrorMatchingInlineSnapshot( - `"error validating action type secrets: error configuring slack action: target url is not whitelisted"` + `"error validating action type secrets: error configuring slack action: target hostname is not whitelisted"` ); }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/slack.ts b/x-pack/plugins/actions/server/builtin_action_types/slack.ts index b8989e59a2257..042853796695d 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/slack.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/slack.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { URL } from 'url'; import { curry } from 'lodash'; import { i18n } from '@kbn/i18n'; import { schema, TypeOf } from '@kbn/config-schema'; @@ -66,8 +67,17 @@ function valdiateActionTypeConfig( configurationUtilities: ActionsConfigurationUtilities, secretsObject: ActionTypeSecretsType ) { + let url: URL; try { - configurationUtilities.ensureWhitelistedUri(secretsObject.webhookUrl); + url = new URL(secretsObject.webhookUrl); + } catch (err) { + return i18n.translate('xpack.actions.builtin.slack.slackConfigurationErrorNoHostname', { + defaultMessage: 'error configuring slack action: unable to parse host name from webhookUrl', + }); + } + + try { + configurationUtilities.ensureWhitelistedHostname(url.hostname); } catch (whitelistError) { return i18n.translate('xpack.actions.builtin.slack.slackConfigurationError', { defaultMessage: 'error configuring slack action: {message}', diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts index 87280169c0960..5dcff8712a28d 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts @@ -94,7 +94,7 @@ export default function slackTest({ getService }: FtrProviderContext) { name: 'A slack action', actionTypeId: '.slack', secrets: { - webhookUrl: 'http://slack.mynonexistent.com', + webhookUrl: 'http://slack.mynonexistent.com/other/stuff/in/the/path', }, }) .expect(400) @@ -103,7 +103,29 @@ export default function slackTest({ getService }: FtrProviderContext) { statusCode: 400, error: 'Bad Request', message: - 'error validating action type secrets: error configuring slack action: target url "http://slack.mynonexistent.com" is not whitelisted in the Kibana config xpack.actions.whitelistedHosts', + 'error validating action type secrets: error configuring slack action: target hostname "slack.mynonexistent.com" is not whitelisted in the Kibana config xpack.actions.whitelistedHosts', + }); + }); + }); + + it('should respond with a 400 Bad Request when creating a slack action with a webhookUrl with no hostname', async () => { + await supertest + .post('/api/action') + .set('kbn-xsrf', 'foo') + .send({ + name: 'A slack action', + actionTypeId: '.slack', + secrets: { + webhookUrl: 'fee-fi-fo-fum', + }, + }) + .expect(400) + .then((resp: any) => { + expect(resp.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: + 'error validating action type secrets: error configuring slack action: unable to parse host name from webhookUrl', }); }); }); From 95b713e69d37f70755353be15abf8ca07999ee0c Mon Sep 17 00:00:00 2001 From: Dmitry Lemeshko Date: Sat, 15 Feb 2020 00:43:32 +0100 Subject: [PATCH 09/65] update chromedriver to 80.0.1 (#57602) Co-authored-by: Elastic Machine --- package.json | 4 ++-- yarn.lock | 32 +++++++++++++++++++++++++------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 3156e87e763b2..e801f75b976f8 100644 --- a/package.json +++ b/package.json @@ -294,8 +294,8 @@ "@kbn/eslint-import-resolver-kibana": "2.0.0", "@kbn/eslint-plugin-eslint": "1.0.0", "@kbn/expect": "1.0.0", - "@kbn/plugin-generator": "1.0.0", "@kbn/optimizer": "1.0.0", + "@kbn/plugin-generator": "1.0.0", "@kbn/test": "1.0.0", "@kbn/utility-types": "1.0.0", "@microsoft/api-documenter": "7.7.2", @@ -394,7 +394,7 @@ "chai": "3.5.0", "chance": "1.0.18", "cheerio": "0.22.0", - "chromedriver": "79.0.0", + "chromedriver": "^80.0.1", "classnames": "2.2.6", "dedent": "^0.7.0", "delete-empty": "^2.0.0", diff --git a/yarn.lock b/yarn.lock index cac39dfd4c352..33cb366e72f00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3996,6 +3996,11 @@ "@svgr/plugin-svgo" "^4.2.0" loader-utils "^1.2.3" +"@testim/chrome-version@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.0.7.tgz#0cd915785ec4190f08a3a6acc9b61fc38fb5f1a9" + integrity sha512-8UT/J+xqCYfn3fKtOznAibsHpiuDshCb0fwgWxRazTT19Igp9ovoXMPhXyLD6m3CKQGTMHgqoxaFfMWaL40Rnw== + "@testing-library/dom@^6.3.0": version "6.10.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-6.10.1.tgz#da5bf5065d3f9e484aef4cc495f4e1a5bea6df2e" @@ -7114,6 +7119,13 @@ axios@^0.19.0: follow-redirects "1.5.10" is-buffer "^2.0.2" +axios@^0.19.2: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + axobject-query@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" @@ -9049,15 +9061,16 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@79.0.0: - version "79.0.0" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-79.0.0.tgz#1660ac29924dfcd847911025593d6b6746aeea35" - integrity sha512-DO29C7ntJfzu6q1vuoWwCON8E9x5xzopt7Q41A7Dr7hBKcdNpGw1l9DTt9b+l1qviOWiJLGsD+jHw21ptEHubA== +chromedriver@^80.0.1: + version "80.0.1" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-80.0.1.tgz#35c1642e2d864b9e8262f291003e455b0e422917" + integrity sha512-VfRtZUpBUIjeypS+xM40+VD9g4Drv7L2VibG/4+0zX3mMx4KayN6gfKETycPfO6JwQXTLSxEr58fRcrsa8r5xQ== dependencies: - del "^4.1.1" + "@testim/chrome-version" "^1.0.7" + axios "^0.19.2" + del "^5.1.0" extract-zip "^1.6.7" - mkdirp "^0.5.1" - request "^2.88.0" + mkdirp "^1.0.3" tcp-port-used "^1.0.1" ci-info@^1.0.0: @@ -20795,6 +20808,11 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi dependencies: minimist "0.0.8" +mkdirp@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.3.tgz#4cf2e30ad45959dddea53ad97d518b6c8205e1ea" + integrity sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g== + mocha-junit-reporter@^1.23.1: version "1.23.1" resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.1.tgz#ba11519c0b967f404e4123dd69bc4ba022ab0f12" From 0cee1a4adb0940ed911daf2ddf50ff15f40fa539 Mon Sep 17 00:00:00 2001 From: spalger Date: Fri, 14 Feb 2020 18:46:01 -0700 Subject: [PATCH 10/65] skip flaky suite (#45244) --- .../apps/visualize/feature_controls/visualize_spaces.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts index 066042896c122..b24c537d25085 100644 --- a/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts +++ b/x-pack/test/functional/apps/visualize/feature_controls/visualize_spaces.ts @@ -20,7 +20,8 @@ export default function({ getPageObjects, getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); const appsMenu = getService('appsMenu'); - describe('visualize', () => { + // FLAKY: https://github.com/elastic/kibana/issues/45244 + describe.skip('visualize', () => { before(async () => { await esArchiver.loadIfNeeded('logstash_functional'); }); From 15e4e6c6327a63e19692f372791ae6dff6d102d8 Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 14 Feb 2020 18:47:32 -0700 Subject: [PATCH 11/65] Fix failed test reporter for SIEM Cypress use (#57240) * [@kbn/test] build with source-maps * [@kbn/test] the jUnit Reporter being used for SIEM Cypress tests sometimes doesn't include testcases in XML Co-authored-by: Elastic Machine --- packages/kbn-test/package.json | 2 +- packages/kbn-test/src/failed_tests_reporter/test_report.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index 0cc54fa2a64c4..a01c4ebab8ad0 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -5,7 +5,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "build": "babel src --out-dir target --delete-dir-on-start --extensions .ts,.js,.tsx --ignore *.test.js,**/__tests__/**", + "build": "babel src --out-dir target --delete-dir-on-start --extensions .ts,.js,.tsx --ignore *.test.js,**/__tests__/** --source-maps=inline", "kbn:bootstrap": "yarn build", "kbn:watch": "yarn build --watch" }, diff --git a/packages/kbn-test/src/failed_tests_reporter/test_report.ts b/packages/kbn-test/src/failed_tests_reporter/test_report.ts index 6b759ef1d4c62..43d84163462d3 100644 --- a/packages/kbn-test/src/failed_tests_reporter/test_report.ts +++ b/packages/kbn-test/src/failed_tests_reporter/test_report.ts @@ -47,7 +47,7 @@ export interface TestSuite { /* number of skipped tests as a string */ skipped: string; }; - testcase: TestCase[]; + testcase?: TestCase[]; } export interface TestCase { @@ -89,7 +89,7 @@ export function* makeTestCaseIter(report: TestReport) { const testSuites = 'testsuites' in report ? report.testsuites.testsuite : [report.testsuite]; for (const testSuite of testSuites) { - for (const testCase of testSuite.testcase) { + for (const testCase of testSuite.testcase || []) { yield testCase; } } From 6e3790537d4ad3456f053afe76f4eea8fd7ea7c3 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Fri, 14 Feb 2020 21:10:33 -0500 Subject: [PATCH 12/65] [Remote clusters] Migrate client-side code out of legacy (#57365) --- .github/CODEOWNERS | 2 +- x-pack/.i18nrc.json | 2 +- .../helpers/setup_environment.js | 35 ----- .../common/cluster_serialization.test.ts | 137 ------------------ .../common/cluster_serialization.ts | 71 --------- .../plugins/remote_clusters/common/index.ts | 14 -- .../legacy/plugins/remote_clusters/index.ts | 14 +- .../components/connection_status/_index.scss | 6 - .../public/app/services/http.ts | 39 ----- .../plugins/remote_clusters/public/index.html | 3 - .../plugins/remote_clusters/public/index.js | 12 -- .../plugins/remote_clusters/public/index.scss | 11 +- .../plugins/remote_clusters/public/plugin.js | 123 ---------------- .../plugins/remote_clusters/public/shim.ts | 42 ------ .../client_integration/helpers/constants.js | 0 .../helpers/http_requests.js | 0 .../client_integration/helpers/index.js | 2 +- .../helpers/remote_clusters_add.helpers.js | 10 +- .../helpers/remote_clusters_edit.helpers.js | 10 +- .../helpers/remote_clusters_list.helpers.js | 10 +- .../helpers/setup_environment.js | 48 ++++++ .../remote_clusters_add.test.js | 0 .../remote_clusters_edit.test.js | 4 +- .../remote_clusters_list.test.js | 12 +- .../fixtures/remote_cluster.js | 2 +- x-pack/plugins/remote_clusters/kibana.json | 12 +- .../public/application}/app.js | 0 .../public/application}/constants/index.ts | 0 .../public/application}/constants/paths.ts | 0 .../application}/constants/ui_metric.ts | 0 .../public/application/index.d.ts} | 15 +- .../public/application}/index.js | 5 +- .../configured_by_node_warning.js | 0 .../configured_by_node_warning/index.js | 0 .../application}/sections/components/index.js | 0 .../remote_cluster_form.test.js.snap | 0 .../components/remote_cluster_form/index.js | 0 .../remote_cluster_form.js | 2 +- .../remote_cluster_form.test.js | 0 .../remote_cluster_form/request_flyout.js | 2 +- .../__snapshots__/validate_name.test.js.snap | 0 .../__snapshots__/validate_seeds.test.js.snap | 0 .../remote_cluster_form/validators/index.js | 0 .../validators/validate_name.js | 0 .../validators/validate_name.test.js | 0 .../validators/validate_seed.js | 0 .../validators/validate_seed.test.js | 0 .../validators/validate_seeds.js | 0 .../validators/validate_seeds.test.js | 0 .../remote_cluster_page_title/index.js | 0 .../remote_cluster_page_title.js | 0 .../public/application}/sections/index.js | 0 .../sections/remote_cluster_add/index.js | 0 .../remote_cluster_add.container.js | 0 .../remote_cluster_add/remote_cluster_add.js | 0 .../sections/remote_cluster_edit/index.js | 0 .../remote_cluster_edit.container.js | 0 .../remote_cluster_edit.js | 0 .../connection_status/connection_status.js | 0 .../components/connection_status/index.js | 0 .../remote_cluster_list/components/index.js | 0 .../remove_cluster_button_provider/index.js | 0 ...emove_cluster_button_provider.container.js | 0 .../remove_cluster_button_provider.js | 0 .../detail_panel/detail_panel.container.js | 0 .../detail_panel/detail_panel.js | 0 .../remote_cluster_list/detail_panel/index.js | 0 .../sections/remote_cluster_list/index.js | 0 .../remote_cluster_list.container.js | 0 .../remote_cluster_list.js | 4 +- .../remote_cluster_table/index.js | 0 .../remote_cluster_table.container.js | 0 .../remote_cluster_table.js | 0 .../public/application}/services/api.js | 6 +- .../application}/services/api_errors.js | 12 +- .../application}/services/breadcrumb.ts | 23 ++- .../application}/services/documentation.ts | 7 +- .../public/application/services/http.ts | 55 +++++++ .../public/application}/services/index.js | 2 +- .../application/services/notification.ts | 15 ++ .../application}/services/query_params.js | 0 .../public/application/services/redirect.ts | 17 +++ .../public/application}/services/routing.js | 0 .../public/application}/services/ui_metric.ts | 15 +- .../services/validate_seed_node.js | 0 .../services/validate_seed_node.test.js | 0 .../public/application}/store/action_types.js | 0 .../application}/store/actions/add_cluster.js | 15 +- .../store/actions/detail_panel.js | 0 .../store/actions/edit_cluster.js | 14 +- .../application}/store/actions/index.js | 0 .../store/actions/load_clusters.js | 0 .../store/actions/refresh_clusters.js | 0 .../store/actions/remove_clusters.js | 6 +- .../public/application}/store/index.js | 0 .../store/middleware/detail_panel.js | 0 .../application}/store/middleware/index.js | 0 .../store/reducers/add_cluster.js | 0 .../application}/store/reducers/clusters.js | 0 .../store/reducers/detail_panel.js | 0 .../store/reducers/edit_cluster.js | 0 .../application}/store/reducers/index.js | 0 .../store/reducers/remove_cluster.js | 0 .../application}/store/selectors/index.js | 0 .../public/application}/store/store.js | 0 .../remote_clusters/public/index.ts} | 9 +- .../plugins/remote_clusters/public/plugin.ts | 55 +++++++ .../plugins/remote_clusters/public/types.ts | 19 +++ .../plugins/remote_clusters/server/config.ts | 21 +++ .../plugins/remote_clusters/server/index.ts | 2 + .../plugins/remote_clusters/server/plugin.ts | 10 +- .../server/routes/api/add_route.ts | 3 +- .../server/routes/api/update_route.ts | 3 +- 113 files changed, 362 insertions(+), 596 deletions(-) delete mode 100644 x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js delete mode 100644 x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.test.ts delete mode 100644 x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.ts delete mode 100644 x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/_index.scss delete mode 100644 x-pack/legacy/plugins/remote_clusters/public/app/services/http.ts delete mode 100644 x-pack/legacy/plugins/remote_clusters/public/index.html delete mode 100644 x-pack/legacy/plugins/remote_clusters/public/index.js delete mode 100644 x-pack/legacy/plugins/remote_clusters/public/plugin.js delete mode 100644 x-pack/legacy/plugins/remote_clusters/public/shim.ts rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/helpers/constants.js (100%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/helpers/http_requests.js (100%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/helpers/index.js (96%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js (66%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js (68%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js (88%) create mode 100644 x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js (100%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js (93%) rename x-pack/{legacy => }/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js (96%) rename x-pack/{legacy => }/plugins/remote_clusters/fixtures/remote_cluster.js (91%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/app.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/constants/index.ts (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/constants/paths.ts (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/constants/ui_metric.ts (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app/services/redirect.js => plugins/remote_clusters/public/application/index.d.ts} (50%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/index.js (80%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/configured_by_node_warning/configured_by_node_warning.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/configured_by_node_warning/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/remote_cluster_form.js (99%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/remote_cluster_form.test.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/request_flyout.js (97%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/__snapshots__/validate_name.test.js.snap (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/__snapshots__/validate_seeds.test.js.snap (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/validate_name.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/validate_name.test.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/validate_seed.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/validate_seed.test.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/validate_seeds.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_form/validators/validate_seeds.test.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_page_title/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/components/remote_cluster_page_title/remote_cluster_page_title.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_add/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_add/remote_cluster_add.container.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_add/remote_cluster_add.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_edit/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_edit/remote_cluster_edit.container.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_edit/remote_cluster_edit.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/components/connection_status/connection_status.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/components/connection_status/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/components/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/components/remove_cluster_button_provider/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.container.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/detail_panel/detail_panel.container.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/detail_panel/detail_panel.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/detail_panel/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/remote_cluster_list.container.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/remote_cluster_list.js (98%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/remote_cluster_table/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.container.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/api.js (93%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/api_errors.js (77%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/breadcrumb.ts (68%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/documentation.ts (70%) create mode 100644 x-pack/plugins/remote_clusters/public/application/services/http.ts rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/index.js (93%) create mode 100644 x-pack/plugins/remote_clusters/public/application/services/notification.ts rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/query_params.js (100%) create mode 100644 x-pack/plugins/remote_clusters/public/application/services/redirect.ts rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/routing.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/ui_metric.ts (62%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/validate_seed_node.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/services/validate_seed_node.test.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/action_types.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/add_cluster.js (88%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/detail_panel.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/edit_cluster.js (90%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/load_clusters.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/refresh_clusters.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/actions/remove_clusters.js (95%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/middleware/detail_panel.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/middleware/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/reducers/add_cluster.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/reducers/clusters.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/reducers/detail_panel.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/reducers/edit_cluster.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/reducers/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/reducers/remove_cluster.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/selectors/index.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app => plugins/remote_clusters/public/application}/store/store.js (100%) rename x-pack/{legacy/plugins/remote_clusters/public/app/services/notification.ts => plugins/remote_clusters/public/index.ts} (59%) create mode 100644 x-pack/plugins/remote_clusters/public/plugin.ts create mode 100644 x-pack/plugins/remote_clusters/public/types.ts create mode 100644 x-pack/plugins/remote_clusters/server/config.ts diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bf1e341c796fa..e54b7b56707e8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -170,7 +170,7 @@ /x-pack/legacy/plugins/index_lifecycle_management/ @elastic/es-ui /x-pack/legacy/plugins/index_management/ @elastic/es-ui /x-pack/legacy/plugins/license_management/ @elastic/es-ui -/x-pack/legacy/plugins/remote_clusters/ @elastic/es-ui +/x-pack/plugins/remote_clusters/ @elastic/es-ui /x-pack/legacy/plugins/rollup/ @elastic/es-ui /x-pack/plugins/searchprofiler/ @elastic/es-ui /x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json index 27da54042594d..032efcee20afd 100644 --- a/x-pack/.i18nrc.json +++ b/x-pack/.i18nrc.json @@ -27,7 +27,7 @@ "xpack.maps": "legacy/plugins/maps", "xpack.ml": "legacy/plugins/ml", "xpack.monitoring": "legacy/plugins/monitoring", - "xpack.remoteClusters": ["plugins/remote_clusters", "legacy/plugins/remote_clusters"], + "xpack.remoteClusters": "plugins/remote_clusters", "xpack.reporting": ["plugins/reporting", "legacy/plugins/reporting"], "xpack.rollupJobs": "legacy/plugins/rollup", "xpack.searchProfiler": "plugins/searchprofiler", diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js b/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js deleted file mode 100644 index d2385dc900bb2..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js +++ /dev/null @@ -1,35 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios from 'axios'; -import axiosXhrAdapter from 'axios/lib/adapters/xhr'; -import chrome from 'ui/chrome'; // eslint-disable-line import/no-unresolved -import { MANAGEMENT_BREADCRUMB } from 'ui/management'; // eslint-disable-line import/no-unresolved -import { fatalError, toastNotifications } from 'ui/notify'; // eslint-disable-line import/no-unresolved - -import { init as initBreadcrumb } from '../../../public/app/services/breadcrumb'; -import { init as initHttp } from '../../../public/app/services/http'; -import { init as initNotification } from '../../../public/app/services/notification'; -import { init as initUiMetric } from '../../../public/app/services/ui_metric'; -import { init as initHttpRequests } from './http_requests'; - -export const setupEnvironment = () => { - chrome.breadcrumbs = { - set: () => {}, - }; - // axios has a $http like interface so using it to simulate $http - initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path); - initBreadcrumb(() => {}, MANAGEMENT_BREADCRUMB); - initNotification(toastNotifications, fatalError); - initUiMetric(() => () => {}); - - const { server, httpRequestsMockHelpers } = initHttpRequests(); - - return { - server, - httpRequestsMockHelpers, - }; -}; diff --git a/x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.test.ts b/x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.test.ts deleted file mode 100644 index 476fbee7fb6a0..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.test.ts +++ /dev/null @@ -1,137 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { deserializeCluster, serializeCluster } from './cluster_serialization'; - -describe('cluster_serialization', () => { - describe('deserializeCluster()', () => { - it('should throw an error for invalid arguments', () => { - expect(() => deserializeCluster('foo', 'bar')).toThrowError(); - }); - - it('should deserialize a complete cluster object', () => { - expect( - deserializeCluster('test_cluster', { - seeds: ['localhost:9300'], - connected: true, - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, - transport: { - ping_schedule: '-1', - compress: false, - }, - }) - ).toEqual({ - name: 'test_cluster', - seeds: ['localhost:9300'], - isConnected: true, - connectedNodesCount: 1, - maxConnectionsPerCluster: 3, - initialConnectTimeout: '30s', - skipUnavailable: false, - transportPingSchedule: '-1', - transportCompress: false, - }); - }); - - it('should deserialize a cluster object without transport information', () => { - expect( - deserializeCluster('test_cluster', { - seeds: ['localhost:9300'], - connected: true, - num_nodes_connected: 1, - max_connections_per_cluster: 3, - initial_connect_timeout: '30s', - skip_unavailable: false, - }) - ).toEqual({ - name: 'test_cluster', - seeds: ['localhost:9300'], - isConnected: true, - connectedNodesCount: 1, - maxConnectionsPerCluster: 3, - initialConnectTimeout: '30s', - skipUnavailable: false, - }); - }); - - it('should deserialize a cluster object with arbitrary missing properties', () => { - expect( - deserializeCluster('test_cluster', { - seeds: ['localhost:9300'], - connected: true, - num_nodes_connected: 1, - initial_connect_timeout: '30s', - transport: { - compress: false, - }, - }) - ).toEqual({ - name: 'test_cluster', - seeds: ['localhost:9300'], - isConnected: true, - connectedNodesCount: 1, - initialConnectTimeout: '30s', - transportCompress: false, - }); - }); - }); - - describe('serializeCluster()', () => { - it('should throw an error for invalid arguments', () => { - expect(() => serializeCluster('foo')).toThrowError(); - }); - - it('should serialize a complete cluster object to only dynamic properties', () => { - expect( - serializeCluster({ - name: 'test_cluster', - seeds: ['localhost:9300'], - isConnected: true, - connectedNodesCount: 1, - maxConnectionsPerCluster: 3, - initialConnectTimeout: '30s', - skipUnavailable: false, - transportPingSchedule: '-1', - transportCompress: false, - }) - ).toEqual({ - persistent: { - cluster: { - remote: { - test_cluster: { - seeds: ['localhost:9300'], - skip_unavailable: false, - }, - }, - }, - }, - }); - }); - - it('should serialize a cluster object with missing properties', () => { - expect( - serializeCluster({ - name: 'test_cluster', - seeds: ['localhost:9300'], - }) - ).toEqual({ - persistent: { - cluster: { - remote: { - test_cluster: { - seeds: ['localhost:9300'], - skip_unavailable: null, - }, - }, - }, - }, - }); - }); - }); -}); diff --git a/x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.ts b/x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.ts deleted file mode 100644 index 07ea79d42b800..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/common/cluster_serialization.ts +++ /dev/null @@ -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; - * you may not use this file except in compliance with the Elastic License. - */ - -export function deserializeCluster(name: string, esClusterObject: any): any { - if (!name || !esClusterObject || typeof esClusterObject !== 'object') { - throw new Error('Unable to deserialize cluster'); - } - - const { - seeds, - connected: isConnected, - num_nodes_connected: connectedNodesCount, - max_connections_per_cluster: maxConnectionsPerCluster, - initial_connect_timeout: initialConnectTimeout, - skip_unavailable: skipUnavailable, - transport, - } = esClusterObject; - - let deserializedClusterObject: any = { - name, - seeds, - isConnected, - connectedNodesCount, - maxConnectionsPerCluster, - initialConnectTimeout, - skipUnavailable, - }; - - if (transport) { - const { ping_schedule: transportPingSchedule, compress: transportCompress } = transport; - - deserializedClusterObject = { - ...deserializedClusterObject, - transportPingSchedule, - transportCompress, - }; - } - - // It's unnecessary to send undefined values back to the client, so we can remove them. - Object.keys(deserializedClusterObject).forEach(key => { - if (deserializedClusterObject[key] === undefined) { - delete deserializedClusterObject[key]; - } - }); - - return deserializedClusterObject; -} - -export function serializeCluster(deserializedClusterObject: any): any { - if (!deserializedClusterObject || typeof deserializedClusterObject !== 'object') { - throw new Error('Unable to serialize cluster'); - } - - const { name, seeds, skipUnavailable } = deserializedClusterObject; - - return { - persistent: { - cluster: { - remote: { - [name]: { - seeds: seeds ? seeds : null, - skip_unavailable: skipUnavailable !== undefined ? skipUnavailable : null, - }, - }, - }, - }, - }; -} diff --git a/x-pack/legacy/plugins/remote_clusters/common/index.ts b/x-pack/legacy/plugins/remote_clusters/common/index.ts index 8f80b3b7dc6a3..c643f549cbfe1 100644 --- a/x-pack/legacy/plugins/remote_clusters/common/index.ts +++ b/x-pack/legacy/plugins/remote_clusters/common/index.ts @@ -4,20 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { i18n } from '@kbn/i18n'; -import { LICENSE_TYPE_BASIC, LicenseType } from '../../../common/constants'; - export const PLUGIN = { ID: 'remote_clusters', - // Remote Clusters are used in both CCS and CCR, and CCS is available for all licenses. - MINIMUM_LICENSE_REQUIRED: LICENSE_TYPE_BASIC as LicenseType, - getI18nName: (): string => { - return i18n.translate('xpack.remoteClusters.appName', { - defaultMessage: 'Remote Clusters', - }); - }, }; - -export const API_BASE_PATH = '/api/remote_clusters'; - -export { deserializeCluster, serializeCluster } from './cluster_serialization'; diff --git a/x-pack/legacy/plugins/remote_clusters/index.ts b/x-pack/legacy/plugins/remote_clusters/index.ts index 5dd823e09eb8b..37b2224f8d7c2 100644 --- a/x-pack/legacy/plugins/remote_clusters/index.ts +++ b/x-pack/legacy/plugins/remote_clusters/index.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Legacy } from 'kibana'; import { resolve } from 'path'; import { PLUGIN } from './common'; @@ -13,18 +12,11 @@ export function remoteClusters(kibana: any) { id: PLUGIN.ID, configPrefix: 'xpack.remote_clusters', publicDir: resolve(__dirname, 'public'), - // xpack_main is required for license checking. - require: ['kibana', 'elasticsearch', 'xpack_main', 'index_management'], + require: ['kibana'], uiExports: { styleSheetPaths: resolve(__dirname, 'public/index.scss'), - managementSections: ['plugins/remote_clusters'], - injectDefaultVars(server: Legacy.Server) { - const config = server.config(); - return { - remoteClustersUiEnabled: config.get('xpack.remote_clusters.ui.enabled'), - }; - }, }, + // TODO: Remove once CCR has migrated to NP config(Joi: any) { return Joi.object({ // display menu item @@ -41,6 +33,6 @@ export function remoteClusters(kibana: any) { config.get('xpack.remote_clusters.enabled') && config.get('xpack.index_management.enabled') ); }, - init(server: any) {}, + init() {}, }); } diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/_index.scss b/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/_index.scss deleted file mode 100644 index c85cb36c5dc5a..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/_index.scss +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 1. Prevent inherited flexbox layout from compressing this element on IE. - */ - .remoteClustersConnectionStatus__message { - flex-basis: auto !important; /* 1 */ -} diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/http.ts b/x-pack/legacy/plugins/remote_clusters/public/app/services/http.ts deleted file mode 100644 index 54dadf0ae3cb1..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/http.ts +++ /dev/null @@ -1,39 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -let _httpClient: any; -let _prependBasePath: any; - -export function init(httpClient: any, prependBasePath: any): void { - _httpClient = httpClient; - _prependBasePath = prependBasePath; -} - -export function getFullPath(path: string): string { - const apiPrefix = _prependBasePath('/api/remote_clusters'); - - if (path) { - return `${apiPrefix}/${path}`; - } - - return apiPrefix; -} - -export function sendPost(path: string, payload: any): any { - return _httpClient.post(getFullPath(path), payload); -} - -export function sendGet(path: string): any { - return _httpClient.get(getFullPath(path)); -} - -export function sendPut(path: string, payload: any): any { - return _httpClient.put(getFullPath(path), payload); -} - -export function sendDelete(path: string): any { - return _httpClient.delete(getFullPath(path)); -} diff --git a/x-pack/legacy/plugins/remote_clusters/public/index.html b/x-pack/legacy/plugins/remote_clusters/public/index.html deleted file mode 100644 index 4de600769fb40..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/public/index.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/x-pack/legacy/plugins/remote_clusters/public/index.js b/x-pack/legacy/plugins/remote_clusters/public/index.js deleted file mode 100644 index 0a08011dd71a0..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/public/index.js +++ /dev/null @@ -1,12 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Plugin as RemoteClustersPlugin } from './plugin'; -import { createShim } from './shim'; - -const { coreStart, pluginsStart } = createShim(); -const remoteClustersPlugin = new RemoteClustersPlugin(); -remoteClustersPlugin.start(coreStart, pluginsStart); diff --git a/x-pack/legacy/plugins/remote_clusters/public/index.scss b/x-pack/legacy/plugins/remote_clusters/public/index.scss index 4618b005312a5..4ae11323642d8 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/index.scss +++ b/x-pack/legacy/plugins/remote_clusters/public/index.scss @@ -1,8 +1,7 @@ // Import the EUI global scope so we can use EUI constants @import 'src/legacy/ui/public/styles/_styling_constants'; -@import './app/sections/remote_cluster_list/components/connection_status/index'; -// Index management plugin styles +// Remote clusters plugin styles // Prefix all styles with "remoteClusters" to avoid conflicts. // Examples @@ -16,8 +15,14 @@ * as the 'Reset to defaults' link is added to and removed from the DOM. * 2. Fix the positioning. */ - .remoteClusterSkipIfUnavailableSwitch { justify-content: flex-start !important; /* 1 */ padding-top: $euiSizeS !important; } + +/** + * 1. Prevent inherited flexbox layout from compressing this element on IE. + */ + .remoteClustersConnectionStatus__message { + flex-basis: auto !important; /* 1 */ +} diff --git a/x-pack/legacy/plugins/remote_clusters/public/plugin.js b/x-pack/legacy/plugins/remote_clusters/public/plugin.js deleted file mode 100644 index 8dbcfb98859cd..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/public/plugin.js +++ /dev/null @@ -1,123 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { unmountComponentAtNode } from 'react-dom'; -import { i18n } from '@kbn/i18n'; -import routes from 'ui/routes'; - -import template from './index.html'; -import { renderReact } from './app'; -import { CRUD_APP_BASE_PATH } from './app/constants'; -import { setUserHasLeftApp, setRedirect } from './app/services'; -import { init as initBreadcrumbs } from './app/services/breadcrumb'; -import { init as initDocumentation } from './app/services/documentation'; -import { init as initHttp } from './app/services/http'; -import { init as initUiMetric } from './app/services/ui_metric'; -import { init as initNotification } from './app/services/notification'; - -const REACT_ROOT_ID = 'remoteClustersReactRoot'; - -export class Plugin { - start(coreStart, pluginsStart) { - const { - i18n: { Context }, - chrome: { setBreadcrumbs }, - notifications: { toasts }, - fatalError, - http: { - basePath: { prepend: prependBasePath }, - }, - injectedMetadata: { getInjectedVar }, - documentation: { elasticWebsiteUrl, docLinkVersion }, - } = coreStart; - - if (getInjectedVar('remoteClustersUiEnabled')) { - const { - management: { getSection, breadcrumb: managementBreadcrumb }, - uiMetric: { createUiStatsReporter }, - } = pluginsStart; - - const esSection = getSection('elasticsearch'); - esSection.register('remote_clusters', { - visible: true, - display: i18n.translate('xpack.remoteClusters.appTitle', { - defaultMessage: 'Remote Clusters', - }), - order: 5, - url: `#${CRUD_APP_BASE_PATH}/list`, - }); - - // Initialize services - initBreadcrumbs(setBreadcrumbs, managementBreadcrumb); - initDocumentation(`${elasticWebsiteUrl}guide/en/elasticsearch/reference/${docLinkVersion}/`); - initUiMetric(createUiStatsReporter); - initNotification(toasts, fatalError); - - const unmountReactApp = () => { - const appElement = document.getElementById(REACT_ROOT_ID); - if (appElement) { - unmountComponentAtNode(appElement); - } - }; - - // NOTE: The New Platform will implicitly handle much of this logic by mounting the app at - // the base route. - routes.when(`${CRUD_APP_BASE_PATH}/:view?/:id?`, { - template, - controllerAs: 'remoteClusters', - controller: class RemoteClustersController { - constructor($scope, $route, $http, kbnUrl) { - // NOTE: We depend upon Angular's $http service because it's decorated with interceptors, - // e.g. to check license status per request. - initHttp($http, prependBasePath); - - setRedirect(path => { - $scope.$evalAsync(() => { - kbnUrl.redirect(path); - }); - }); - - // If returning to the app, we'll need to reset this state. - setUserHasLeftApp(false); - - // React-router's will cause this controller to re-execute without the $destroy - // handler being called. This means the app will re-mount, so we need to unmount it first - // here. - unmountReactApp(); - - $scope.$$postDigest(() => { - const appElement = document.getElementById(REACT_ROOT_ID); - if (appElement) { - renderReact(appElement, Context); - } - - const appRoute = $route.current; - const stopListeningForLocationChange = $scope.$on('$locationChangeSuccess', () => { - const currentRoute = $route.current; - const isNavigationInApp = - currentRoute.$$route.template === appRoute.$$route.template; - - // When we navigate within the app, prevent Angular from re-matching the route and - // rebuilding the app. - if (isNavigationInApp) { - $route.current = appRoute; - } else { - // Set internal flag so we can prevent reacting to the route change internally. - setUserHasLeftApp(true); - } - }); - - $scope.$on('$destroy', () => { - stopListeningForLocationChange(); - unmountReactApp(); - }); - }); - } - }, - }); - } - } -} diff --git a/x-pack/legacy/plugins/remote_clusters/public/shim.ts b/x-pack/legacy/plugins/remote_clusters/public/shim.ts deleted file mode 100644 index 83975fa4bd0fe..0000000000000 --- a/x-pack/legacy/plugins/remote_clusters/public/shim.ts +++ /dev/null @@ -1,42 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { npStart } from 'ui/new_platform'; -import { management, MANAGEMENT_BREADCRUMB } from 'ui/management'; -import { fatalError } from 'ui/notify'; -import { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } from 'ui/documentation_links'; - -import { createUiStatsReporter } from '../../../../../src/legacy/core_plugins/ui_metric/public'; - -export function createShim() { - const { - core: { chrome, i18n, notifications, http, injectedMetadata }, - } = npStart; - - return { - coreStart: { - chrome, - i18n, - notifications, - fatalError, - injectedMetadata, - http, - documentation: { - elasticWebsiteUrl: ELASTIC_WEBSITE_URL, - docLinkVersion: DOC_LINK_VERSION, - }, - }, - pluginsStart: { - management: { - getSection: management.getSection.bind(management), - breadcrumb: MANAGEMENT_BREADCRUMB, - }, - uiMetric: { - createUiStatsReporter, - }, - }, - }; -} diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/constants.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/constants.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/constants.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/constants.js diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/http_requests.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/http_requests.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/http_requests.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/http_requests.js diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/index.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/index.js similarity index 96% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/index.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/index.js index 084a370666e45..d70ba2a21e176 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/index.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/index.js @@ -8,7 +8,7 @@ import { setup as remoteClustersAddSetup } from './remote_clusters_add.helpers'; import { setup as remoteClustersEditSetup } from './remote_clusters_edit.helpers'; import { setup as remoteClustersListSetup } from './remote_clusters_list.helpers'; -export { nextTick, getRandomString, findTestSubject } from '../../../../../../test_utils'; +export { nextTick, getRandomString, findTestSubject } from '../../../../../test_utils'; export { setupEnvironment } from './setup_environment'; diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js similarity index 66% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js index e573a4ebcef92..dd1d5d2187176 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_add.helpers.js @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed } from '../../../../../../test_utils'; -import { RemoteClusterAdd } from '../../../public/app/sections/remote_cluster_add'; -import { createRemoteClustersStore } from '../../../public/app/store'; -import { registerRouter } from '../../../public/app/services/routing'; +import { registerTestBed } from '../../../../../test_utils'; + +/* eslint-disable @kbn/eslint/no-restricted-paths */ +import { RemoteClusterAdd } from '../../../public/application/sections/remote_cluster_add'; +import { createRemoteClustersStore } from '../../../public/application/store'; +import { registerRouter } from '../../../public/application/services/routing'; const testBedConfig = { store: createRemoteClustersStore, diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js similarity index 68% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js index 6a90ed6657c78..426aea90e5a99 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_edit.helpers.js @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed } from '../../../../../../test_utils'; -import { RemoteClusterEdit } from '../../../public/app/sections/remote_cluster_edit'; -import { createRemoteClustersStore } from '../../../public/app/store'; -import { registerRouter } from '../../../public/app/services/routing'; +import { registerTestBed } from '../../../../../test_utils'; + +/* eslint-disable @kbn/eslint/no-restricted-paths */ +import { RemoteClusterEdit } from '../../../public/application/sections/remote_cluster_edit'; +import { createRemoteClustersStore } from '../../../public/application/store'; +import { registerRouter } from '../../../public/application/services/routing'; import { REMOTE_CLUSTER_EDIT_NAME } from './constants'; diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js similarity index 88% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js index 0009bd84ffa6a..dc9b22b40542a 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/remote_clusters_list.helpers.js @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { registerTestBed, findTestSubject } from '../../../../../../test_utils'; -import { RemoteClusterList } from '../../../public/app/sections/remote_cluster_list'; -import { createRemoteClustersStore } from '../../../public/app/store'; -import { registerRouter } from '../../../public/app/services/routing'; +import { registerTestBed, findTestSubject } from '../../../../../test_utils'; + +/* eslint-disable @kbn/eslint/no-restricted-paths */ +import { RemoteClusterList } from '../../../public/application/sections/remote_cluster_list'; +import { createRemoteClustersStore } from '../../../public/application/store'; +import { registerRouter } from '../../../public/application/services/routing'; const testBedConfig = { store: createRemoteClustersStore, diff --git a/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js new file mode 100644 index 0000000000000..c912a4ddabc9d --- /dev/null +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/helpers/setup_environment.js @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + notificationServiceMock, + fatalErrorsServiceMock, + docLinksServiceMock, + injectedMetadataServiceMock, +} from '../../../../../../src/core/public/mocks'; + +import { usageCollectionPluginMock } from '../../../../../../src/plugins/usage_collection/public/mocks'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { HttpService } from '../../../../../../src/core/public/http'; + +/* eslint-disable @kbn/eslint/no-restricted-paths */ +import { init as initBreadcrumb } from '../../../public/application/services/breadcrumb'; +import { init as initHttp } from '../../../public/application/services/http'; +import { init as initNotification } from '../../../public/application/services/notification'; +import { init as initUiMetric } from '../../../public/application/services/ui_metric'; +import { init as initDocumentation } from '../../../public/application/services/documentation'; +import { init as initHttpRequests } from './http_requests'; + +export const setupEnvironment = () => { + const httpServiceSetupMock = new HttpService().setup({ + injectedMetadata: injectedMetadataServiceMock.createSetupContract(), + fatalErrors: fatalErrorsServiceMock.createSetupContract(), + }); + + initBreadcrumb(() => {}); + initDocumentation(docLinksServiceMock.createStartContract()); + initUiMetric(usageCollectionPluginMock.createSetupContract()); + initNotification( + notificationServiceMock.createSetupContract().toasts, + fatalErrorsServiceMock.createSetupContract() + ); + initHttp(httpServiceSetupMock); + + const { server, httpRequestsMockHelpers } = initHttpRequests(); + + return { + server, + httpRequestsMockHelpers, + }; +}; diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_add.test.js diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js similarity index 93% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js index 95dc65a96e30a..cab91854a5114 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_edit.test.js @@ -5,7 +5,7 @@ */ jest.mock('ui/new_platform'); -import { RemoteClusterForm } from '../../public/app/sections/components/remote_cluster_form'; +import { RemoteClusterForm } from '../../public/application/sections/components/remote_cluster_form'; import { pageHelpers, setupEnvironment, nextTick } from './helpers'; import { REMOTE_CLUSTER_EDIT, REMOTE_CLUSTER_EDIT_NAME } from './helpers/constants'; @@ -31,7 +31,7 @@ describe('Edit Remote cluster', () => { httpRequestsMockHelpers.setLoadRemoteClustersResponse([REMOTE_CLUSTER_EDIT]); ({ component, find, exists } = setup()); - await nextTick(); + await nextTick(100); // We need to wait next tick for the mock server response to kick in component.update(); }); diff --git a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js similarity index 96% rename from x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js rename to x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js index 699c00e450a1f..1b7c600218cee 100644 --- a/x-pack/legacy/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js +++ b/x-pack/plugins/remote_clusters/__jest__/client_integration/remote_clusters_list.test.js @@ -12,7 +12,7 @@ import { findTestSubject, } from './helpers'; -import { getRouter } from '../../public/app/services'; +import { getRouter } from '../../public/application/services'; import { getRemoteClusterMock } from '../../fixtures/remote_cluster'; jest.mock('ui/new_platform'); @@ -39,11 +39,11 @@ describe('', () => { describe('on component mount', () => { let exists; - beforeEach(async () => { + beforeEach(() => { ({ exists } = setup()); }); - test('should show a "loading remote clusters" indicator', async () => { + test('should show a "loading remote clusters" indicator', () => { expect(exists('remoteClustersTableLoading')).toBe(true); }); }); @@ -55,7 +55,7 @@ describe('', () => { beforeEach(async () => { ({ exists, component } = setup()); - await nextTick(); // We need to wait next tick for the mock server response to kick in + await nextTick(100); // We need to wait next tick for the mock server response to kick in component.update(); }); @@ -97,7 +97,7 @@ describe('', () => { // Mount the component ({ component, find, exists, table, actions } = setup()); - await nextTick(); // Make sure that the Http request is fulfilled + await nextTick(100); // Make sure that the Http request is fulfilled component.update(); // Read the remote clusters list table @@ -206,7 +206,7 @@ describe('', () => { actions.clickBulkDeleteButton(); actions.clickConfirmModalDeleteRemoteCluster(); - await nextTick(550); // there is a 500ms timeout in the api action + await nextTick(600); // there is a 500ms timeout in the api action component.update(); ({ rows } = table.getMetaData('remoteClusterListTable')); diff --git a/x-pack/legacy/plugins/remote_clusters/fixtures/remote_cluster.js b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js similarity index 91% rename from x-pack/legacy/plugins/remote_clusters/fixtures/remote_cluster.js rename to x-pack/plugins/remote_clusters/fixtures/remote_cluster.js index 248e2b8232cad..e3e087548cf00 100644 --- a/x-pack/legacy/plugins/remote_clusters/fixtures/remote_cluster.js +++ b/x-pack/plugins/remote_clusters/fixtures/remote_cluster.js @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { getRandomString } from '../../../../test_utils'; +import { getRandomString } from '../../../test_utils'; export const getRemoteClusterMock = ({ name = getRandomString(), diff --git a/x-pack/plugins/remote_clusters/kibana.json b/x-pack/plugins/remote_clusters/kibana.json index de1e3d1e26865..27ae6802966dd 100644 --- a/x-pack/plugins/remote_clusters/kibana.json +++ b/x-pack/plugins/remote_clusters/kibana.json @@ -1,9 +1,17 @@ { "id": "remote_clusters", "version": "kibana", + "configPath": [ + "xpack", + "remote_clusters" + ], "requiredPlugins": [ - "licensing" + "licensing", + "management" + ], + "optionalPlugins": [ + "usageCollection" ], "server": true, - "ui": false + "ui": true } diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/app.js b/x-pack/plugins/remote_clusters/public/application/app.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/app.js rename to x-pack/plugins/remote_clusters/public/application/app.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/constants/index.ts b/x-pack/plugins/remote_clusters/public/application/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/constants/index.ts rename to x-pack/plugins/remote_clusters/public/application/constants/index.ts diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/constants/paths.ts b/x-pack/plugins/remote_clusters/public/application/constants/paths.ts similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/constants/paths.ts rename to x-pack/plugins/remote_clusters/public/application/constants/paths.ts diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/constants/ui_metric.ts b/x-pack/plugins/remote_clusters/public/application/constants/ui_metric.ts similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/constants/ui_metric.ts rename to x-pack/plugins/remote_clusters/public/application/constants/ui_metric.ts diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/redirect.js b/x-pack/plugins/remote_clusters/public/application/index.d.ts similarity index 50% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/redirect.js rename to x-pack/plugins/remote_clusters/public/application/index.d.ts index ec77c2a2bfe99..b5c5ad5522134 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/redirect.js +++ b/x-pack/plugins/remote_clusters/public/application/index.d.ts @@ -4,14 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -// This depends upon Angular, which is why we use this provider pattern to access it within -// our React app. -let _redirect; +import { RegisterManagementAppArgs, I18nStart } from '../types'; -export function setRedirect(redirect) { - _redirect = redirect; -} - -export function redirect(path) { - _redirect(path); -} +export declare const renderApp: ( + elem: HTMLElement | null, + I18nContext: I18nStart['Context'] +) => ReturnType; diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/index.js b/x-pack/plugins/remote_clusters/public/application/index.js similarity index 80% rename from x-pack/legacy/plugins/remote_clusters/public/app/index.js rename to x-pack/plugins/remote_clusters/public/application/index.js index 2de59f4590553..0b8b26ace5daa 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/index.js +++ b/x-pack/plugins/remote_clusters/public/application/index.js @@ -5,14 +5,14 @@ */ import React from 'react'; -import { render } from 'react-dom'; +import { render, unmountComponentAtNode } from 'react-dom'; import { HashRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import { App } from './app'; import { remoteClustersStore } from './store'; -export const renderReact = async (elem, I18nContext) => { +export const renderApp = (elem, I18nContext) => { render( @@ -23,4 +23,5 @@ export const renderReact = async (elem, I18nContext) => { , elem ); + return () => unmountComponentAtNode(elem); }; diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/configured_by_node_warning/configured_by_node_warning.js b/x-pack/plugins/remote_clusters/public/application/sections/components/configured_by_node_warning/configured_by_node_warning.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/configured_by_node_warning/configured_by_node_warning.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/configured_by_node_warning/configured_by_node_warning.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/configured_by_node_warning/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/configured_by_node_warning/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/configured_by_node_warning/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/configured_by_node_warning/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js similarity index 99% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/remote_cluster_form.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index f070bb218be42..08cd01496a8b9 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -491,7 +491,7 @@ export class RemoteClusterForm extends Component { let errorBody; - if (cause) { + if (cause && Array.isArray(cause)) { if (cause.length === 1) { errorBody =

{cause[0]}

; } else { diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/remote_cluster_form.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/remote_cluster_form.test.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.test.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/request_flyout.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/request_flyout.js similarity index 97% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/request_flyout.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/request_flyout.js index deec26129abe5..70e2267001e3c 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/request_flyout.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/request_flyout.js @@ -26,7 +26,7 @@ import { EuiTitle, } from '@elastic/eui'; -import { serializeCluster } from '../../../../../common'; +import { serializeCluster } from '../../../../../common/constants'; export class RequestFlyout extends PureComponent { static propTypes = { diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/__snapshots__/validate_name.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_name.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/__snapshots__/validate_name.test.js.snap rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_name.test.js.snap diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/__snapshots__/validate_seeds.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_seeds.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/__snapshots__/validate_seeds.test.js.snap rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/__snapshots__/validate_seeds.test.js.snap diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_name.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_name.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_name.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_name.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_name.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_name.test.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_name.test.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_name.test.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seed.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seed.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seed.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.test.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seed.test.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seed.test.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seeds.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seeds.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seeds.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seeds.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seeds.test.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seeds.test.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_form/validators/validate_seeds.test.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/validators/validate_seeds.test.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_page_title/index.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_page_title/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_page_title/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_page_title/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_page_title/remote_cluster_page_title.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_page_title/remote_cluster_page_title.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/components/remote_cluster_page_title/remote_cluster_page_title.js rename to x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_page_title/remote_cluster_page_title.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/index.js b/x-pack/plugins/remote_clusters/public/application/sections/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_add/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_add/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_add/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_add/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_add/remote_cluster_add.container.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_add/remote_cluster_add.container.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_add/remote_cluster_add.container.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_add/remote_cluster_add.container.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_add/remote_cluster_add.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_add/remote_cluster_add.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_add/remote_cluster_add.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_add/remote_cluster_add.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_edit/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_edit/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_edit/remote_cluster_edit.container.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.container.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_edit/remote_cluster_edit.container.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.container.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_edit/remote_cluster_edit.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_edit/remote_cluster_edit.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_edit/remote_cluster_edit.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/connection_status.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/connection_status.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/connection_status.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/connection_status/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/connection_status/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/remove_cluster_button_provider/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/remove_cluster_button_provider/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/remove_cluster_button_provider/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/remove_cluster_button_provider/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.container.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.container.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.container.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.container.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/components/remove_cluster_button_provider/remove_cluster_button_provider.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/detail_panel/detail_panel.container.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.container.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/detail_panel/detail_panel.container.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.container.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/detail_panel/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/detail_panel/detail_panel.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/detail_panel.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/detail_panel/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/detail_panel/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/detail_panel/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_list.container.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_list.container.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_list.container.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_list.container.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_list.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_list.js similarity index 98% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_list.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_list.js index dea6bc7627cc1..207aa8045c011 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_list.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_list.js @@ -145,9 +145,9 @@ export class RemoteClusterList extends Component { } renderError(error) { - // We can safely depend upon the shape of this error coming from Angular $http, because we + // We can safely depend upon the shape of this error coming from http service, because we // handle unexpected error shapes in the API action. - const { statusCode, error: errorString } = error.data; + const { statusCode, error: errorString } = error.body; const title = i18n.translate('xpack.remoteClusters.remoteClusterList.loadingErrorTitle', { defaultMessage: 'Error loading remote clusters', diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_table/index.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_table/index.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.container.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.container.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.container.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.container.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js b/x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js rename to x-pack/plugins/remote_clusters/public/application/sections/remote_cluster_list/remote_cluster_table/remote_cluster_table.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/api.js b/x-pack/plugins/remote_clusters/public/application/services/api.js similarity index 93% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/api.js rename to x-pack/plugins/remote_clusters/public/application/services/api.js index 8631bc5af12a8..72d5e3f9c3f9e 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/api.js +++ b/x-pack/plugins/remote_clusters/public/application/services/api.js @@ -9,19 +9,19 @@ import { trackUserRequest } from './ui_metric'; import { sendGet, sendPost, sendPut, sendDelete } from './http'; export async function loadClusters() { - const response = await sendGet(); - return response.data; + return await sendGet(); } export async function addCluster(cluster) { const request = sendPost('', cluster); + return await trackUserRequest(request, UIM_CLUSTER_ADD); } export async function editCluster(cluster) { const { name, ...rest } = cluster; - const request = sendPut(name, rest); + return await trackUserRequest(request, UIM_CLUSTER_UPDATE); } diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/api_errors.js b/x-pack/plugins/remote_clusters/public/application/services/api_errors.js similarity index 77% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/api_errors.js rename to x-pack/plugins/remote_clusters/public/application/services/api_errors.js index c50ad92532071..8376f37f36f49 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/api_errors.js +++ b/x-pack/plugins/remote_clusters/public/application/services/api_errors.js @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fatalError, toasts } from './notification'; +import { toasts, fatalError } from './notification'; function createToastConfig(error, errorTitle) { - // Expect an error in the shape provided by Angular's $http service. - if (error && error.data) { - const { error: errorString, statusCode, message } = error.data; + // Expect an error in the shape provided by http service. + if (error && error.body) { + const { error: errorString, statusCode, message } = error.body; return { title: errorTitle, text: `${statusCode}: ${errorString}. ${message}`, @@ -26,7 +26,7 @@ export function showApiWarning(error, errorTitle) { // This error isn't an HTTP error, so let the fatal error screen tell the user something // unexpected happened. - return fatalError(error, errorTitle); + return fatalError.add(error, errorTitle); } export function showApiError(error, errorTitle) { @@ -38,5 +38,5 @@ export function showApiError(error, errorTitle) { // This error isn't an HTTP error, so let the fatal error screen tell the user something // unexpected happened. - fatalError(error, errorTitle); + fatalError.add(error, errorTitle); } diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/breadcrumb.ts b/x-pack/plugins/remote_clusters/public/application/services/breadcrumb.ts similarity index 68% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/breadcrumb.ts rename to x-pack/plugins/remote_clusters/public/application/services/breadcrumb.ts index 68d34cc2a17d4..f90a0d3456166 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/breadcrumb.ts +++ b/x-pack/plugins/remote_clusters/public/application/services/breadcrumb.ts @@ -8,13 +8,22 @@ import { i18n } from '@kbn/i18n'; import { CRUD_APP_BASE_PATH } from '../constants'; -let _setBreadcrumbs: any; -let _breadcrumbs: any; +interface Breadcrumb { + text: string; + href?: string; +} +interface Breadcrumbs { + home: Breadcrumb; + add: Breadcrumb; + edit: Breadcrumb; +} + +let _setBreadcrumbs: (breadcrumbs: Breadcrumb[]) => void; +let _breadcrumbs: Breadcrumbs; -export function init(setGlobalBreadcrumbs: any, managementBreadcrumb: any): void { +export function init(setGlobalBreadcrumbs: (breadcrumbs: Breadcrumb[]) => void): void { _setBreadcrumbs = setGlobalBreadcrumbs; _breadcrumbs = { - management: managementBreadcrumb, home: { text: i18n.translate('xpack.remoteClusters.listBreadcrumbTitle', { defaultMessage: 'Remote Clusters', @@ -34,13 +43,13 @@ export function init(setGlobalBreadcrumbs: any, managementBreadcrumb: any): void }; } -export function setBreadcrumbs(type: string, queryParams?: string): void { +export function setBreadcrumbs(type: 'home' | 'add' | 'edit', queryParams?: string): void { if (!_breadcrumbs[type]) { return; } if (type === 'home') { - _setBreadcrumbs([_breadcrumbs.management, _breadcrumbs.home]); + _setBreadcrumbs([_breadcrumbs.home]); } else { // Support deep-linking back to a remote cluster in the detail panel. const homeBreadcrumb = { @@ -48,6 +57,6 @@ export function setBreadcrumbs(type: string, queryParams?: string): void { href: `${_breadcrumbs.home.href}${queryParams}`, }; - _setBreadcrumbs([_breadcrumbs.management, homeBreadcrumb, _breadcrumbs[type]]); + _setBreadcrumbs([homeBreadcrumb, _breadcrumbs[type]]); } } diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/documentation.ts b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts similarity index 70% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/documentation.ts rename to x-pack/plugins/remote_clusters/public/application/services/documentation.ts index 4d04aa6fad74d..38cf2223a313b 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/documentation.ts +++ b/x-pack/plugins/remote_clusters/public/application/services/documentation.ts @@ -4,11 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { DocLinksStart } from 'kibana/public'; + export let skippingDisconnectedClustersUrl: string; export let remoteClustersUrl: string; export let transportPortUrl: string; -export function init(esDocBasePath: string): void { +export function init(docLinks: DocLinksStart): void { + const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; + const esDocBasePath = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}`; + skippingDisconnectedClustersUrl = `${esDocBasePath}/modules-cross-cluster-search.html#_skipping_disconnected_clusters`; remoteClustersUrl = `${esDocBasePath}/modules-remote-clusters.html`; transportPortUrl = `${esDocBasePath}/modules-transport.html`; diff --git a/x-pack/plugins/remote_clusters/public/application/services/http.ts b/x-pack/plugins/remote_clusters/public/application/services/http.ts new file mode 100644 index 0000000000000..b49e95f3a5c65 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/services/http.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HttpSetup, HttpResponse } from 'kibana/public'; +import { API_BASE_PATH } from '../../../common/constants'; + +let _httpClient: HttpSetup; + +export function init(httpClient: HttpSetup): void { + _httpClient = httpClient; +} + +export function getFullPath(path: string): string { + if (path) { + return `${API_BASE_PATH}/${path}`; + } + + return API_BASE_PATH; +} + +export function sendPost( + path: string, + payload: { + name: string; + seeds: string[]; + skipUnavailable: boolean; + } +): Promise { + return _httpClient.post(getFullPath(path), { + body: JSON.stringify(payload), + }); +} + +export function sendGet(path: string): Promise { + return _httpClient.get(getFullPath(path)); +} + +export function sendPut( + path: string, + payload: { + seeds: string[]; + skipUnavailable: boolean; + } +): Promise { + return _httpClient.put(getFullPath(path), { + body: JSON.stringify(payload), + }); +} + +export function sendDelete(path: string): Promise { + return _httpClient.delete(getFullPath(path)); +} diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/index.js b/x-pack/plugins/remote_clusters/public/application/services/index.js similarity index 93% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/index.js rename to x-pack/plugins/remote_clusters/public/application/services/index.js index 69679e55dfef5..031770d9500ed 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/index.js +++ b/x-pack/plugins/remote_clusters/public/application/services/index.js @@ -8,7 +8,7 @@ export { loadClusters, addCluster, editCluster, removeClusterRequest } from './a export { showApiError, showApiWarning } from './api_errors'; -export { setRedirect, redirect } from './redirect'; +export { initRedirect, redirect } from './redirect'; export { isSeedNodeValid, isSeedNodePortValid } from './validate_seed_node'; diff --git a/x-pack/plugins/remote_clusters/public/application/services/notification.ts b/x-pack/plugins/remote_clusters/public/application/services/notification.ts new file mode 100644 index 0000000000000..1c9173e519b48 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/services/notification.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { NotificationsSetup, FatalErrorsSetup } from 'src/core/public'; + +export let toasts: NotificationsSetup['toasts']; +export let fatalError: FatalErrorsSetup; + +export function init(_toasts: NotificationsSetup['toasts'], _fatalError: FatalErrorsSetup): void { + toasts = _toasts; + fatalError = _fatalError; +} diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js b/x-pack/plugins/remote_clusters/public/application/services/query_params.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/query_params.js rename to x-pack/plugins/remote_clusters/public/application/services/query_params.js diff --git a/x-pack/plugins/remote_clusters/public/application/services/redirect.ts b/x-pack/plugins/remote_clusters/public/application/services/redirect.ts new file mode 100644 index 0000000000000..00a97fa74c5ce --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/application/services/redirect.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CoreStart } from 'kibana/public'; + +let navigateToApp: CoreStart['application']['navigateToApp']; + +export function init(_navigateToApp: CoreStart['application']['navigateToApp']) { + navigateToApp = _navigateToApp; +} + +export function redirect(path: string) { + navigateToApp('kibana', { path: `#${path}` }); +} diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/routing.js b/x-pack/plugins/remote_clusters/public/application/services/routing.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/routing.js rename to x-pack/plugins/remote_clusters/public/application/services/routing.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/ui_metric.ts b/x-pack/plugins/remote_clusters/public/application/services/ui_metric.ts similarity index 62% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/ui_metric.ts rename to x-pack/plugins/remote_clusters/public/application/services/ui_metric.ts index 36a23476c1873..91354155cacb0 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/ui_metric.ts +++ b/x-pack/plugins/remote_clusters/public/application/services/ui_metric.ts @@ -4,17 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; +import { UiStatsMetricType } from '@kbn/analytics'; import { UIM_APP_NAME } from '../constants'; -import { - createUiStatsReporter, - METRIC_TYPE, -} from '../../../../../../../src/legacy/core_plugins/ui_metric/public'; -export let trackUiMetric: ReturnType; -export { METRIC_TYPE }; +export let trackUiMetric: (metricType: UiStatsMetricType, eventName: string) => void; +export let METRIC_TYPE: UsageCollectionSetup['METRIC_TYPE']; -export function init(getReporter: typeof createUiStatsReporter): void { - trackUiMetric = getReporter(UIM_APP_NAME); +export function init(usageCollection: UsageCollectionSetup): void { + trackUiMetric = usageCollection.reportUiStats.bind(usageCollection, UIM_APP_NAME); + METRIC_TYPE = usageCollection.METRIC_TYPE; } /** diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/validate_seed_node.js b/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/validate_seed_node.js rename to x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/validate_seed_node.test.js b/x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/validate_seed_node.test.js rename to x-pack/plugins/remote_clusters/public/application/services/validate_seed_node.test.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/action_types.js b/x-pack/plugins/remote_clusters/public/application/store/action_types.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/action_types.js rename to x-pack/plugins/remote_clusters/public/application/store/action_types.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/add_cluster.js b/x-pack/plugins/remote_clusters/public/application/store/actions/add_cluster.js similarity index 88% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/add_cluster.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/add_cluster.js index 726bc56b08f12..0b7838f48b137 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/add_cluster.js +++ b/x-pack/plugins/remote_clusters/public/application/store/actions/add_cluster.js @@ -35,12 +35,12 @@ export const addCluster = cluster => async dispatch => { ]); } catch (error) { if (error) { - const { statusCode, data } = error; + const { body } = error; - // Expect an error in the shape provided by Angular's $http service. - if (data) { - // Some errors have statusCode directly available but some are under a data property. - if ((statusCode || (data && data.statusCode)) === 409) { + // Expect an error in the shape provided by http service. + if (body) { + const { statusCode, message } = body; + if (statusCode && statusCode === 409) { return dispatch({ type: ADD_CLUSTER_FAILURE, payload: { @@ -63,9 +63,8 @@ export const addCluster = cluster => async dispatch => { error: { message: i18n.translate('xpack.remoteClusters.addAction.failedDefaultErrorMessage', { defaultMessage: 'Request failed with a {statusCode} error. {message}', - values: { statusCode, message: data.message }, + values: { statusCode, message }, }), - cause: data.cause, }, }, }); @@ -74,7 +73,7 @@ export const addCluster = cluster => async dispatch => { // This error isn't an HTTP error, so let the fatal error screen tell the user something // unexpected happened. - return fatalError( + return fatalError.add( error, i18n.translate('xpack.remoteClusters.addAction.errorTitle', { defaultMessage: 'Error adding cluster', diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/store/actions/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/detail_panel.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/detail_panel.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/edit_cluster.js b/x-pack/plugins/remote_clusters/public/application/store/actions/edit_cluster.js similarity index 90% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/edit_cluster.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/edit_cluster.js index 3ed481aa0f8ea..062704472521e 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/edit_cluster.js +++ b/x-pack/plugins/remote_clusters/public/application/store/actions/edit_cluster.js @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; -import { fatalError, toasts } from '../../services/notification'; +import { toasts, fatalError } from '../../services/notification'; import { CRUD_APP_BASE_PATH } from '../../constants'; import { loadClusters } from './load_clusters'; @@ -39,19 +39,19 @@ export const editCluster = cluster => async dispatch => { ]); } catch (error) { if (error) { - const { statusCode, data } = error; + const { body } = error; - // Expect an error in the shape provided by Angular's $http service. - if (data) { + // Expect an error in the shape provided by http service. + if (body) { + const { statusCode, message } = body; return dispatch({ type: EDIT_CLUSTER_FAILURE, payload: { error: { message: i18n.translate('xpack.remoteClusters.editAction.failedDefaultErrorMessage', { defaultMessage: 'Request failed with a {statusCode} error. {message}', - values: { statusCode, message: data.message }, + values: { statusCode, message }, }), - cause: data.cause, }, }, }); @@ -60,7 +60,7 @@ export const editCluster = cluster => async dispatch => { // This error isn't an HTTP error, so let the fatal error screen tell the user something // unexpected happened. - return fatalError( + return fatalError.add( error, i18n.translate('xpack.remoteClusters.editAction.errorTitle', { defaultMessage: 'Error editing cluster', diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/index.js b/x-pack/plugins/remote_clusters/public/application/store/actions/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/index.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/load_clusters.js b/x-pack/plugins/remote_clusters/public/application/store/actions/load_clusters.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/load_clusters.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/load_clusters.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/refresh_clusters.js b/x-pack/plugins/remote_clusters/public/application/store/actions/refresh_clusters.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/refresh_clusters.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/refresh_clusters.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/remove_clusters.js b/x-pack/plugins/remote_clusters/public/application/store/actions/remove_clusters.js similarity index 95% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/actions/remove_clusters.js rename to x-pack/plugins/remote_clusters/public/application/store/actions/remove_clusters.js index 4086a91e29021..8e22eac8b292b 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/store/actions/remove_clusters.js +++ b/x-pack/plugins/remote_clusters/public/application/store/actions/remove_clusters.js @@ -30,7 +30,7 @@ function getErrorTitle(count, name = null) { } } else { return i18n.translate('xpack.remoteClusters.removeAction.errorMultipleNotificationTitle', { - defaultMessage: `Error removing '{count}' remote clusters`, + defaultMessage: `Error removing {count} remote clusters`, values: { count }, }); } @@ -46,7 +46,7 @@ export const removeClusters = names => async (dispatch, getState) => { await Promise.all([ sendRemoveClusterRequest(names.join(',')).then(response => { - ({ itemsDeleted, errors } = response.data); + ({ itemsDeleted, errors } = response); }), // Wait at least half a second to avoid a weird flicker of the saving feedback (only visible // when requests resolve very quickly). @@ -55,7 +55,7 @@ export const removeClusters = names => async (dispatch, getState) => { const errorTitle = getErrorTitle(names.length, names[0]); toasts.addDanger({ title: errorTitle, - text: error.data.message, + text: error.body?.message, }); }); diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/index.js b/x-pack/plugins/remote_clusters/public/application/store/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/index.js rename to x-pack/plugins/remote_clusters/public/application/store/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/middleware/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/store/middleware/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/middleware/detail_panel.js rename to x-pack/plugins/remote_clusters/public/application/store/middleware/detail_panel.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/middleware/index.js b/x-pack/plugins/remote_clusters/public/application/store/middleware/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/middleware/index.js rename to x-pack/plugins/remote_clusters/public/application/store/middleware/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/add_cluster.js b/x-pack/plugins/remote_clusters/public/application/store/reducers/add_cluster.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/add_cluster.js rename to x-pack/plugins/remote_clusters/public/application/store/reducers/add_cluster.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/clusters.js b/x-pack/plugins/remote_clusters/public/application/store/reducers/clusters.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/clusters.js rename to x-pack/plugins/remote_clusters/public/application/store/reducers/clusters.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/detail_panel.js b/x-pack/plugins/remote_clusters/public/application/store/reducers/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/detail_panel.js rename to x-pack/plugins/remote_clusters/public/application/store/reducers/detail_panel.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/edit_cluster.js b/x-pack/plugins/remote_clusters/public/application/store/reducers/edit_cluster.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/edit_cluster.js rename to x-pack/plugins/remote_clusters/public/application/store/reducers/edit_cluster.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/index.js b/x-pack/plugins/remote_clusters/public/application/store/reducers/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/index.js rename to x-pack/plugins/remote_clusters/public/application/store/reducers/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/remove_cluster.js b/x-pack/plugins/remote_clusters/public/application/store/reducers/remove_cluster.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/reducers/remove_cluster.js rename to x-pack/plugins/remote_clusters/public/application/store/reducers/remove_cluster.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/selectors/index.js b/x-pack/plugins/remote_clusters/public/application/store/selectors/index.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/selectors/index.js rename to x-pack/plugins/remote_clusters/public/application/store/selectors/index.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/store/store.js b/x-pack/plugins/remote_clusters/public/application/store/store.js similarity index 100% rename from x-pack/legacy/plugins/remote_clusters/public/app/store/store.js rename to x-pack/plugins/remote_clusters/public/application/store/store.js diff --git a/x-pack/legacy/plugins/remote_clusters/public/app/services/notification.ts b/x-pack/plugins/remote_clusters/public/index.ts similarity index 59% rename from x-pack/legacy/plugins/remote_clusters/public/app/services/notification.ts rename to x-pack/plugins/remote_clusters/public/index.ts index 1ac329253cddb..dbe22b71b48df 100644 --- a/x-pack/legacy/plugins/remote_clusters/public/app/services/notification.ts +++ b/x-pack/plugins/remote_clusters/public/index.ts @@ -3,11 +3,6 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { RemoteClustersUIPlugin } from './plugin'; -export let toasts: any; -export let fatalError: any; - -export function init(_toasts: any, _fatalError: any): void { - toasts = _toasts; - fatalError = _fatalError; -} +export const plugin = () => new RemoteClustersUIPlugin(); diff --git a/x-pack/plugins/remote_clusters/public/plugin.ts b/x-pack/plugins/remote_clusters/public/plugin.ts new file mode 100644 index 0000000000000..5b84fa1fde369 --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/plugin.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +import { CoreSetup, Plugin, CoreStart } from 'kibana/public'; +import { init as initBreadcrumbs } from './application/services/breadcrumb'; +import { init as initDocumentation } from './application/services/documentation'; +import { init as initHttp } from './application/services/http'; +import { init as initUiMetric } from './application/services/ui_metric'; +import { init as initNotification } from './application/services/notification'; +import { init as initRedirect } from './application/services/redirect'; +import { Dependencies } from './types'; + +export class RemoteClustersUIPlugin implements Plugin { + setup( + { notifications: { toasts }, http, getStartServices }: CoreSetup, + { management, usageCollection }: Dependencies + ) { + const esSection = management.sections.getSection('elasticsearch'); + + esSection!.registerApp({ + id: 'remote_clusters', + title: i18n.translate('xpack.remoteClusters.appTitle', { + defaultMessage: 'Remote Clusters', + }), + mount: async ({ element, setBreadcrumbs }) => { + const [core] = await getStartServices(); + const { + i18n: { Context: i18nContext }, + docLinks, + fatalErrors, + } = core; + + // Initialize services + initBreadcrumbs(setBreadcrumbs); + initDocumentation(docLinks); + initUiMetric(usageCollection); + initNotification(toasts, fatalErrors); + initHttp(http); + + const { renderApp } = await import('./application'); + return renderApp(element, i18nContext); + }, + }); + } + + start({ application }: CoreStart) { + initRedirect(application.navigateToApp); + } + + stop() {} +} diff --git a/x-pack/plugins/remote_clusters/public/types.ts b/x-pack/plugins/remote_clusters/public/types.ts new file mode 100644 index 0000000000000..45ae90f91587a --- /dev/null +++ b/x-pack/plugins/remote_clusters/public/types.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ManagementSetup } from 'src/plugins/management/public'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/public'; +import { RegisterManagementAppArgs } from 'src/plugins/management/public'; +import { I18nStart } from 'kibana/public'; + +export interface Dependencies { + management: ManagementSetup; + usageCollection: UsageCollectionSetup; +} + +export { RegisterManagementAppArgs }; + +export { I18nStart }; diff --git a/x-pack/plugins/remote_clusters/server/config.ts b/x-pack/plugins/remote_clusters/server/config.ts new file mode 100644 index 0000000000000..9525fe1e2a0db --- /dev/null +++ b/x-pack/plugins/remote_clusters/server/config.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from 'kibana/server'; + +export const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: true }), +}); + +export type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, + exposeToBrowser: { + enabled: true, + }, +}; diff --git a/x-pack/plugins/remote_clusters/server/index.ts b/x-pack/plugins/remote_clusters/server/index.ts index 896161d82919b..927fa768fc9fd 100644 --- a/x-pack/plugins/remote_clusters/server/index.ts +++ b/x-pack/plugins/remote_clusters/server/index.ts @@ -6,4 +6,6 @@ import { PluginInitializerContext } from 'kibana/server'; import { RemoteClustersServerPlugin } from './plugin'; +export { config } from './config'; + export const plugin = (ctx: PluginInitializerContext) => new RemoteClustersServerPlugin(ctx); diff --git a/x-pack/plugins/remote_clusters/server/plugin.ts b/x-pack/plugins/remote_clusters/server/plugin.ts index dd0bb536d2695..d15ae44c8d5db 100644 --- a/x-pack/plugins/remote_clusters/server/plugin.ts +++ b/x-pack/plugins/remote_clusters/server/plugin.ts @@ -6,10 +6,12 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, Logger, Plugin, PluginInitializerContext } from 'src/core/server'; -import { PLUGIN } from '../common/constants'; +import { Observable } from 'rxjs'; import { LICENSE_CHECK_STATE } from '../../licensing/common/types'; -import { Dependencies, LicenseStatus, RouteDependencies } from './types'; +import { PLUGIN } from '../common/constants'; +import { Dependencies, LicenseStatus, RouteDependencies } from './types'; +import { ConfigType } from './config'; import { registerGetRoute, registerAddRoute, @@ -20,9 +22,11 @@ import { export class RemoteClustersServerPlugin implements Plugin { licenseStatus: LicenseStatus; log: Logger; + config: Observable; - constructor({ logger }: PluginInitializerContext) { + constructor({ logger, config }: PluginInitializerContext) { this.log = logger.get(); + this.config = config.create(); this.licenseStatus = { valid: false }; } diff --git a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts index aa09b6bf45667..e4ede01ca23ea 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/add_route.ts @@ -38,8 +38,7 @@ export const register = (deps: RouteDependencies): void => { // Check if cluster already exists. const existingCluster = await doesClusterExist(callAsCurrentUser, name); if (existingCluster) { - return response.customError({ - statusCode: 409, + return response.conflict({ body: { message: i18n.translate( 'xpack.remoteClusters.addRemoteCluster.existingRemoteClusterErrorMessage', diff --git a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts index fd707f15ad11e..ed584307d84c1 100644 --- a/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts +++ b/x-pack/plugins/remote_clusters/server/routes/api/update_route.ts @@ -44,8 +44,7 @@ export const register = (deps: RouteDependencies): void => { // Check if cluster does exist. const existingCluster = await doesClusterExist(callAsCurrentUser, name); if (!existingCluster) { - return response.customError({ - statusCode: 404, + return response.notFound({ body: { message: i18n.translate( 'xpack.remoteClusters.updateRemoteCluster.noRemoteClusterErrorMessage', From 94c856dd428fbf4638b3dca1e2cbf0cfa0c7bf1c Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Fri, 14 Feb 2020 20:14:59 -0700 Subject: [PATCH 13/65] [SIEM][Detection Engine] Fixes queries to ignore errors when signals index is not present ## Summary Fixes a bug where if you try to query the signals index and it is not present you would get an error that would bubble up to the UI. Before: Screen Shot 2020-02-13 at 12 57 55 PM After: Screen Shot 2020-02-13 at 8 43 28 PM Screen Shot 2020-02-13 at 8 43 34 PM Also fixes a regression bug with error toasters showing up when they shouldn't because of a signals index not existing. This only effects 7.6.x and master and is _not_ part of 7.6.0 at the moment. * https://github.com/elastic/kibana/issues/57641 ### Checklist Delete any items that are not applicable to this PR. ~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~ ~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~ ~~- [ ] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios~~ ~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~ ~~- [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)~~ ~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~ ### For maintainers ~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~ --- .../signals/errors_types/get_index_error.ts | 4 +- .../signals/use_signal_index.tsx | 2 +- .../signals/open_close_signals_route.ts | 1 + .../routes/signals/query_signals_route.ts | 1 + .../security_and_spaces/tests/index.ts | 2 + .../tests/open_close_signals.ts | 54 +++++++++++++++++ .../tests/query_signals.ts | 60 +++++++++++++++++++ .../security_and_spaces/tests/utils.ts | 30 ++++++++++ 8 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts create mode 100644 x-pack/test/detection_engine_api_integration/security_and_spaces/tests/query_signals.ts diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts index e4f2a658b7362..4f45b480772f2 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/errors_types/get_index_error.ts @@ -8,13 +8,13 @@ import { MessageBody } from '../../../../components/ml/api/throw_if_not_ok'; export class SignalIndexError extends Error { message: string = ''; - statusCode: number = -1; + status_code: number = -1; error: string = ''; constructor(errObj: MessageBody) { super(errObj.message); this.message = errObj.message ?? ''; - this.statusCode = errObj.statusCode ?? -1; + this.status_code = errObj.status_code ?? -1; this.error = errObj.error ?? ''; this.name = 'SignalIndexError'; diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx index 2635d6e5540f1..813bd2483689c 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/signals/use_signal_index.tsx @@ -60,7 +60,7 @@ export const useSignalIndex = (): Return => { signalIndexName: null, createDeSignalIndex: createIndex, }); - if (error instanceof SignalIndexError && error.statusCode !== 404) { + if (error instanceof SignalIndexError && error.status_code !== 404) { errorToToaster({ title: i18n.SIGNAL_GET_NAME_FAILURE, error, dispatchToaster }); } } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index b2b938625180e..dd3b8d3c99e0c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -54,6 +54,7 @@ export const setSignalsStatusRouteDef = ( }, query: queryObject, }, + ignoreUnavailable: true, }); } catch (exc) { // error while getting or updating signal with id: id in signal index .siem-signals diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts index a3602ffbded41..adb6e5f32921a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -38,6 +38,7 @@ export const querySignalsRouteDef = ( return clusterClient.callAsCurrentUser('search', { index, body: { query, aggs, _source, track_total_hits, size }, + ignoreUnavailable: true, }); } catch (exc) { // error while getting or updating signal with id: id in signal index .siem-signals diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts index ca6ef5b6cede9..b8034fd92e988 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/index.ts @@ -25,5 +25,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => { loadTestFile(require.resolve('./update_rules_bulk')); loadTestFile(require.resolve('./patch_rules_bulk')); loadTestFile(require.resolve('./patch_rules')); + loadTestFile(require.resolve('./query_signals')); + loadTestFile(require.resolve('./open_close_signals')); }); }; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts new file mode 100644 index 0000000000000..e9e3e4299d108 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/open_close_signals.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { DETECTION_ENGINE_SIGNALS_STATUS_URL } from '../../../../legacy/plugins/siem/common/constants'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { + createSignalsIndex, + deleteSignalsIndex, + setSignalStatus, + getSignalStatusEmptyResponse, +} from './utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + + describe('open_close_signals', () => { + describe('validation checks', () => { + it('should not give errors when querying and the signals index does not exist yet', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setSignalStatus({ signalIds: ['123'], status: 'open' })) + .expect(200); + + // remove any server generated items that are indeterministic + delete body.took; + + expect(body).to.eql(getSignalStatusEmptyResponse()); + }); + + it('should not give errors when querying and the signals index does exist and is empty', async () => { + await createSignalsIndex(supertest); + const { body } = await supertest + .post(DETECTION_ENGINE_SIGNALS_STATUS_URL) + .set('kbn-xsrf', 'true') + .send(setSignalStatus({ signalIds: ['123'], status: 'open' })) + .expect(200); + + // remove any server generated items that are indeterministic + delete body.took; + + expect(body).to.eql(getSignalStatusEmptyResponse()); + + await deleteSignalsIndex(supertest); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/query_signals.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/query_signals.ts new file mode 100644 index 0000000000000..6fa62412ed467 --- /dev/null +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/query_signals.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; + +import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../legacy/plugins/siem/common/constants'; +import { FtrProviderContext } from '../../common/ftr_provider_context'; +import { getSignalStatus, createSignalsIndex, deleteSignalsIndex } from './utils'; + +// eslint-disable-next-line import/no-default-export +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + + describe('query_signals_route', () => { + describe('validation checks', () => { + it('should not give errors when querying and the signals index does not exist yet', async () => { + const { body } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getSignalStatus()) + .expect(200); + + // remove any server generated items that are indeterministic + delete body.took; + + expect(body).to.eql({ + timed_out: false, + _shards: { total: 0, successful: 0, skipped: 0, failed: 0 }, + hits: { total: { value: 0, relation: 'eq' }, max_score: 0, hits: [] }, + }); + }); + + it('should not give errors when querying and the signals index does exist and is empty', async () => { + await createSignalsIndex(supertest); + const { body } = await supertest + .post(DETECTION_ENGINE_QUERY_SIGNALS_URL) + .set('kbn-xsrf', 'true') + .send(getSignalStatus()) + .expect(200); + + // remove any server generated items that are indeterministic + delete body.took; + + expect(body).to.eql({ + timed_out: false, + _shards: { total: 1, successful: 1, skipped: 0, failed: 0 }, + hits: { total: { value: 0, relation: 'eq' }, max_score: null, hits: [] }, + aggregations: { + statuses: { doc_count_error_upper_bound: 0, sum_other_doc_count: 0, buckets: [] }, + }, + }); + + await deleteSignalsIndex(supertest); + }); + }); + }); +}; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts index b78073c0e737b..bef700d0409a5 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts @@ -53,6 +53,36 @@ export const getSimpleRule = (ruleId = 'rule-1'): Partial = query: 'user.name: root or user.name: admin', }); +export const getSignalStatus = () => ({ + aggs: { statuses: { terms: { field: 'signal.status', size: 10 } } }, +}); + +export const setSignalStatus = ({ + signalIds, + status, +}: { + signalIds: string[]; + status: 'open' | 'closed'; +}) => ({ + signal_ids: signalIds, + status, +}); + +export const getSignalStatusEmptyResponse = () => ({ + timed_out: false, + total: 0, + updated: 0, + deleted: 0, + batches: 0, + version_conflicts: 0, + noops: 0, + retries: { bulk: 0, search: 0 }, + throttled_millis: 0, + requests_per_second: -1, + throttled_until_millis: 0, + failures: [], +}); + /** * This is a typical simple rule for testing that is easy for most basic testing */ From e370df2e5f3fc3e40ac238e1f8cc15cd2c1978f2 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Sat, 15 Feb 2020 12:46:04 +0100 Subject: [PATCH 14/65] Expose Vis on the contract as it requires visTypes (#56968) Co-authored-by: Elastic Machine --- .../buckets/_terms_other_bucket_helper.js | 4 +- .../kibana/public/discover/build_services.ts | 3 ++ .../kibana/public/discover/legacy.ts | 10 ++++- .../discover/np_ready/angular/discover.js | 7 ++- .../kibana/public/discover/plugin.ts | 6 +++ .../__tests__/region_map_visualization.js | 8 ++-- .../coordinate_maps_visualization.js | 8 ++-- .../public/metric_vis_type.test.ts | 2 +- .../public/agg_table/__tests__/agg_table.js | 13 +++--- .../agg_table/__tests__/agg_table_group.js | 6 +-- .../__tests__/tag_cloud_visualization.js | 4 +- .../public/__tests__/vega_visualization.js | 8 ++-- .../__tests__/visualizations/pie_chart.js | 6 +-- .../np_ready/public/legacy/__tests__/_vis.js | 7 ++- .../public/np_ready/public/mocks.ts | 1 + .../public/np_ready/public/plugin.ts | 3 ++ .../np_ready/public/{vis.d.ts => vis.ts} | 9 ++-- .../public/np_ready/public/vis_impl.d.ts | 44 +++++++++++++++++++ .../np_ready/public/{vis.js => vis_impl.js} | 6 +-- .../public/saved_visualizations/_saved_vis.ts | 9 ++-- .../tabify/__tests__/_get_columns.js | 12 ++--- .../tabify/__tests__/_integration.js | 6 +-- .../tabify/__tests__/_response_writer.js | 8 ++-- .../ui/public/vis/__tests__/_agg_config.js | 42 +++++++++--------- .../ui/public/vis/__tests__/_agg_configs.js | 26 +++++------ 25 files changed, 165 insertions(+), 93 deletions(-) rename src/legacy/core_plugins/visualizations/public/np_ready/public/{vis.d.ts => vis.ts} (84%) create mode 100644 src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts rename src/legacy/core_plugins/visualizations/public/np_ready/public/{vis.js => vis_impl.js} (98%) diff --git a/src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js b/src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js index 247290731df57..749dad377f2e2 100644 --- a/src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js +++ b/src/legacy/core_plugins/data/public/search/aggs/__tests__/buckets/_terms_other_bucket_helper.js @@ -24,7 +24,7 @@ import { mergeOtherBucketAggResponse, updateMissingBucket, } from '../../buckets/_terms_other_bucket_helper'; -import { Vis } from '../../../../../../../core_plugins/visualizations/public'; +import { start as visualizationsStart } from '../../../../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; const visConfigSingleTerm = { @@ -191,7 +191,7 @@ describe('Terms Agg Other bucket helper', () => { ngMock.inject(Private => { const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider); - vis = new Vis(indexPattern, aggConfig); + vis = new visualizationsStart.Vis(indexPattern, aggConfig); }); } diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts index f9265323b2dcb..81f1c911f1cc9 100644 --- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts @@ -36,6 +36,7 @@ import { SharePluginStart } from '../../../../../plugins/share/public'; import { SavedSearch } from './np_ready/types'; import { DocViewsRegistry } from './np_ready/doc_views/doc_views_registry'; import { ChartsPluginStart } from '../../../../../plugins/charts/public'; +import { VisualizationsStart } from '../../../visualizations/public'; export interface DiscoverServices { addBasePath: (path: string) => string; @@ -56,6 +57,7 @@ export interface DiscoverServices { getSavedSearchById: (id: string) => Promise; getSavedSearchUrlById: (id: string) => Promise; uiSettings: IUiSettingsClient; + visualizations: VisualizationsStart; } export async function buildServices( core: CoreStart, @@ -89,5 +91,6 @@ export async function buildServices( timefilter: plugins.data.query.timefilter.timefilter, toastNotifications: core.notifications.toasts, uiSettings: core.uiSettings, + visualizations: plugins.visualizations, }; } diff --git a/src/legacy/core_plugins/kibana/public/discover/legacy.ts b/src/legacy/core_plugins/kibana/public/discover/legacy.ts index ff44fbbe115d5..a1ef646f4fe85 100644 --- a/src/legacy/core_plugins/kibana/public/discover/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/discover/legacy.ts @@ -20,10 +20,18 @@ import { PluginInitializerContext } from 'kibana/public'; import { npSetup, npStart } from 'ui/new_platform'; import { plugin } from './index'; +import { + setup as visualizationsSetup, + start as visualizationsStart, +} from '../../../../core_plugins/visualizations/public/np_ready/public/legacy'; // Legacy compatibility part - to be removed at cutover, replaced by a kibana.json file export const pluginInstance = plugin({} as PluginInitializerContext); export const setup = pluginInstance.setup(npSetup.core, { ...npSetup.plugins, + visualizations: visualizationsSetup, +}); +export const start = pluginInstance.start(npStart.core, { + ...npStart.plugins, + visualizations: visualizationsStart, }); -export const start = pluginInstance.start(npStart.core, npStart.plugins); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js index 39a9ca6641fd1..bf5049cd976a3 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.js @@ -60,7 +60,6 @@ import { ensureDefaultIndexPattern, registerTimefilterWithGlobalStateFactory, } from '../../kibana_services'; -import { Vis } from '../../../../../visualizations/public'; const { core, @@ -71,6 +70,7 @@ const { timefilter, toastNotifications, uiSettings, + visualizations, } = getServices(); import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; @@ -990,7 +990,10 @@ function discoverController( }, }; - $scope.vis = new Vis($scope.searchSource.getField('index'), visSavedObject.visState); + $scope.vis = new visualizations.Vis( + $scope.searchSource.getField('index'), + visSavedObject.visState + ); visSavedObject.vis = $scope.vis; $scope.searchSource.onRequestStart((searchSource, options) => { diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts index a495b56d5e9ea..565382313e369 100644 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts @@ -36,6 +36,10 @@ import { DocViewInput, DocViewInputFn } from './np_ready/doc_views/doc_views_typ import { DocViewTable } from './np_ready/components/table/table'; import { JsonCodeBlock } from './np_ready/components/json_code_block/json_code_block'; import { HomePublicPluginSetup } from '../../../../../plugins/home/public'; +import { + VisualizationsStart, + VisualizationsSetup, +} from '../../../visualizations/public/np_ready/public'; /** * These are the interfaces with your public contracts. You should export these @@ -51,6 +55,7 @@ export interface DiscoverSetupPlugins { embeddable: IEmbeddableSetup; kibanaLegacy: KibanaLegacySetup; home: HomePublicPluginSetup; + visualizations: VisualizationsSetup; } export interface DiscoverStartPlugins { uiActions: UiActionsStart; @@ -60,6 +65,7 @@ export interface DiscoverStartPlugins { data: DataPublicPluginStart; share: SharePluginStart; inspector: any; + visualizations: VisualizationsStart; } const innerAngularName = 'app/discover'; const embeddableAngularName = 'app/discoverEmbeddable'; diff --git a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js index 55447905e6421..f11aab9b9db88 100644 --- a/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/__tests__/region_map_visualization.js @@ -38,8 +38,10 @@ import afterdatachangePng from './afterdatachange.png'; import afterdatachangeandresizePng from './afterdatachangeandresize.png'; import aftercolorchangePng from './aftercolorchange.png'; import changestartupPng from './changestartup.png'; -import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; -import { Vis } from '../../../visualizations/public/np_ready/public/vis'; +import { + setup as visualizationsSetup, + start as visualizationsStart, +} from '../../../visualizations/public/np_ready/public/legacy'; import { createRegionMapVisualization } from '../region_map_visualization'; import { createRegionMapTypeDefinition } from '../region_map_type'; @@ -158,7 +160,7 @@ describe('RegionMapsVisualizationTests', function() { imageComparator = new ImageComparator(); - vis = new Vis(indexPattern, { + vis = new visualizationsStart.Vis(indexPattern, { type: 'region_map', }); diff --git a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js index ef2ea831e84fd..27e9459c7e06c 100644 --- a/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js +++ b/src/legacy/core_plugins/tile_map/public/__tests__/coordinate_maps_visualization.js @@ -32,8 +32,10 @@ import EMS_TILES from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_ import EMS_STYLE_ROAD_MAP_BRIGHT from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_bright'; import EMS_STYLE_ROAD_MAP_DESATURATED from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_desaturated'; import EMS_STYLE_DARK_MAP from '../../../../ui/public/vis/__tests__/map/ems_mocks/sample_style_dark'; -import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; -import { Vis } from '../../../visualizations/public/np_ready/public/vis'; +import { + setup as visualizationsSetup, + start as visualizationsStart, +} from '../../../visualizations/public/np_ready/public/legacy'; import { createTileMapVisualization } from '../tile_map_visualization'; import { createTileMapTypeDefinition } from '../tile_map_type'; @@ -124,7 +126,7 @@ describe('CoordinateMapsVisualizationTest', function() { setupDOM('512px', '512px'); imageComparator = new ImageComparator(); - vis = new Vis(indexPattern, { + vis = new visualizationsStart.Vis(indexPattern, { type: 'tile_map', }); vis.params = { diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts index 15cd920b79e4a..67b5d018f4638 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts @@ -59,7 +59,7 @@ describe('metric_vis - createMetricVisTypeDefinition', () => { // TODO: remove when Vis is converted to typescript. Only importing Vis as type // @ts-ignore - vis = new Vis(stubIndexPattern, { + vis = new visualizationsStart.Vis(stubIndexPattern, { type: 'metric', aggs: [{ id: '1', type: 'top_hits', schema: 'metric', params: { field: 'ip' } }], }); diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js index fc5a24a66dab3..0dbff60613cb0 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table.js @@ -26,10 +26,11 @@ import sinon from 'sinon'; import { tabifyAggResponse, npStart } from '../../legacy_imports'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { round } from 'lodash'; - -import { Vis } from '../../../../visualizations/public'; import { tableVisTypeDefinition } from '../../table_vis_type'; -import { setup as visualizationsSetup } from '../../../../visualizations/public/np_ready/public/legacy'; +import { + setup as visualizationsSetup, + start as visualizationsStart, +} from '../../../../visualizations/public/np_ready/public/legacy'; import { getAngularModule } from '../../get_inner_angular'; import { initTableVisLegacyModule } from '../../table_vis_legacy_module'; import { tableVisResponseHandler } from '../../table_vis_response_handler'; @@ -42,10 +43,10 @@ describe('Table Vis - AggTable Directive', function() { const tabifiedData = {}; const init = () => { - const vis1 = new Vis(indexPattern, 'table'); + const vis1 = new visualizationsStart.Vis(indexPattern, 'table'); tabifiedData.metricOnly = tabifyAggResponse(vis1.aggs, fixtures.metricOnly); - const vis2 = new Vis(indexPattern, { + const vis2 = new visualizationsStart.Vis(indexPattern, { type: 'table', params: { showMetricsAtAllLevels: true, @@ -64,7 +65,7 @@ describe('Table Vis - AggTable Directive', function() { metricsAtAllLevels: true, }); - const vis3 = new Vis(indexPattern, { + const vis3 = new visualizationsStart.Vis(indexPattern, { type: 'table', aggs: [ { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js index 3c633f21cbabb..f6ae41b024b7d 100644 --- a/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js +++ b/src/legacy/core_plugins/vis_type_table/public/agg_table/__tests__/agg_table_group.js @@ -23,10 +23,10 @@ import expect from '@kbn/expect'; import fixtures from 'fixtures/fake_hierarchical_data'; import { tabifyAggResponse, npStart } from '../../legacy_imports'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { Vis } from '../../../../visualizations/public'; import { getAngularModule } from '../../get_inner_angular'; import { initTableVisLegacyModule } from '../../table_vis_legacy_module'; import { tableVisResponseHandler } from '../../table_vis_response_handler'; +import { start as visualizationsStart } from '../../../../visualizations/public/np_ready/public/legacy'; describe('Table Vis - AggTableGroup Directive', function() { let $rootScope; @@ -35,10 +35,10 @@ describe('Table Vis - AggTableGroup Directive', function() { const tabifiedData = {}; const init = () => { - const vis1 = new Vis(indexPattern, 'table'); + const vis1 = new visualizationsStart.Vis(indexPattern, 'table'); tabifiedData.metricOnly = tabifyAggResponse(vis1.aggs, fixtures.metricOnly); - const vis2 = new Vis(indexPattern, { + const vis2 = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: [ { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js index 5f7d1ad90ecf8..55ecf98f994d2 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud_visualization.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { Vis } from '../../../../visualizations/public/np_ready/public/vis'; +import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import { ImageComparator } from 'test_utils/image_comparator'; import { createTagCloudVisualization } from '../tag_cloud_visualization'; import basicdrawPng from './basicdraw.png'; @@ -76,7 +76,7 @@ describe('TagCloudVisualizationTest', function() { beforeEach(async function() { setupDOM('512px', '512px'); imageComparator = new ImageComparator(); - vis = new Vis(indexPattern, { + vis = new visualizationsStart.Vis(indexPattern, { type: 'tagcloud', params: { bucket: { accessor: 0, format: {} }, diff --git a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js index 868e5729bd494..378590af29d3a 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js +++ b/src/legacy/core_plugins/vis_type_vega/public/__tests__/vega_visualization.js @@ -23,7 +23,6 @@ import ngMock from 'ng_mock'; import $ from 'jquery'; import { createVegaVisualization } from '../vega_visualization'; import LogstashIndexPatternStubProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { Vis } from '../../../visualizations/public/np_ready/public/vis'; import { ImageComparator } from 'test_utils/image_comparator'; import vegaliteGraph from '!!raw-loader!./vegalite_graph.hjson'; @@ -41,7 +40,10 @@ import vegaMapImage256 from './vega_map_image_256.png'; import { VegaParser } from '../data_model/vega_parser'; import { SearchCache } from '../data_model/search_cache'; -import { setup as visualizationsSetup } from '../../../visualizations/public/np_ready/public/legacy'; +import { + setup as visualizationsSetup, + start as visualizationsStart, +} from '../../../visualizations/public/np_ready/public/legacy'; import { createVegaTypeDefinition } from '../vega_type'; // TODO This is an integration test and thus requires a running platform. When moving to the new platform, // this test has to be migrated to the newly created integration test environment. @@ -106,7 +108,7 @@ describe('VegaVisualizations', () => { setupDOM('512px', '512px'); imageComparator = new ImageComparator(); - vis = new Vis(indexPattern, { type: 'vega' }); + vis = new visualizationsStart.Vis(indexPattern, { type: 'vega' }); }); afterEach(function() { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js index e4da572259b69..534a523103774 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js +++ b/src/legacy/core_plugins/vis_type_vislib/public/vislib/__tests__/visualizations/pie_chart.js @@ -25,7 +25,7 @@ import expect from '@kbn/expect'; import fixtures from 'fixtures/fake_hierarchical_data'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; -import { Vis } from '../../../../../visualizations/public'; +import { start as visualizationsStart } from '../../../../../visualizations/public/np_ready/public/legacy'; import { getVis, getMockUiState } from '../lib/fixtures/_vis_fixture'; import { tabifyAggResponse } from '../../../legacy_imports'; import { vislibSlicesResponseHandler } from '../../response_handler'; @@ -133,7 +133,7 @@ describe('No global chart settings', function() { responseHandler = vislibSlicesResponseHandler; let id1 = 1; - stubVis1 = new Vis(indexPattern, { + stubVis1 = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: rowAgg, }); @@ -222,7 +222,7 @@ describe('Vislib PieChart Class Test Suite', function() { responseHandler = vislibSlicesResponseHandler; let id = 1; - stubVis = new Vis(indexPattern, { + stubVis = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: dataAgg, }); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js index d963315ef79db..8c75ba24051b0 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/__tests__/_vis.js @@ -20,7 +20,6 @@ import _ from 'lodash'; import ngMock from 'ng_mock'; import expect from '@kbn/expect'; -import { Vis } from '../..'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; import { start as visualizations } from '../../legacy'; @@ -49,7 +48,7 @@ describe('Vis Class', function() { ); beforeEach(function() { - vis = new Vis(indexPattern, stateFixture); + vis = new visualizations.Vis(indexPattern, stateFixture); }); const verifyVis = function(vis) { @@ -85,7 +84,7 @@ describe('Vis Class', function() { describe('setState()', function() { it('should set the state to defaults', function() { - const vis = new Vis(indexPattern); + const vis = new visualizations.Vis(indexPattern); expect(vis).to.have.property('type'); expect(vis.type).to.eql(visTypes.get('histogram')); expect(vis).to.have.property('aggs'); @@ -101,7 +100,7 @@ describe('Vis Class', function() { expect(vis.isHierarchical()).to.be(true); }); it('should return false for non-hierarchical vis (like histogram)', function() { - const vis = new Vis(indexPattern); + const vis = new visualizations.Vis(indexPattern); expect(vis.isHierarchical()).to.be(false); }); }); diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts index 1063b1718b851..a948757d7bd83 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts @@ -49,6 +49,7 @@ const createStartContract = (): VisualizationsStart => ({ }, getSavedVisualizationsLoader: jest.fn(), showNewVisModal: jest.fn(), + Vis: jest.fn(), }); const createInstance = async () => { diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts index cbbeb7ff980a6..36c04923e3fd0 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts @@ -43,6 +43,7 @@ import { SavedObjectKibanaServicesWithVisualizations, } from '../../saved_visualizations'; import { SavedVisualizations } from '../../../../kibana/public/visualize/np_ready/types'; +import { VisImpl, VisImplConstructor } from './vis_impl'; import { showNewVisModal } from './wizard'; /** * Interface for this plugin's returned setup/start contracts. @@ -57,6 +58,7 @@ export interface VisualizationsStart { types: TypesStart; getSavedVisualizationsLoader: () => SavedVisualizations; showNewVisModal: typeof showNewVisModal; + Vis: VisImplConstructor; } export interface VisualizationsSetupDeps { @@ -131,6 +133,7 @@ export class VisualizationsPlugin types, getSavedVisualizationsLoader: () => this.getSavedVisualizationsLoader(), showNewVisModal, + Vis: VisImpl, }; } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts similarity index 84% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts index 71bf9bcf983ff..59a8013523ef6 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.d.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts @@ -42,9 +42,8 @@ export interface VisState { aggs: IAggConfigs; } -export declare class VisualizationController { - constructor(element: HTMLElement, vis: Vis); - public render(visData: any, visParams: any, update: { [key in Status]: boolean }): Promise; - public destroy(): void; - public isLoaded?(): Promise | void; +export interface VisualizationController { + render(visData: any, visParams: any, update: { [key in Status]: boolean }): Promise; + destroy(): void; + isLoaded?(): Promise | void; } diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts new file mode 100644 index 0000000000000..45d65efb5dcdf --- /dev/null +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts @@ -0,0 +1,44 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Vis, VisState } from './vis'; +import { VisType } from './types'; +import { IIndexPattern } from '../../../../../../plugins/data/common'; + +type InitVisStateType = + | Partial + | Partial & { type: string }> + | string; + +export type VisImplConstructor = new ( + indexPattern: IIndexPattern, + visState?: InitVisStateType +) => VisImpl; + +export declare class VisImpl implements Vis { + constructor(indexPattern: IIndexPattern, visState?: InitVisStateType); + + type: VisType; + + // Since we haven't typed everything here yet, we basically "any" the rest + // of that interface. This should be removed as soon as this type definition + // has been completed. But that way we at least have typing for a couple of + // properties on that type. + [key: string]: any; +} diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.js similarity index 98% rename from src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js rename to src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.js index 0c2e5012df439..6f4ab6d708184 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.js +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.js @@ -33,7 +33,7 @@ import { AggConfigs, PersistedState } from '../../legacy_imports'; import { updateVisualizationConfig } from './legacy/vis_update'; import { getTypes } from './services'; -class Vis extends EventEmitter { +class VisImpl extends EventEmitter { constructor(indexPattern, visState) { super(); visState = visState || {}; @@ -203,6 +203,6 @@ class Vis extends EventEmitter { } } -Vis.prototype.type = 'histogram'; +VisImpl.prototype.type = 'histogram'; -export { Vis }; +export { VisImpl }; diff --git a/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts index ca2305a9cc91c..8b8c5fe94cc95 100644 --- a/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/legacy/core_plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -26,14 +26,14 @@ */ import { SavedObject, SavedObjectKibanaServices } from 'ui/saved_objects/types'; import { createSavedObjectClass } from 'ui/saved_objects/saved_object'; -// @ts-ignore -import { updateOldState, Vis } from '../index'; +import { updateOldState } from '../index'; import { extractReferences, injectReferences } from './saved_visualization_references'; import { IIndexPattern } from '../../../../../plugins/data/public'; import { VisSavedObject } from '../embeddable/visualize_embeddable'; import { createSavedSearchesLoader } from '../../../kibana/public/discover'; import { VisualizeConstants } from '../../../kibana/public/visualize'; +import { VisImpl } from '../np_ready/public/vis_impl'; async function _afterEsResp(savedVis: VisSavedObject, services: any) { await _getLinkedSavedSearch(savedVis, services); @@ -72,9 +72,8 @@ async function _createVis(savedVis: VisSavedObject) { if (savedVis.visState) { savedVis.visState.title = savedVis.title; } - // the typescript compiler is wrong here, will be right when vis.js -> vis.ts - // @ts-ignore - savedVis.vis = new Vis(savedVis.searchSource!.getField('index'), savedVis.visState); + + savedVis.vis = new VisImpl(savedVis.searchSource!.getField('index')!, savedVis.visState); savedVis.vis!.savedSearchId = savedVis.savedSearchId; diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js index 56536a2e5b536..3eb41c03050d0 100644 --- a/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js +++ b/src/legacy/ui/public/agg_response/tabify/__tests__/_get_columns.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { tabifyGetColumns } from '../_get_columns'; -import { Vis } from '../../../../../core_plugins/visualizations/public'; +import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('get columns', function() { let indexPattern; @@ -33,7 +33,7 @@ describe('get columns', function() { ); it('should inject a count metric if no aggs exist', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'pie', }); while (vis.aggs.length) vis.aggs.pop(); @@ -49,7 +49,7 @@ describe('get columns', function() { }); it('should inject a count metric if only buckets exist', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: [ { @@ -68,7 +68,7 @@ describe('get columns', function() { }); it('should inject the metric after each bucket if the vis is hierarchical', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: [ { @@ -104,7 +104,7 @@ describe('get columns', function() { }); it('should inject the multiple metrics after each bucket if the vis is hierarchical', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: [ { @@ -156,7 +156,7 @@ describe('get columns', function() { }); it('should put all metrics at the end of the columns if the vis is not hierarchical', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js index c7c328000772b..f3f2e20149acf 100644 --- a/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js +++ b/src/legacy/ui/public/agg_response/tabify/__tests__/_integration.js @@ -22,7 +22,7 @@ import fixtures from 'fixtures/fake_hierarchical_data'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { tabifyAggResponse } from '../tabify'; -import { Vis } from '../../../../../core_plugins/visualizations/public'; +import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('tabifyAggResponse Integration', function() { @@ -42,7 +42,7 @@ describe('tabifyAggResponse Integration', function() { } it('transforms a simple response properly', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [], }); @@ -73,7 +73,7 @@ describe('tabifyAggResponse Integration', function() { let esResp; beforeEach(function() { - vis = new Vis(indexPattern, { + vis = new visualizationsStart.Vis(indexPattern, { type: 'pie', aggs: [ { type: 'avg', schema: 'metric', params: { field: 'bytes' } }, diff --git a/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js b/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js index ced495ef23c36..b0c0f2f3d9100 100644 --- a/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js +++ b/src/legacy/ui/public/agg_response/tabify/__tests__/_response_writer.js @@ -20,7 +20,7 @@ import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { TabbedAggResponseWriter } from '../_response_writer'; -import { Vis } from '../../../../../core_plugins/visualizations/public'; +import { start as visualizationsStart } from '../../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('TabbedAggResponseWriter class', function() { @@ -61,7 +61,7 @@ describe('TabbedAggResponseWriter class', function() { ]; const createResponseWritter = (aggs = [], opts = {}) => { - const vis = new Vis(indexPattern, { type: 'histogram', aggs: aggs }); + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: aggs }); return new TabbedAggResponseWriter(vis.getAggConfig(), opts); }; @@ -88,7 +88,7 @@ describe('TabbedAggResponseWriter class', function() { describe('sets timeRange', function() { it("to the first nested object's range", function() { - const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] }); + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [] }); const range = { gte: 0, lte: 100, @@ -106,7 +106,7 @@ describe('TabbedAggResponseWriter class', function() { }); it('to undefined if no nested object', function() { - const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] }); + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [] }); const writer = new TabbedAggResponseWriter(vis.getAggConfig(), { timeRange: {}, diff --git a/src/legacy/ui/public/vis/__tests__/_agg_config.js b/src/legacy/ui/public/vis/__tests__/_agg_config.js index 7dccf3eec18aa..9e53044f681ba 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_config.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_config.js @@ -20,8 +20,8 @@ import sinon from 'sinon'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; -import { Vis } from '../../../../core_plugins/visualizations/public'; import { AggType, AggConfig } from '../../agg_types'; +import { start as visualizationsStart } from '../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; @@ -37,7 +37,7 @@ describe('AggConfig', function() { describe('#toDsl', function() { it('calls #write()', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -55,7 +55,7 @@ describe('AggConfig', function() { }); it('uses the type name as the agg name', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -73,7 +73,7 @@ describe('AggConfig', function() { }); it('uses the params from #write() output as the agg params', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -93,7 +93,7 @@ describe('AggConfig', function() { }); it('includes subAggs from #write() output', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -219,8 +219,8 @@ describe('AggConfig', function() { testsIdentical.forEach((visConfig, index) => { it(`identical aggregations (${index})`, function() { - const vis1 = new Vis(indexPattern, visConfig); - const vis2 = new Vis(indexPattern, visConfig); + const vis1 = new visualizationsStart.Vis(indexPattern, visConfig); + const vis2 = new visualizationsStart.Vis(indexPattern, visConfig); expect(vis1.aggs.jsonDataEquals(vis2.aggs.aggs)).to.be(true); }); }); @@ -258,8 +258,8 @@ describe('AggConfig', function() { testsIdenticalDifferentOrder.forEach((test, index) => { it(`identical aggregations (${index}) - init json is in different order`, function() { - const vis1 = new Vis(indexPattern, test.config1); - const vis2 = new Vis(indexPattern, test.config2); + const vis1 = new visualizationsStart.Vis(indexPattern, test.config1); + const vis2 = new visualizationsStart.Vis(indexPattern, test.config2); expect(vis1.aggs.jsonDataEquals(vis2.aggs.aggs)).to.be(true); }); }); @@ -323,8 +323,8 @@ describe('AggConfig', function() { testsDifferent.forEach((test, index) => { it(`different aggregations (${index})`, function() { - const vis1 = new Vis(indexPattern, test.config1); - const vis2 = new Vis(indexPattern, test.config2); + const vis1 = new visualizationsStart.Vis(indexPattern, test.config1); + const vis2 = new visualizationsStart.Vis(indexPattern, test.config2); expect(vis1.aggs.jsonDataEquals(vis2.aggs.aggs)).to.be(false); }); }); @@ -332,7 +332,7 @@ describe('AggConfig', function() { describe('#toJSON', function() { it('includes the aggs id, params, type and schema', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -360,7 +360,7 @@ describe('AggConfig', function() { }); it('test serialization order is identical (for visual consistency)', function() { - const vis1 = new Vis(indexPattern, { + const vis1 = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -369,7 +369,7 @@ describe('AggConfig', function() { }, ], }); - const vis2 = new Vis(indexPattern, { + const vis2 = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -387,26 +387,26 @@ describe('AggConfig', function() { describe('#makeLabel', function() { it('uses the custom label if it is defined', function() { - const vis = new Vis(indexPattern, {}); + const vis = new visualizationsStart.Vis(indexPattern, {}); const aggConfig = vis.aggs.aggs[0]; aggConfig.params.customLabel = 'Custom label'; const label = aggConfig.makeLabel(); expect(label).to.be(aggConfig.params.customLabel); }); it('default label should be "Count"', function() { - const vis = new Vis(indexPattern, {}); + const vis = new visualizationsStart.Vis(indexPattern, {}); const aggConfig = vis.aggs.aggs[0]; const label = aggConfig.makeLabel(); expect(label).to.be('Count'); }); it('default label should be "Percentage of Count" when percentageMode is set to true', function() { - const vis = new Vis(indexPattern, {}); + const vis = new visualizationsStart.Vis(indexPattern, {}); const aggConfig = vis.aggs.aggs[0]; const label = aggConfig.makeLabel(true); expect(label).to.be('Percentage of Count'); }); - it('empty label if the Vis type is not defined', function() { - const vis = new Vis(indexPattern, {}); + it('empty label if the visualizationsStart.Vis type is not defined', function() { + const vis = new visualizationsStart.Vis(indexPattern, {}); const aggConfig = vis.aggs.aggs[0]; aggConfig.type = undefined; const label = aggConfig.makeLabel(); @@ -416,7 +416,7 @@ describe('AggConfig', function() { describe('#fieldFormatter - custom getFormat handler', function() { it('returns formatter from getFormat handler', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'metric', aggs: [ { @@ -447,7 +447,7 @@ describe('AggConfig', function() { let vis; beforeEach(function() { - vis = new Vis(indexPattern, visStateAggWithoutCustomGetFormat); + vis = new visualizationsStart.Vis(indexPattern, visStateAggWithoutCustomGetFormat); }); it("returns the field's formatter", function() { diff --git a/src/legacy/ui/public/vis/__tests__/_agg_configs.js b/src/legacy/ui/public/vis/__tests__/_agg_configs.js index 62ad9a40ad058..172523ec50c8b 100644 --- a/src/legacy/ui/public/vis/__tests__/_agg_configs.js +++ b/src/legacy/ui/public/vis/__tests__/_agg_configs.js @@ -22,7 +22,7 @@ import sinon from 'sinon'; import expect from '@kbn/expect'; import ngMock from 'ng_mock'; import { AggConfig, AggConfigs, AggGroupNames, Schemas } from '../../agg_types'; -import { Vis } from '../../../../core_plugins/visualizations/public'; +import { start as visualizationsStart } from '../../../../core_plugins/visualizations/public/np_ready/public/legacy'; import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern'; describe('AggConfigs', function() { @@ -38,7 +38,7 @@ describe('AggConfigs', function() { describe('constructor', function() { it('handles passing just a vis', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [], }); @@ -48,7 +48,7 @@ describe('AggConfigs', function() { }); it('converts configStates into AggConfig objects if they are not already', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [], }); @@ -72,7 +72,7 @@ describe('AggConfigs', function() { }); it('attempts to ensure that all states have an id', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [], }); @@ -154,7 +154,7 @@ describe('AggConfigs', function() { describe('#getRequestAggs', function() { it('performs a stable sort, but moves metrics to the bottom', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { type: 'avg', schema: 'metric' }, @@ -185,7 +185,7 @@ describe('AggConfigs', function() { describe('#getResponseAggs', function() { it('returns all request aggs for basic aggs', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { type: 'terms', schema: 'split' }, @@ -206,7 +206,7 @@ describe('AggConfigs', function() { }); it('expands aggs that have multiple responses', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { type: 'terms', schema: 'split' }, @@ -231,14 +231,14 @@ describe('AggConfigs', function() { describe('#toDsl', function() { it('uses the sorted aggs', function() { - const vis = new Vis(indexPattern, { type: 'histogram' }); + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram' }); sinon.spy(vis.aggs, 'getRequestAggs'); vis.aggs.toDsl(); expect(vis.aggs.getRequestAggs).to.have.property('callCount', 1); }); it('calls aggConfig#toDsl() on each aggConfig and compiles the nested output', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { type: 'date_histogram', schema: 'segment' }, @@ -272,7 +272,7 @@ describe('AggConfigs', function() { }); it("skips aggs that don't have a dsl representation", function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -295,7 +295,7 @@ describe('AggConfigs', function() { }); it('writes multiple metric aggregations at the same level', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { @@ -326,7 +326,7 @@ describe('AggConfigs', function() { }); it('writes multiple metric aggregations at every level if the vis is hierarchical', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { type: 'terms', schema: 'segment', params: { field: 'ip', orderBy: 1 } }, @@ -362,7 +362,7 @@ describe('AggConfigs', function() { }); it('adds the parent aggs of nested metrics at every level if the vis is hierarchical', function() { - const vis = new Vis(indexPattern, { + const vis = new visualizationsStart.Vis(indexPattern, { type: 'histogram', aggs: [ { From 3b232ae73577d2067c820d8b22d57a07cba21334 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Sat, 15 Feb 2020 19:32:16 -0500 Subject: [PATCH 15/65] Fix login redirect for expired sessions (#57157) --- .../security/public/services/auto_logout.js | 22 ++++- x-pack/plugins/security/public/plugin.tsx | 7 +- .../public/session/session_expired.test.ts | 91 +++++++------------ .../public/session/session_expired.ts | 30 +++--- ...thorized_response_http_interceptor.test.ts | 8 +- 5 files changed, 76 insertions(+), 82 deletions(-) diff --git a/x-pack/legacy/plugins/security/public/services/auto_logout.js b/x-pack/legacy/plugins/security/public/services/auto_logout.js index eb00d62d0266c..fa4d149d1f2e6 100644 --- a/x-pack/legacy/plugins/security/public/services/auto_logout.js +++ b/x-pack/legacy/plugins/security/public/services/auto_logout.js @@ -8,12 +8,26 @@ import { uiModules } from 'ui/modules'; import chrome from 'ui/chrome'; const module = uiModules.get('security'); + +const getNextParameter = () => { + const { location } = window; + const next = encodeURIComponent(`${location.pathname}${location.search}${location.hash}`); + return `&next=${next}`; +}; + +const getProviderParameter = tenant => { + const key = `${tenant}/session_provider`; + const providerName = sessionStorage.getItem(key); + return providerName ? `&provider=${encodeURIComponent(providerName)}` : ''; +}; + module.service('autoLogout', ($window, Promise) => { return () => { - const next = chrome.removeBasePath(`${window.location.pathname}${window.location.hash}`); - $window.location.href = chrome.addBasePath( - `/logout?next=${encodeURIComponent(next)}&msg=SESSION_EXPIRED` - ); + const logoutUrl = chrome.getInjected('logoutUrl'); + const tenant = `${chrome.getInjected('session.tenant', '')}`; + const next = getNextParameter(); + const provider = getProviderParameter(tenant); + $window.location.href = `${logoutUrl}?msg=SESSION_EXPIRED${next}${provider}`; return Promise.halt(); }; }); diff --git a/x-pack/plugins/security/public/plugin.tsx b/x-pack/plugins/security/public/plugin.tsx index f1ac2e2ecc3e2..467f86bd1ac69 100644 --- a/x-pack/plugins/security/public/plugin.tsx +++ b/x-pack/plugins/security/public/plugin.tsx @@ -57,13 +57,14 @@ export class SecurityPlugin { home, licensing, management }: PluginSetupDependencies ) { const { http, notifications, injectedMetadata } = core; - const { basePath, anonymousPaths } = http; + const { anonymousPaths } = http; anonymousPaths.register('/login'); anonymousPaths.register('/logout'); anonymousPaths.register('/logged_out'); - const tenant = `${injectedMetadata.getInjectedVar('session.tenant', '')}`; - const sessionExpired = new SessionExpired(basePath, tenant); + const tenant = injectedMetadata.getInjectedVar('session.tenant', '') as string; + const logoutUrl = injectedMetadata.getInjectedVar('logoutUrl') as string; + const sessionExpired = new SessionExpired(logoutUrl, tenant); http.intercept(new UnauthorizedResponseHttpInterceptor(sessionExpired, anonymousPaths)); this.sessionTimeout = new SessionTimeout(notifications, sessionExpired, http, tenant); http.intercept(new SessionTimeoutHttpInterceptor(this.sessionTimeout, anonymousPaths)); diff --git a/x-pack/plugins/security/public/session/session_expired.test.ts b/x-pack/plugins/security/public/session/session_expired.test.ts index 678c397dfbc64..f5ad9cb464e3e 100644 --- a/x-pack/plugins/security/public/session/session_expired.test.ts +++ b/x-pack/plugins/security/public/session/session_expired.test.ts @@ -4,11 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { coreMock } from 'src/core/public/mocks'; import { SessionExpired } from './session_expired'; -describe('Session Expiration', () => { +describe('#logout', () => { const mockGetItem = jest.fn().mockReturnValue(null); + const CURRENT_URL = '/foo/bar?baz=quz#quuz'; + const LOGOUT_URL = '/logout'; + const TENANT = '/some-basepath'; + + let newUrlPromise: Promise; beforeAll(() => { Object.defineProperty(window, 'sessionStorage', { @@ -19,69 +23,42 @@ describe('Session Expiration', () => { }); }); + beforeEach(() => { + window.history.pushState({}, '', CURRENT_URL); + mockGetItem.mockReset(); + newUrlPromise = new Promise(resolve => { + jest.spyOn(window.location, 'assign').mockImplementation(url => { + resolve(url); + }); + }); + }); + afterAll(() => { delete (window as any).sessionStorage; }); - describe('logout', () => { - const mockCurrentUrl = (url: string) => window.history.pushState({}, '', url); - const tenant = ''; - - it('redirects user to "/logout" when there is no basePath', async () => { - const { basePath } = coreMock.createSetup().http; - mockCurrentUrl('/foo/bar?baz=quz#quuz'); - const sessionExpired = new SessionExpired(basePath, tenant); - const newUrlPromise = new Promise(resolve => { - jest.spyOn(window.location, 'assign').mockImplementation(url => { - resolve(url); - }); - }); - - sessionExpired.logout(); + it(`redirects user to the logout URL with 'msg' and 'next' parameters`, async () => { + const sessionExpired = new SessionExpired(LOGOUT_URL, TENANT); + sessionExpired.logout(); - const url = await newUrlPromise; - expect(url).toBe( - `/logout?next=${encodeURIComponent('/foo/bar?baz=quz#quuz')}&msg=SESSION_EXPIRED` - ); - }); - - it('adds a provider parameter when an auth provider is saved in sessionStorage', async () => { - const { basePath } = coreMock.createSetup().http; - mockCurrentUrl('/foo/bar?baz=quz#quuz'); - const sessionExpired = new SessionExpired(basePath, tenant); - const newUrlPromise = new Promise(resolve => { - jest.spyOn(window.location, 'assign').mockImplementation(url => { - resolve(url); - }); - }); - mockGetItem.mockReturnValueOnce('basic'); - - sessionExpired.logout(); + const next = `&next=${encodeURIComponent(CURRENT_URL)}`; + await expect(newUrlPromise).resolves.toBe(`${LOGOUT_URL}?msg=SESSION_EXPIRED${next}`); + }); - const url = await newUrlPromise; - expect(url).toBe( - `/logout?next=${encodeURIComponent( - '/foo/bar?baz=quz#quuz' - )}&msg=SESSION_EXPIRED&provider=basic` - ); - }); + it(`adds 'provider' parameter when sessionStorage contains the provider name for this tenant`, async () => { + const providerName = 'basic'; + mockGetItem.mockReturnValueOnce(providerName); - it('redirects user to "/${basePath}/logout" and removes basePath from next parameter when there is a basePath', async () => { - const { basePath } = coreMock.createSetup({ basePath: '/foo' }).http; - mockCurrentUrl('/foo/bar?baz=quz#quuz'); - const sessionExpired = new SessionExpired(basePath, tenant); - const newUrlPromise = new Promise(resolve => { - jest.spyOn(window.location, 'assign').mockImplementation(url => { - resolve(url); - }); - }); + const sessionExpired = new SessionExpired(LOGOUT_URL, TENANT); + sessionExpired.logout(); - sessionExpired.logout(); + expect(mockGetItem).toHaveBeenCalledTimes(1); + expect(mockGetItem).toHaveBeenCalledWith(`${TENANT}/session_provider`); - const url = await newUrlPromise; - expect(url).toBe( - `/foo/logout?next=${encodeURIComponent('/bar?baz=quz#quuz')}&msg=SESSION_EXPIRED` - ); - }); + const next = `&next=${encodeURIComponent(CURRENT_URL)}`; + const provider = `&provider=${providerName}`; + await expect(newUrlPromise).resolves.toBe( + `${LOGOUT_URL}?msg=SESSION_EXPIRED${next}${provider}` + ); }); }); diff --git a/x-pack/plugins/security/public/session/session_expired.ts b/x-pack/plugins/security/public/session/session_expired.ts index a43da85526757..5866526b8851e 100644 --- a/x-pack/plugins/security/public/session/session_expired.ts +++ b/x-pack/plugins/security/public/session/session_expired.ts @@ -4,26 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'src/core/public'; - export interface ISessionExpired { logout(): void; } +const getNextParameter = () => { + const { location } = window; + const next = encodeURIComponent(`${location.pathname}${location.search}${location.hash}`); + return `&next=${next}`; +}; + +const getProviderParameter = (tenant: string) => { + const key = `${tenant}/session_provider`; + const providerName = sessionStorage.getItem(key); + return providerName ? `&provider=${encodeURIComponent(providerName)}` : ''; +}; + export class SessionExpired { - constructor(private basePath: HttpSetup['basePath'], private tenant: string) {} + constructor(private logoutUrl: string, private tenant: string) {} logout() { - const next = this.basePath.remove( - `${window.location.pathname}${window.location.search}${window.location.hash}` - ); - const key = `${this.tenant}/session_provider`; - const providerName = sessionStorage.getItem(key); - const provider = providerName ? `&provider=${encodeURIComponent(providerName)}` : ''; - window.location.assign( - this.basePath.prepend( - `/logout?next=${encodeURIComponent(next)}&msg=SESSION_EXPIRED${provider}` - ) - ); + const next = getNextParameter(); + const provider = getProviderParameter(this.tenant); + window.location.assign(`${this.logoutUrl}?msg=SESSION_EXPIRED${next}${provider}`); } } diff --git a/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.test.ts b/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.test.ts index ff2db01cb6c58..fba2a2ec98146 100644 --- a/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.test.ts +++ b/x-pack/plugins/security/public/session/unauthorized_response_http_interceptor.test.ts @@ -33,7 +33,7 @@ afterEach(() => { it(`logs out 401 responses`, async () => { const http = setupHttp('/foo'); - const sessionExpired = new SessionExpired(http.basePath, tenant); + const sessionExpired = new SessionExpired(`${http.basePath}/logout`, tenant); const logoutPromise = new Promise(resolve => { jest.spyOn(sessionExpired, 'logout').mockImplementation(() => resolve()); }); @@ -59,7 +59,7 @@ it(`ignores anonymous paths`, async () => { const http = setupHttp('/foo'); const { anonymousPaths } = http; anonymousPaths.register('/bar'); - const sessionExpired = new SessionExpired(http.basePath, tenant); + const sessionExpired = new SessionExpired(`${http.basePath}/logout`, tenant); const interceptor = new UnauthorizedResponseHttpInterceptor(sessionExpired, anonymousPaths); http.intercept(interceptor); fetchMock.mock('*', 401); @@ -70,7 +70,7 @@ it(`ignores anonymous paths`, async () => { it(`ignores errors which don't have a response, for example network connectivity issues`, async () => { const http = setupHttp('/foo'); - const sessionExpired = new SessionExpired(http.basePath, tenant); + const sessionExpired = new SessionExpired(`${http.basePath}/logout`, tenant); const interceptor = new UnauthorizedResponseHttpInterceptor(sessionExpired, http.anonymousPaths); http.intercept(interceptor); fetchMock.mock('*', new Promise((resolve, reject) => reject(new Error('Network is down')))); @@ -81,7 +81,7 @@ it(`ignores errors which don't have a response, for example network connectivity it(`ignores requests which omit credentials`, async () => { const http = setupHttp('/foo'); - const sessionExpired = new SessionExpired(http.basePath, tenant); + const sessionExpired = new SessionExpired(`${http.basePath}/logout`, tenant); const interceptor = new UnauthorizedResponseHttpInterceptor(sessionExpired, http.anonymousPaths); http.intercept(interceptor); fetchMock.mock('*', 401); From 67087e34d1406b00b2e0ca5ce286bb90dc8b65da Mon Sep 17 00:00:00 2001 From: Ben Skelker <54019610+benskelker@users.noreply.github.com> Date: Sun, 16 Feb 2020 10:39:58 +0200 Subject: [PATCH 16/65] fixes ui titles (#57535) Co-authored-by: Elastic Machine --- .../siem/public/components/alerts_viewer/translations.ts | 2 +- .../components/signals_histogram_panel/translations.ts | 2 +- .../plugins/siem/public/pages/overview/translations.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/translations.ts b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/translations.ts index b0bc38bd3ebdc..79466251fc57b 100644 --- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/translations.ts @@ -19,7 +19,7 @@ export const ALERTS_TABLE_TITLE = i18n.translate('xpack.siem.alertsView.alertsTa }); export const ALERTS_GRAPH_TITLE = i18n.translate('xpack.siem.alertsView.alertsGraphTitle', { - defaultMessage: 'External alerts count', + defaultMessage: 'External alert count', }); export const ALERTS_STACK_BY_MODULE = i18n.translate( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/translations.ts index 4cecf7376ca41..8c88fa4a5dae6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals_histogram_panel/translations.ts @@ -86,7 +86,7 @@ export const STACK_BY_USERS = i18n.translate( export const HISTOGRAM_HEADER = i18n.translate( 'xpack.siem.detectionEngine.signals.histogram.headerTitle', { - defaultMessage: 'Signals count', + defaultMessage: 'Signal count', } ); diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/translations.ts b/x-pack/legacy/plugins/siem/public/pages/overview/translations.ts index e20083bf51772..5ccd25984bc40 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/overview/translations.ts @@ -7,11 +7,11 @@ import { i18n } from '@kbn/i18n'; export const ALERTS_GRAPH_TITLE = i18n.translate('xpack.siem.overview.alertsGraphTitle', { - defaultMessage: 'External alerts count', + defaultMessage: 'External alert count', }); export const EVENTS = i18n.translate('xpack.siem.overview.eventsTitle', { - defaultMessage: 'Events count', + defaultMessage: 'Event count', }); export const NEWS_FEED_TITLE = i18n.translate('xpack.siem.overview.newsFeedSidebarTitle', { @@ -31,7 +31,7 @@ export const RECENT_TIMELINES = i18n.translate('xpack.siem.overview.recentTimeli }); export const SIGNAL_COUNT = i18n.translate('xpack.siem.overview.signalCountTitle', { - defaultMessage: 'Signals count', + defaultMessage: 'Signal count', }); export const VIEW_ALERTS = i18n.translate('xpack.siem.overview.viewAlertsButtonLabel', { From 335c7129924d25dc200d69c9b29c0b734634701b Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 16 Feb 2020 18:13:22 +0200 Subject: [PATCH 17/65] allow using any path to generate --- packages/kbn-plugin-generator/index.js | 4 +- .../kbn-plugin-generator/sao_template/sao.js | 81 +++++++++++++++---- 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/packages/kbn-plugin-generator/index.js b/packages/kbn-plugin-generator/index.js index 15adce7f01c8e..5417649db1a39 100644 --- a/packages/kbn-plugin-generator/index.js +++ b/packages/kbn-plugin-generator/index.js @@ -54,9 +54,8 @@ exports.run = function run(argv) { } const name = options._[0]; - const isKibanaPlugin = options.internal; const template = resolve(__dirname, './sao_template'); - const kibanaPlugins = resolve(__dirname, isKibanaPlugin ? '../../src/plugins' : '../../plugins'); + const kibanaPlugins = resolve(process.cwd(), 'plugins'); const targetPath = resolve(kibanaPlugins, snakeCase(name)); sao({ @@ -64,7 +63,6 @@ exports.run = function run(argv) { targetPath: targetPath, configOptions: { name, - isKibanaPlugin, targetPath, }, }).catch(error => { diff --git a/packages/kbn-plugin-generator/sao_template/sao.js b/packages/kbn-plugin-generator/sao_template/sao.js index aed4b9a02838f..f8d13b1229b7b 100755 --- a/packages/kbn-plugin-generator/sao_template/sao.js +++ b/packages/kbn-plugin-generator/sao_template/sao.js @@ -17,7 +17,8 @@ * under the License. */ -const { relative } = require('path'); +const { relative, resolve } = require('path'); +const fs = require('fs'); const startCase = require('lodash.startcase'); const camelCase = require('lodash.camelcase'); @@ -29,9 +30,54 @@ const pkg = require('../package.json'); const kibanaPkgPath = require.resolve('../../../package.json'); const kibanaPkg = require(kibanaPkgPath); // eslint-disable-line import/no-dynamic-require -module.exports = function({ name, targetPath, isKibanaPlugin }) { +async function gitInit(dir) { + // Only plugins in /plugins get git init + try { + await execa('git', ['init', dir]); + console.log(`Git repo initialized in ${dir}`); + } catch (error) { + console.error(error); + throw new Error(`Failure to git init ${dir}: ${error.all || error}`); + } +} + +async function moveToCustomFolder(from, to) { + try { + await execa('mv', [from, to]); + } catch (error) { + console.error(error); + throw new Error(`Failure to move plugin to ${to}: ${error.all || error}`); + } +} + +async function eslintPlugin(dir) { + try { + await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']); + } catch (error) { + console.error(error); + throw new Error(`Failure when running prettier on the generated output: ${error.all || error}`); + } +} + +module.exports = function({ name, targetPath }) { return { prompts: { + customPath: { + message: 'Would you like to create the plugin in a different folder?', + default: '/plugins', + filter(value) { + // Keep default value empty + if (value === '/plugins') return ''; + return value; + }, + validate(customPath) { + const p = resolve(process.cwd(), customPath); + const exists = fs.existsSync(p); + if (!exists) + return `Folder should exist relative to the kibana root folder. Consider /src/plugins or /x-pack/plugins.`; + return true; + }, + }, description: { message: 'Provide a short description', default: 'An awesome Kibana plugin', @@ -64,7 +110,9 @@ module.exports = function({ name, targetPath, isKibanaPlugin }) { generateEslint: { type: 'confirm', message: 'Would you like to use a custom eslint file?', - default: !isKibanaPlugin, + default({ customPath }) { + return !customPath; + }, }, }, filters: { @@ -86,31 +134,32 @@ module.exports = function({ name, targetPath, isKibanaPlugin }) { camelCase, snakeCase, name, - isKibanaPlugin, + // kibana plugins are placed in a the non default path + isKibanaPlugin: !answers.customPath, kbnVersion: answers.kbnVersion, upperCamelCaseName: name.charAt(0).toUpperCase() + camelCase(name).slice(1), hasUi: !!answers.generateApp, hasServer: !!answers.generateApi, hasScss: !!answers.generateScss, - relRoot: isKibanaPlugin ? '../../../..' : '../../..', + relRoot: relative(resolve(answers.customPath || targetPath, 'public'), process.cwd()), }, answers ), enforceNewFolder: true, installDependencies: false, - gitInit: !isKibanaPlugin, - async post({ log }) { - const dir = relative(process.cwd(), targetPath); + async post({ log, answers }) { + let dir = relative(process.cwd(), targetPath); + if (answers.customPath) { + // Move to custom path + moveToCustomFolder(targetPath, answers.customPath); + dir = relative(process.cwd(), resolve(answers.customPath, snakeCase(name))); + } else { + // Init git only in the default path + await gitInit(dir); + } // Apply eslint to the generated plugin - try { - await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']); - } catch (error) { - console.error(error); - throw new Error( - `Failure when running prettier on the generated output: ${error.all || error}` - ); - } + eslintPlugin(dir); log.success(chalk`🎉 From 36599fdfac18ddf6ecd475f0de461e4d5e87900f Mon Sep 17 00:00:00 2001 From: Liza K Date: Sun, 16 Feb 2020 18:19:37 +0200 Subject: [PATCH 18/65] revert --- packages/kbn-plugin-generator/index.js | 4 +- .../kbn-plugin-generator/sao_template/sao.js | 81 ++++--------------- 2 files changed, 19 insertions(+), 66 deletions(-) diff --git a/packages/kbn-plugin-generator/index.js b/packages/kbn-plugin-generator/index.js index 5417649db1a39..15adce7f01c8e 100644 --- a/packages/kbn-plugin-generator/index.js +++ b/packages/kbn-plugin-generator/index.js @@ -54,8 +54,9 @@ exports.run = function run(argv) { } const name = options._[0]; + const isKibanaPlugin = options.internal; const template = resolve(__dirname, './sao_template'); - const kibanaPlugins = resolve(process.cwd(), 'plugins'); + const kibanaPlugins = resolve(__dirname, isKibanaPlugin ? '../../src/plugins' : '../../plugins'); const targetPath = resolve(kibanaPlugins, snakeCase(name)); sao({ @@ -63,6 +64,7 @@ exports.run = function run(argv) { targetPath: targetPath, configOptions: { name, + isKibanaPlugin, targetPath, }, }).catch(error => { diff --git a/packages/kbn-plugin-generator/sao_template/sao.js b/packages/kbn-plugin-generator/sao_template/sao.js index f8d13b1229b7b..aed4b9a02838f 100755 --- a/packages/kbn-plugin-generator/sao_template/sao.js +++ b/packages/kbn-plugin-generator/sao_template/sao.js @@ -17,8 +17,7 @@ * under the License. */ -const { relative, resolve } = require('path'); -const fs = require('fs'); +const { relative } = require('path'); const startCase = require('lodash.startcase'); const camelCase = require('lodash.camelcase'); @@ -30,54 +29,9 @@ const pkg = require('../package.json'); const kibanaPkgPath = require.resolve('../../../package.json'); const kibanaPkg = require(kibanaPkgPath); // eslint-disable-line import/no-dynamic-require -async function gitInit(dir) { - // Only plugins in /plugins get git init - try { - await execa('git', ['init', dir]); - console.log(`Git repo initialized in ${dir}`); - } catch (error) { - console.error(error); - throw new Error(`Failure to git init ${dir}: ${error.all || error}`); - } -} - -async function moveToCustomFolder(from, to) { - try { - await execa('mv', [from, to]); - } catch (error) { - console.error(error); - throw new Error(`Failure to move plugin to ${to}: ${error.all || error}`); - } -} - -async function eslintPlugin(dir) { - try { - await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']); - } catch (error) { - console.error(error); - throw new Error(`Failure when running prettier on the generated output: ${error.all || error}`); - } -} - -module.exports = function({ name, targetPath }) { +module.exports = function({ name, targetPath, isKibanaPlugin }) { return { prompts: { - customPath: { - message: 'Would you like to create the plugin in a different folder?', - default: '/plugins', - filter(value) { - // Keep default value empty - if (value === '/plugins') return ''; - return value; - }, - validate(customPath) { - const p = resolve(process.cwd(), customPath); - const exists = fs.existsSync(p); - if (!exists) - return `Folder should exist relative to the kibana root folder. Consider /src/plugins or /x-pack/plugins.`; - return true; - }, - }, description: { message: 'Provide a short description', default: 'An awesome Kibana plugin', @@ -110,9 +64,7 @@ module.exports = function({ name, targetPath }) { generateEslint: { type: 'confirm', message: 'Would you like to use a custom eslint file?', - default({ customPath }) { - return !customPath; - }, + default: !isKibanaPlugin, }, }, filters: { @@ -134,32 +86,31 @@ module.exports = function({ name, targetPath }) { camelCase, snakeCase, name, - // kibana plugins are placed in a the non default path - isKibanaPlugin: !answers.customPath, + isKibanaPlugin, kbnVersion: answers.kbnVersion, upperCamelCaseName: name.charAt(0).toUpperCase() + camelCase(name).slice(1), hasUi: !!answers.generateApp, hasServer: !!answers.generateApi, hasScss: !!answers.generateScss, - relRoot: relative(resolve(answers.customPath || targetPath, 'public'), process.cwd()), + relRoot: isKibanaPlugin ? '../../../..' : '../../..', }, answers ), enforceNewFolder: true, installDependencies: false, - async post({ log, answers }) { - let dir = relative(process.cwd(), targetPath); - if (answers.customPath) { - // Move to custom path - moveToCustomFolder(targetPath, answers.customPath); - dir = relative(process.cwd(), resolve(answers.customPath, snakeCase(name))); - } else { - // Init git only in the default path - await gitInit(dir); - } + gitInit: !isKibanaPlugin, + async post({ log }) { + const dir = relative(process.cwd(), targetPath); // Apply eslint to the generated plugin - eslintPlugin(dir); + try { + await execa('yarn', ['lint:es', `./${dir}/**/*.ts*`, '--no-ignore', '--fix']); + } catch (error) { + console.error(error); + throw new Error( + `Failure when running prettier on the generated output: ${error.all || error}` + ); + } log.success(chalk`🎉 From f998174898eb68f8fe3996dc7186079c336a0e34 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Sun, 16 Feb 2020 17:55:52 -0500 Subject: [PATCH 19/65] [eventLog] get kibana.index name from config instead of hard-coding it (#57607) fixes https://github.com/elastic/kibana/issues/55629 --- x-pack/plugins/event_log/server/plugin.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index e32d4ff6f7acc..653e753446bb1 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Observable } from 'rxjs'; import { first } from 'rxjs/operators'; import { CoreSetup, @@ -12,6 +13,7 @@ import { Plugin as CorePlugin, PluginInitializerContext, ClusterClient, + SharedGlobalConfig, } from 'src/core/server'; import { IEventLogConfig, IEventLogService, IEventLogger, IEventLogConfig$ } from './types'; @@ -20,9 +22,6 @@ import { createEsContext, EsContext } from './es'; export type PluginClusterClient = Pick; -// TODO - figure out how to get ${kibana.index} for `.kibana` -const KIBANA_INDEX = '.kibana'; - const PROVIDER = 'event_log'; const ACTIONS = { starting: 'starting', @@ -35,13 +34,18 @@ export class Plugin implements CorePlugin { private eventLogService?: IEventLogService; private esContext?: EsContext; private eventLogger?: IEventLogger; + private globalConfig$: Observable; constructor(private readonly context: PluginInitializerContext) { this.systemLogger = this.context.logger.get(); this.config$ = this.context.config.create(); + this.globalConfig$ = this.context.config.legacy.globalConfig$; } async setup(core: CoreSetup): Promise { + const globalConfig = await this.globalConfig$.pipe(first()).toPromise(); + const kibanaIndex = globalConfig.kibana.index; + this.systemLogger.debug('setting up plugin'); const config = await this.config$.pipe(first()).toPromise(); @@ -49,7 +53,7 @@ export class Plugin implements CorePlugin { this.esContext = createEsContext({ logger: this.systemLogger, // TODO: get index prefix from config.get(kibana.index) - indexNameRoot: KIBANA_INDEX, + indexNameRoot: kibanaIndex, clusterClient: core.elasticsearch.adminClient, }); From 5d3797ee0ba75faa120e1093ffe5db24f2d69f19 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Sun, 16 Feb 2020 20:10:35 -0500 Subject: [PATCH 20/65] change id of x-pack event_log plugin to eventLog (#57612) change id of x-pack event_log plugin to eventLog --- x-pack/plugins/actions/kibana.json | 2 +- x-pack/plugins/actions/server/plugin.test.ts | 4 ++-- x-pack/plugins/actions/server/plugin.ts | 6 +++--- x-pack/plugins/event_log/README.md | 16 ++++++++-------- x-pack/plugins/event_log/kibana.json | 4 ++-- x-pack/plugins/event_log/server/plugin.ts | 7 ++++--- .../alerting_api_integration/common/config.ts | 2 +- .../common/config.ts | 2 +- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/actions/kibana.json b/x-pack/plugins/actions/kibana.json index ff7f22af6261d..0c40dec46a6ee 100644 --- a/x-pack/plugins/actions/kibana.json +++ b/x-pack/plugins/actions/kibana.json @@ -4,7 +4,7 @@ "version": "8.0.0", "kibanaVersion": "kibana", "configPath": ["xpack", "actions"], - "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "event_log"], + "requiredPlugins": ["licensing", "taskManager", "encryptedSavedObjects", "eventLog"], "optionalPlugins": ["spaces"], "ui": false } diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index ba6fbcd32daee..91944dfa8f3ad 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -27,7 +27,7 @@ describe('Actions Plugin', () => { taskManager: taskManagerMock.createSetup(), encryptedSavedObjects: encryptedSavedObjectsMock.createSetup(), licensing: licensingMock.createSetup(), - event_log: eventLogMock.createSetup(), + eventLog: eventLogMock.createSetup(), }; }); @@ -107,7 +107,7 @@ describe('Actions Plugin', () => { taskManager: taskManagerMock.createSetup(), encryptedSavedObjects: encryptedSavedObjectsMock.createSetup(), licensing: licensingMock.createSetup(), - event_log: eventLogMock.createSetup(), + eventLog: eventLogMock.createSetup(), }; pluginsStart = { taskManager: taskManagerMock.createStart(), diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index 1714666882b05..b0555921f395f 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -70,7 +70,7 @@ export interface ActionsPluginsSetup { encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; licensing: LicensingPluginSetup; spaces?: SpacesPluginSetup; - event_log: IEventLogService; + eventLog: IEventLogService; } export interface ActionsPluginsStart { encryptedSavedObjects: EncryptedSavedObjectsPluginStart; @@ -132,8 +132,8 @@ export class ActionsPlugin implements Plugin, Plugi attributesToEncrypt: new Set(['apiKey']), }); - plugins.event_log.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS)); - this.eventLogger = plugins.event_log.getLogger({ + plugins.eventLog.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS)); + this.eventLogger = plugins.eventLog.getLogger({ event: { provider: EVENT_LOG_PROVIDER }, }); diff --git a/x-pack/plugins/event_log/README.md b/x-pack/plugins/event_log/README.md index d2d67ffc27d94..027bbc694801f 100644 --- a/x-pack/plugins/event_log/README.md +++ b/x-pack/plugins/event_log/README.md @@ -9,14 +9,14 @@ and actions. ## Basic Usage - Logging Events -Follow these steps to use `event_log` in your plugin: +Follow these steps to use `eventLog` in your plugin: -1. Declare `event_log` as a dependency in `kibana.json`: +1. Declare `eventLog` as a dependency in `kibana.json`: ```json { ... - "requiredPlugins": ["event_log"], + "requiredPlugins": ["eventLog"], ... } ``` @@ -28,13 +28,13 @@ API provided in the `setup` stage: ... import { IEventLogger, IEventLogService } from '../../event_log/server'; interface PluginSetupDependencies { - event_log: IEventLogService; + eventLog: IEventLogService; } ... -public setup(core: CoreSetup, { event_log }: PluginSetupDependencies) { +public setup(core: CoreSetup, { eventLog }: PluginSetupDependencies) { ... - event_log.registerProviderActions('my-plugin', ['action-1, action-2']); - const eventLogger: IEventLogger = event_log.getLogger({ event: { provider: 'my-plugin' } }); + eventLog.registerProviderActions('my-plugin', ['action-1, action-2']); + const eventLogger: IEventLogger = eventLog.getLogger({ event: { provider: 'my-plugin' } }); ... } ... @@ -73,7 +73,7 @@ a new elasticsearch index referred to as the "event log". Example events are actions firing, alerts running their scheduled functions, alerts scheduling actions to run, etc. -This functionality will be provided in a new NP plugin `event_log`, and will +This functionality will be provided in a new NP plugin `eventLog`, and will provide server-side plugin APIs to write to the event log, and run limited queries against it. For now, access via HTTP will not be available, due to security concerns and lack of use cases. diff --git a/x-pack/plugins/event_log/kibana.json b/x-pack/plugins/event_log/kibana.json index 52b68deeffce5..7231d967b4c8d 100644 --- a/x-pack/plugins/event_log/kibana.json +++ b/x-pack/plugins/event_log/kibana.json @@ -1,8 +1,8 @@ { - "id": "event_log", + "id": "eventLog", "version": "0.0.1", "kibanaVersion": "kibana", - "configPath": ["xpack", "event_log"], + "configPath": ["xpack", "eventLog"], "server": true, "ui": false } diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index 653e753446bb1..fdb08b2d090a6 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -22,7 +22,8 @@ import { createEsContext, EsContext } from './es'; export type PluginClusterClient = Pick; -const PROVIDER = 'event_log'; +const PROVIDER = 'eventLog'; + const ACTIONS = { starting: 'starting', stopping: 'stopping', @@ -88,7 +89,7 @@ export class Plugin implements CorePlugin { // will log the event after initialization this.eventLogger.logEvent({ event: { action: ACTIONS.starting }, - message: 'event_log starting', + message: 'eventLog starting', }); } @@ -101,7 +102,7 @@ export class Plugin implements CorePlugin { // when Kibana is actuaelly stopping, as it's written asynchronously this.eventLogger.logEvent({ event: { action: ACTIONS.stopping }, - message: 'event_log stopping', + message: 'eventLog stopping', }); } } diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index d23bd2ac4646d..eb03aafc03d08 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -75,7 +75,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) ])}`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, '--xpack.alerting.enabled=true', - '--xpack.event_log.logEntries=true', + '--xpack.eventLog.logEntries=true', ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, diff --git a/x-pack/test/detection_engine_api_integration/common/config.ts b/x-pack/test/detection_engine_api_integration/common/config.ts index bf8e6982b545d..d2bfeeb6433d3 100644 --- a/x-pack/test/detection_engine_api_integration/common/config.ts +++ b/x-pack/test/detection_engine_api_integration/common/config.ts @@ -74,7 +74,7 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) ])}`, `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, '--xpack.alerting.enabled=true', - '--xpack.event_log.logEntries=true', + '--xpack.eventLog.logEntries=true', ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, From 8e17fdabd4954ee49ab6d2e327351cb20abc6e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Mon, 17 Feb 2020 14:22:07 +0530 Subject: [PATCH 21/65] [Index management] Client-side NP ready (#57295) --- src/plugins/management/public/index.ts | 1 + src/plugins/management/public/types.ts | 2 +- .../components/detail_panel/detail_panel.js | 2 +- .../components/detail_panel/detail_panel.js | 2 +- .../public/extend_index_management/index.js | 4 +- .../__jest__/extend_index_management.test.js | 5 + .../components/policy_table/policy_table.js | 2 +- .../np_ready/extend_index_management/index.js | 28 +- .../helpers/home.helpers.ts | 9 +- .../helpers/setup_environment.ts | 39 --- .../helpers/setup_environment.tsx | 56 ++++ .../helpers/template_clone.helpers.ts | 5 +- .../helpers/template_create.helpers.ts | 8 +- .../helpers/template_edit.helpers.ts | 5 +- .../__jest__/components/index_table.test.js | 49 ++- .../__jest__/lib/flatten_object.test.js | 2 +- .../index_management/public/app/index.tsx | 36 -- .../index_list/detail_panel/detail_panel.js | 164 --------- .../public/app/services/ui_metric.ts | 25 -- .../public/app/store/reducers/detail_panel.js | 89 ----- .../public/{app => application}/app.tsx | 4 +- .../public/application/app_context.tsx | 52 +++ .../{app => application}/components/index.ts | 0 .../client_integration/helpers/index.ts | 0 .../helpers/mappings_editor.helpers.ts | 0 .../mappings_editor.test.tsx | 0 .../components/mappings_editor/_index.scss | 0 .../mappings_editor/components/_index.scss | 0 .../mappings_editor/components/code_block.tsx | 0 .../configuration_form/configuration_form.tsx | 0 .../configuration_form_schema.tsx | 0 .../dynamic_mapping_section.tsx | 0 .../dynamic_mapping_section/index.ts | 0 .../components/configuration_form/index.ts | 0 .../meta_field_section/index.ts | 0 .../meta_field_section/meta_field_section.tsx | 0 .../configuration_form/routing_section.tsx | 0 .../source_field_section/index.ts | 0 .../source_field_section.tsx | 0 .../components/document_fields/_index.scss | 0 .../document_fields/document_fields.tsx | 0 .../document_fields_header.tsx | 0 .../editor_toggle_controls.tsx | 0 .../field_parameters/analyzer_parameter.tsx | 0 .../analyzer_parameter_selects.tsx | 0 .../field_parameters/analyzers_parameter.tsx | 0 .../field_parameters/boost_parameter.tsx | 0 .../coerce_number_parameter.tsx | 0 .../coerce_shape_parameter.tsx | 0 .../field_parameters/copy_to_parameter.tsx | 0 .../field_parameters/doc_values_parameter.tsx | 0 .../field_parameters/dynamic_parameter.tsx | 0 .../eager_global_ordinals_parameter.tsx | 0 .../field_parameters/enabled_parameter.tsx | 0 .../fielddata_frequency_filter_absolute.tsx | 0 .../fielddata_frequency_filter_percentage.tsx | 0 .../field_parameters/fielddata_parameter.tsx | 0 .../field_parameters/format_parameter.tsx | 0 .../field_parameters/ignore_malformed.tsx | 0 .../ignore_z_value_parameter.tsx | 0 .../document_fields/field_parameters/index.ts | 0 .../field_parameters/index_parameter.tsx | 0 .../field_parameters/locale_parameter.tsx | 0 .../max_shingle_size_parameter.tsx | 0 .../field_parameters/name_parameter.tsx | 0 .../field_parameters/norms_parameter.tsx | 0 .../field_parameters/null_value_parameter.tsx | 0 .../orientation_parameter.tsx | 0 .../field_parameters/path_parameter.tsx | 0 .../field_parameters/relations_parameter.tsx | 0 .../field_parameters/similarity_parameter.tsx | 0 .../split_queries_on_whitespace_parameter.tsx | 0 .../field_parameters/store_parameter.tsx | 0 .../term_vector_parameter.tsx | 0 .../field_parameters/type_parameter.tsx | 0 .../fields/_field_list_item.scss | 0 .../document_fields/fields/_index.scss | 0 .../fields/create_field/create_field.tsx | 0 .../fields/create_field/index.ts | 0 .../required_parameters_forms/alias_type.tsx | 0 .../dense_vector_type.tsx | 0 .../required_parameters_forms/index.ts | 0 .../scaled_float_type.tsx | 0 .../token_count_type.tsx | 0 .../fields/delete_field_provider.tsx | 0 .../edit_field/_edit_field_form_row.scss | 0 .../fields/edit_field/_index.scss | 0 .../advanced_parameters_section.tsx | 0 .../edit_field/basic_parameters_section.tsx | 0 .../fields/edit_field/edit_field.tsx | 0 .../edit_field/edit_field_container.tsx | 0 .../fields/edit_field/edit_field_form_row.tsx | 0 .../edit_field/edit_field_header_form.tsx | 0 .../edit_field/field_description_section.tsx | 0 .../fields/edit_field/index.ts | 0 .../edit_field/update_field_provider.tsx | 0 .../fields/field_types/alias_type.tsx | 0 .../fields/field_types/binary_type.tsx | 0 .../fields/field_types/boolean_type.tsx | 0 .../fields/field_types/completion_type.tsx | 0 .../fields/field_types/date_type.tsx | 0 .../fields/field_types/dense_vector_type.tsx | 0 .../fields/field_types/flattened_type.tsx | 0 .../fields/field_types/geo_point_type.tsx | 0 .../fields/field_types/geo_shape_type.tsx | 0 .../fields/field_types/index.ts | 0 .../fields/field_types/ip_type.tsx | 0 .../fields/field_types/join_type.tsx | 0 .../fields/field_types/keyword_type.tsx | 0 .../fields/field_types/nested_type.tsx | 0 .../fields/field_types/numeric_type.tsx | 0 .../fields/field_types/object_type.tsx | 0 .../fields/field_types/range_type.tsx | 0 .../fields/field_types/search_as_you_type.tsx | 0 .../fields/field_types/shape_type.tsx | 0 .../fields/field_types/text_type.tsx | 0 .../fields/field_types/token_count_type.tsx | 0 .../document_fields/fields/fields_list.tsx | 0 .../fields/fields_list_item.tsx | 0 .../fields/fields_list_item_container.tsx | 0 .../document_fields/fields/index.ts | 0 .../modal_confirmation_delete_fields.tsx | 0 .../document_fields/fields_json_editor.tsx | 0 .../document_fields/fields_tree_editor.tsx | 0 .../components/document_fields/index.ts | 0 .../document_fields/search_fields/index.ts | 0 .../search_fields/search_result.tsx | 0 .../search_fields/search_result_item.tsx | 0 .../components/fields_tree.tsx | 0 .../mappings_editor/components/index.ts | 0 .../components/load_mappings/index.ts | 0 .../load_mappings/load_from_json_button.tsx | 0 .../load_mappings_provider.test.tsx | 0 .../load_mappings/load_mappings_provider.tsx | 0 .../components/multiple_mappings_warning.tsx | 0 .../components/templates_form/index.ts | 0 .../templates_form/templates_form.tsx | 0 .../templates_form/templates_form_schema.ts | 0 .../mappings_editor/components/tree/index.ts | 0 .../mappings_editor/components/tree/tree.tsx | 0 .../components/tree/tree_item.tsx | 0 .../constants/data_types_definition.tsx | 0 .../constants/default_values.ts | 0 .../constants/field_options.tsx | 0 .../constants/field_options_i18n.ts | 0 .../mappings_editor/constants/index.ts | 0 .../constants/mappings_editor.ts | 0 .../constants/parameters_definition.tsx | 0 .../components/mappings_editor/index.ts | 0 .../index_settings_context.tsx | 0 .../mappings_editor/lib/error_reporter.ts | 0 .../lib/extract_mappings_definition.test.ts | 0 .../lib/extract_mappings_definition.ts | 0 .../components/mappings_editor/lib/index.ts | 0 .../lib/mappings_validator.test.ts | 0 .../mappings_editor/lib/mappings_validator.ts | 0 .../mappings_editor/lib/search_fields.test.ts | 0 .../mappings_editor/lib/search_fields.tsx | 0 .../mappings_editor/lib/serializers.ts | 0 .../mappings_editor/lib/utils.test.ts | 0 .../components/mappings_editor/lib/utils.ts | 0 .../mappings_editor/lib/validators.ts | 0 .../mappings_editor/mappings_editor.tsx | 0 .../mappings_editor/mappings_state.tsx | 0 .../components/mappings_editor/reducer.ts | 0 .../mappings_editor/shared_imports.ts | 0 .../components/mappings_editor/types.ts | 0 .../components/no_match/index.ts | 0 .../components/no_match/no_match.tsx | 0 .../components/page_error/index.ts | 0 .../page_error/page_error_forbidden.tsx | 0 .../components/section_error.tsx | 0 .../components/section_loading.tsx | 0 .../components/template_delete_modal.tsx | 0 .../components/template_form/index.ts | 0 .../components/template_form/steps/index.ts | 0 .../template_form/steps/step_aliases.tsx | 0 .../template_form/steps/step_logistics.tsx | 0 .../template_form/steps/step_mappings.tsx | 0 .../template_form/steps/step_review.tsx | 0 .../template_form/steps/step_settings.tsx | 0 .../template_form/steps/use_json_step.ts | 0 .../template_form/template_form.tsx | 0 .../template_form/template_form_schemas.tsx | 0 .../template_form/template_steps.tsx | 0 .../components/template_form/types.ts | 0 .../constants/detail_panel_tabs.ts | 0 .../{app => application}/constants/index.ts | 0 .../constants/refresh_intervals.ts | 0 .../public/application/index.tsx | 45 +++ .../public/{app => application}/lib/ace.js | 0 .../{app => application}/lib/edit_settings.js | 0 .../lib/flatten_object.js | 0 .../lib/flatten_panel_tree.js | 0 .../lib/index_status_labels.js | 0 .../lib/manage_angular_lifecycle.ts | 0 .../{app => application}/lib/render_badges.js | 6 +- .../sections/home/home.tsx | 0 .../sections/home/index.ts | 0 .../detail_panel/detail_panel.container.js | 0 .../index_list/detail_panel/detail_panel.js | 167 ++++++++++ .../edit_settings_json.container.js | 0 .../edit_settings_json/edit_settings_json.js | 0 .../detail_panel/edit_settings_json/index.js | 0 .../home/index_list/detail_panel/index.js | 0 .../detail_panel/show_json/index.js | 0 .../show_json/show_json.container.js | 0 .../detail_panel/show_json/show_json.js | 0 .../index_list/detail_panel/summary/index.js | 0 .../detail_panel/summary/summary.container.js | 0 .../detail_panel/summary/summary.js | 58 ++-- .../sections/home/index_list/index.ts | 0 .../index_actions_context_menu/index.js | 0 .../index_actions_context_menu.container.js | 0 .../index_actions_context_menu.js | 126 +++---- .../sections/home/index_list/index_list.d.ts | 0 .../sections/home/index_list/index_list.js | 0 .../home/index_list/index_table/index.js | 0 .../index_table/index_table.container.js | 0 .../index_list/index_table/index_table.js | 314 +++++++++--------- .../sections/home/template_list/index.ts | 0 .../template_list/template_details/index.ts | 0 .../template_details/tabs/index.ts | 0 .../template_details/tabs/tab_aliases.tsx | 0 .../template_details/tabs/tab_mappings.tsx | 0 .../template_details/tabs/tab_settings.tsx | 0 .../template_details/tabs/tab_summary.tsx | 0 .../template_details/template_details.tsx | 3 +- .../home/template_list/template_list.tsx | 3 +- .../template_list/template_table/index.ts | 0 .../template_table/template_table.tsx | 3 +- .../sections/template_clone/index.ts | 0 .../template_clone/template_clone.tsx | 0 .../sections/template_create/index.ts | 0 .../template_create/template_create.tsx | 0 .../sections/template_edit/index.ts | 0 .../sections/template_edit/template_edit.tsx | 0 .../{app => application}/services/api.ts | 11 +- .../services/breadcrumbs.ts | 21 +- .../services/documentation.ts | 2 +- .../services/health_to_color.ts | 0 .../{app => application}/services/http.ts | 4 +- .../{app => application}/services/index.ts | 4 + .../services/navigation.ts | 0 .../services/notification.ts | 4 +- .../{app => application}/services/routing.ts | 0 .../services/sort_table.ts | 0 .../public/application/services/ui_metric.ts | 49 +++ .../services/use_request.ts | 0 .../store/actions/clear_cache_indices.js | 0 .../store/actions/clear_row_status.js | 0 .../store/actions/close_indices.js | 0 .../store/actions/delete_indices.js | 0 .../store/actions/detail_panel.js | 0 .../store/actions/edit_index_settings.js | 0 .../store/actions/extension_action.js | 0 .../store/actions/flush_indices.js | 0 .../store/actions/forcemerge_indices.js | 0 .../store/actions/freeze_indices.js | 0 .../store/actions/index.js | 0 .../store/actions/load_index_data.js | 0 .../store/actions/load_indices.js | 0 .../store/actions/open_indices.js | 0 .../store/actions/refresh_indices.js | 0 .../store/actions/reload_indices.js | 0 .../store/actions/table_state.js | 0 .../store/actions/unfreeze_indices.js | 0 .../store/actions/update_index_settings.js | 0 .../{app => application}/store/index.ts | 0 .../store/reducers/detail_panel.js | 89 +++++ .../store/reducers/index.js} | 2 +- .../store/reducers/index_management.js | 15 +- .../store/reducers/indices.js | 0 .../store/reducers/row_status.js | 0 .../store/reducers/table_state.js | 0 .../application/store/selectors/index.d.ts | 8 + .../store/selectors/index.js | 12 +- .../public/application/store/store.d.ts | 9 + .../{app => application}/store/store.js | 9 +- .../index_management/public/index.scss | 2 +- .../plugins/index_management/public/index.ts | 44 +-- .../public/index_management_extensions.ts | 72 ---- .../plugins/index_management/public/legacy.ts | 31 -- .../plugins/index_management/public/mocks.ts | 21 ++ .../plugins/index_management/public/plugin.ts | 112 +++++-- .../public/register_management_section.ts | 19 -- .../public/register_routes.ts | 34 -- .../services/extensions_service.mock.ts | 31 ++ .../public/services/extensions_service.ts | 97 ++++++ .../index_management/public/services/index.ts | 7 + .../{app/store/reducers/index.js => types.ts} | 2 +- x-pack/legacy/plugins/rollup/public/legacy.ts | 5 +- x-pack/legacy/plugins/rollup/public/plugin.ts | 11 +- 293 files changed, 1138 insertions(+), 903 deletions(-) delete mode 100644 x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.ts create mode 100644 x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx delete mode 100644 x-pack/legacy/plugins/index_management/public/app/index.tsx delete mode 100644 x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/detail_panel.js delete mode 100644 x-pack/legacy/plugins/index_management/public/app/services/ui_metric.ts delete mode 100644 x-pack/legacy/plugins/index_management/public/app/store/reducers/detail_panel.js rename x-pack/legacy/plugins/index_management/public/{app => application}/app.tsx (93%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/app_context.tsx rename x-pack/legacy/plugins/index_management/public/{app => application}/components/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/__jest__/client_integration/helpers/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/_index.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/_index.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/code_block.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/configuration_form.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/meta_field_section/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/routing_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/source_field_section/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/_index.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/document_fields.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/document_fields_header.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/_field_list_item.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/_index.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/fields_list.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields_json_editor.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/fields_tree_editor.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/search_fields/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/search_fields/search_result.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/fields_tree.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/load_mappings/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/load_mappings/load_from_json_button.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/multiple_mappings_warning.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/templates_form/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/templates_form/templates_form.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/templates_form/templates_form_schema.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/tree/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/tree/tree.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/components/tree/tree_item.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/data_types_definition.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/default_values.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/field_options.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/field_options_i18n.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/mappings_editor.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/constants/parameters_definition.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/index_settings_context.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/error_reporter.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/extract_mappings_definition.test.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/extract_mappings_definition.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/mappings_validator.test.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/mappings_validator.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/search_fields.test.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/search_fields.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/serializers.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/utils.test.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/utils.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/lib/validators.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/mappings_editor.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/mappings_state.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/reducer.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/shared_imports.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/mappings_editor/types.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/no_match/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/no_match/no_match.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/page_error/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/page_error/page_error_forbidden.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/section_error.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/section_loading.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_delete_modal.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/step_aliases.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/step_logistics.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/step_mappings.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/step_review.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/step_settings.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/steps/use_json_step.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/template_form.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/template_form_schemas.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/template_steps.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/components/template_form/types.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/constants/detail_panel_tabs.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/constants/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/constants/refresh_intervals.ts (100%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/index.tsx rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/ace.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/edit_settings.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/flatten_object.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/flatten_panel_tree.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/index_status_labels.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/manage_angular_lifecycle.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/lib/render_badges.js (83%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/home.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/detail_panel.container.js (100%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/edit_settings_json/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/show_json/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/show_json/show_json.container.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/show_json/show_json.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/summary/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/summary/summary.container.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/detail_panel/summary/summary.js (72%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_actions_context_menu/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js (91%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_list.d.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_list.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_table/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_table/index_table.container.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/index_list/index_table/index_table.js (62%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/tabs/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/tabs/tab_aliases.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/tabs/tab_mappings.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/tabs/tab_settings.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/tabs/tab_summary.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_details/template_details.tsx (99%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_list.tsx (98%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_table/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/home/template_list/template_table/template_table.tsx (98%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/template_clone/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/template_clone/template_clone.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/template_create/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/template_create/template_create.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/template_edit/index.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/sections/template_edit/template_edit.tsx (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/api.ts (94%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/breadcrumbs.ts (81%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/documentation.ts (99%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/health_to_color.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/http.ts (86%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/index.ts (82%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/navigation.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/notification.ts (91%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/routing.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/services/sort_table.ts (100%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts rename x-pack/legacy/plugins/index_management/public/{app => application}/services/use_request.ts (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/clear_cache_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/clear_row_status.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/close_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/delete_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/detail_panel.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/edit_index_settings.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/extension_action.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/flush_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/forcemerge_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/freeze_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/index.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/load_index_data.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/load_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/open_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/refresh_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/reload_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/table_state.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/unfreeze_indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/actions/update_index_settings.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/index.ts (100%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js rename x-pack/legacy/plugins/index_management/public/{app/store/store.d.ts => application/store/reducers/index.js} (82%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/reducers/index_management.js (63%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/reducers/indices.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/reducers/row_status.js (100%) rename x-pack/legacy/plugins/index_management/public/{app => application}/store/reducers/table_state.js (100%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts rename x-pack/legacy/plugins/index_management/public/{app => application}/store/selectors/index.js (93%) create mode 100644 x-pack/legacy/plugins/index_management/public/application/store/store.d.ts rename x-pack/legacy/plugins/index_management/public/{app => application}/store/store.js (71%) delete mode 100644 x-pack/legacy/plugins/index_management/public/index_management_extensions.ts delete mode 100644 x-pack/legacy/plugins/index_management/public/legacy.ts create mode 100644 x-pack/legacy/plugins/index_management/public/mocks.ts delete mode 100644 x-pack/legacy/plugins/index_management/public/register_management_section.ts delete mode 100644 x-pack/legacy/plugins/index_management/public/register_routes.ts create mode 100644 x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts create mode 100644 x-pack/legacy/plugins/index_management/public/services/extensions_service.ts create mode 100644 x-pack/legacy/plugins/index_management/public/services/index.ts rename x-pack/legacy/plugins/index_management/public/{app/store/reducers/index.js => types.ts} (78%) diff --git a/src/plugins/management/public/index.ts b/src/plugins/management/public/index.ts index 4ece75bbf36da..9e59834303546 100644 --- a/src/plugins/management/public/index.ts +++ b/src/plugins/management/public/index.ts @@ -29,6 +29,7 @@ export { ManagementStart, RegisterManagementApp, RegisterManagementAppArgs, + ManagementAppMountParams, } from './types'; export { ManagementApp } from './management_app'; export { ManagementSection } from './management_section'; diff --git a/src/plugins/management/public/types.ts b/src/plugins/management/public/types.ts index 4dbea30ff062d..a8bdd5cca24a3 100644 --- a/src/plugins/management/public/types.ts +++ b/src/plugins/management/public/types.ts @@ -64,7 +64,7 @@ export type RegisterManagementApp = (managementApp: RegisterManagementAppArgs) = export type Unmount = () => Promise | void; -interface ManagementAppMountParams { +export interface ManagementAppMountParams { basePath: string; // base path for setting up your router element: HTMLElement; // element the section should render into setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void; diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js index cd107be408727..16ed0a7d695ad 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/auto_follow_pattern_list/components/detail_panel/detail_panel.js @@ -7,7 +7,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getIndexListUri } from '../../../../../../../../index_management/public/app/services/navigation'; +import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; import { EuiButtonEmpty, diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js index d4e43a1bd1840..e3bda2e67097d 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/detail_panel/detail_panel.js @@ -7,7 +7,7 @@ import React, { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getIndexListUri } from '../../../../../../../../index_management/public/app/services/navigation'; +import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; import { EuiButton, diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js b/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js index 549c71748e1be..809a7c3e87b75 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/extend_index_management/index.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { i18n } from '@kbn/i18n'; -import { addBadgeExtension } from '../../../index_management/public/index_management_extensions'; +import { extensionsService } from '../../../index_management/public'; import { get } from 'lodash'; const propertyPath = 'isFollowerIndex'; @@ -19,4 +19,4 @@ export const followerBadgeExtension = { filterExpression: 'isFollowerIndex:true', }; -addBadgeExtension(followerBadgeExtension); +extensionsService.addBadge(followerBadgeExtension); diff --git a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js index cfae072b57a26..bcbae7e093f46 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/__jest__/extend_index_management.test.js @@ -7,6 +7,7 @@ import moment from 'moment-timezone'; import axios from 'axios'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; + import { mountWithIntl } from '../../../../test_utils/enzyme_helpers'; import { retryLifecycleActionExtension, @@ -26,6 +27,10 @@ initHttp(axios.create({ adapter: axiosXhrAdapter }), path => path); initUiMetric(() => () => {}); jest.mock('ui/new_platform'); +jest.mock('../../index_management/public', async () => { + const { indexManagementMock } = await import('../../index_management/public/mocks.ts'); + return indexManagementMock.createSetup(); +}); const indexWithoutLifecyclePolicy = { health: 'yellow', diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js index eba7f75aa9baf..83d32eae1097d 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/application/sections/policy_table/components/policy_table/policy_table.js @@ -37,7 +37,7 @@ import { import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; -import { getIndexListUri } from '../../../../../../../../index_management/public/app/services/navigation'; +import { getIndexListUri } from '../../../../../../../../index_management/public/application/services/navigation'; import { BASE_PATH } from '../../../../../../../common/constants'; import { UIM_EDIT_CLICK } from '../../../../constants'; import { getPolicyPath } from '../../../../services/navigation'; diff --git a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js index 422a7986aa057..6958c4ecce0cc 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js +++ b/x-pack/legacy/plugins/index_lifecycle_management/public/np_ready/extend_index_management/index.js @@ -9,13 +9,7 @@ import { get, every, any } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiSearchBar } from '@elastic/eui'; -import { - addSummaryExtension, - addBannerExtension, - addActionExtension, - addFilterExtension, -} from '../../../../index_management/public/index_management_extensions'; - +import { extensionsService } from '../../../../index_management/public'; import { init as initUiMetric } from '../application/services/ui_metric'; import { init as initNotification } from '../application/services/notification'; import { retryLifecycleForIndex } from '../application/services/api'; @@ -27,13 +21,17 @@ const stepPath = 'ilm.step'; export const retryLifecycleActionExtension = ({ indices, - createUiStatsReporter, + usageCollection, toasts, fatalErrors, }) => { // These are hacks that we can remove once the New Platform migration is done. They're needed here // because API requests and API errors require them. - initUiMetric(createUiStatsReporter); + const getLegacyReporter = appName => (type, name) => { + usageCollection.reportUiStats(appName, type, name); + }; + + initUiMetric(getLegacyReporter); initNotification(toasts, fatalErrors); const allHaveErrors = every(indices, index => { @@ -207,11 +205,11 @@ export const ilmFilterExtension = indices => { }; export const addAllExtensions = () => { - addActionExtension(retryLifecycleActionExtension); - addActionExtension(removeLifecyclePolicyActionExtension); - addActionExtension(addLifecyclePolicyActionExtension); + extensionsService.addAction(retryLifecycleActionExtension); + extensionsService.addAction(removeLifecyclePolicyActionExtension); + extensionsService.addAction(addLifecyclePolicyActionExtension); - addBannerExtension(ilmBannerExtension); - addSummaryExtension(ilmSummaryExtension); - addFilterExtension(ilmFilterExtension); + extensionsService.addBanner(ilmBannerExtension); + extensionsService.addSummary(ilmSummaryExtension); + extensionsService.addFilter(ilmFilterExtension); }; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts index 4a4896347333c..e5f0b25d89c3e 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts +++ b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/home.helpers.ts @@ -13,13 +13,14 @@ import { findTestSubject, nextTick, } from '../../../../../../test_utils'; -import { IndexManagementHome } from '../../../public/app/sections/home'; +import { IndexManagementHome } from '../../../public/application/sections/home'; import { BASE_PATH } from '../../../common/constants'; -import { indexManagementStore } from '../../../public/app/store'; +import { indexManagementStore } from '../../../public/application/store'; import { Template } from '../../../common/types'; +import { WithAppDependencies, services } from './setup_environment'; const testBedConfig: TestBedConfig = { - store: indexManagementStore, + store: () => indexManagementStore(services as any), memoryRouter: { initialEntries: [`${BASE_PATH}indices`], componentRoutePath: `${BASE_PATH}:section(indices|templates)`, @@ -27,7 +28,7 @@ const testBedConfig: TestBedConfig = { doMountAsync: true, }; -const initTestBed = registerTestBed(IndexManagementHome, testBedConfig); +const initTestBed = registerTestBed(WithAppDependencies(IndexManagementHome), testBedConfig); export interface IdxMgmtHomeTestBed extends TestBed { findAction: (action: 'edit' | 'clone' | 'delete') => ReactWrapper; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.ts b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.ts deleted file mode 100644 index d5ac185a63ccd..0000000000000 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.ts +++ /dev/null @@ -1,39 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios from 'axios'; -import axiosXhrAdapter from 'axios/lib/adapters/xhr'; -import { init as initHttpRequests } from './http_requests'; -import { httpService } from '../../../public/app/services/http'; -import { breadcrumbService } from '../../../public/app/services/breadcrumbs'; -import { documentationService } from '../../../public/app/services/documentation'; -import { notificationService } from '../../../public/app/services/notification'; -import { uiMetricService } from '../../../public/app/services/ui_metric'; -import { createUiStatsReporter } from '../../../../../../../src/legacy/core_plugins/ui_metric/public'; - -/* eslint-disable @kbn/eslint/no-restricted-paths */ -import { notificationServiceMock } from '../../../../../../../src/core/public/notifications/notifications_service.mock'; -import { chromeServiceMock } from '../../../../../../../src/core/public/chrome/chrome_service.mock'; -import { docLinksServiceMock } from '../../../../../../../src/core/public/doc_links/doc_links_service.mock'; - -const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); - -export const setupEnvironment = () => { - // Mock initialization of services - // @ts-ignore - httpService.init(mockHttpClient); - breadcrumbService.init(chromeServiceMock.createStartContract(), ''); - documentationService.init(docLinksServiceMock.createStartContract()); - notificationService.init(notificationServiceMock.createStartContract()); - uiMetricService.init(createUiStatsReporter); - - const { server, httpRequestsMockHelpers } = initHttpRequests(); - - return { - server, - httpRequestsMockHelpers, - }; -}; diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx new file mode 100644 index 0000000000000..0212efe1f322d --- /dev/null +++ b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/setup_environment.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import axios from 'axios'; +import axiosXhrAdapter from 'axios/lib/adapters/xhr'; + +import { + notificationServiceMock, + docLinksServiceMock, +} from '../../../../../../../src/core/public/mocks'; +import { AppContextProvider } from '../../../public/application/app_context'; +import { httpService } from '../../../public/application/services/http'; +import { breadcrumbService } from '../../../public/application/services/breadcrumbs'; +import { documentationService } from '../../../public/application/services/documentation'; +import { notificationService } from '../../../public/application/services/notification'; +import { ExtensionsService } from '../../../public/services'; +import { UiMetricService } from '../../../public/application/services/ui_metric'; +import { setUiMetricService } from '../../../public/application/services/api'; +import { setExtensionsService } from '../../../public/application/store/selectors'; +import { init as initHttpRequests } from './http_requests'; + +const mockHttpClient = axios.create({ adapter: axiosXhrAdapter }); + +export const services = { + extensionsService: new ExtensionsService(), + uiMetricService: new UiMetricService('index_management'), +}; +services.uiMetricService.setup({ reportUiStats() {} } as any); +setExtensionsService(services.extensionsService); +setUiMetricService(services.uiMetricService); +const appDependencies = { services, core: {}, plugins: {} } as any; + +export const setupEnvironment = () => { + // Mock initialization of services + // @ts-ignore + httpService.setup(mockHttpClient); + breadcrumbService.setup(() => undefined); + documentationService.setup(docLinksServiceMock.createStartContract()); + notificationService.setup(notificationServiceMock.createSetupContract()); + + const { server, httpRequestsMockHelpers } = initHttpRequests(); + + return { + server, + httpRequestsMockHelpers, + }; +}; + +export const WithAppDependencies = (Comp: any) => (props: any) => ( + + + +); diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts index 286e56b4349e4..cd1b67c08d934 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts +++ b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_clone.helpers.ts @@ -6,9 +6,10 @@ import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateClone } from '../../../public/app/sections/template_clone'; +import { TemplateClone } from '../../../public/application/sections/template_clone'; import { formSetup } from './template_form.helpers'; import { TEMPLATE_NAME } from './constants'; +import { WithAppDependencies } from './setup_environment'; const testBedConfig: TestBedConfig = { memoryRouter: { @@ -18,6 +19,6 @@ const testBedConfig: TestBedConfig = { doMountAsync: true, }; -const initTestBed = registerTestBed(TemplateClone, testBedConfig); +const initTestBed = registerTestBed(WithAppDependencies(TemplateClone), testBedConfig); export const setup = formSetup.bind(null, initTestBed); diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts index b1b5d51ef6a9d..8e62bc25d6bd1 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts +++ b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_create.helpers.ts @@ -6,8 +6,9 @@ import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateCreate } from '../../../public/app/sections/template_create'; +import { TemplateCreate } from '../../../public/application/sections/template_create'; import { formSetup, TestSubjects } from './template_form.helpers'; +import { WithAppDependencies } from './setup_environment'; const testBedConfig: TestBedConfig = { memoryRouter: { @@ -17,6 +18,9 @@ const testBedConfig: TestBedConfig = { doMountAsync: true, }; -const initTestBed = registerTestBed(TemplateCreate, testBedConfig); +const initTestBed = registerTestBed( + WithAppDependencies(TemplateCreate), + testBedConfig +); export const setup = formSetup.bind(null, initTestBed); diff --git a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts index e8e19f637854d..cb1025234b48e 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts +++ b/x-pack/legacy/plugins/index_management/__jest__/client_integration/helpers/template_edit.helpers.ts @@ -6,9 +6,10 @@ import { registerTestBed, TestBedConfig } from '../../../../../../test_utils'; import { BASE_PATH } from '../../../common/constants'; -import { TemplateEdit } from '../../../public/app/sections/template_edit'; +import { TemplateEdit } from '../../../public/application/sections/template_edit'; import { formSetup, TestSubjects } from './template_form.helpers'; import { TEMPLATE_NAME } from './constants'; +import { WithAppDependencies } from './setup_environment'; const testBedConfig: TestBedConfig = { memoryRouter: { @@ -18,6 +19,6 @@ const testBedConfig: TestBedConfig = { doMountAsync: true, }; -const initTestBed = registerTestBed(TemplateEdit, testBedConfig); +const initTestBed = registerTestBed(WithAppDependencies(TemplateEdit), testBedConfig); export const setup = formSetup.bind(null, initTestBed); diff --git a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js b/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js index fe55b93d24a91..2b4fd89436458 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js +++ b/x-pack/legacy/plugins/index_management/__jest__/components/index_table.test.js @@ -8,23 +8,25 @@ import React from 'react'; import axios from 'axios'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import { MemoryRouter } from 'react-router-dom'; -import { AppWithoutRouter } from '../../public/app/app'; +import { AppWithoutRouter } from '../../public/application/app'; +import { AppContextProvider } from '../../public/application/app_context'; import { Provider } from 'react-redux'; -import { loadIndicesSuccess } from '../../public/app/store/actions'; -import { breadcrumbService } from '../../public/app/services/breadcrumbs'; -import { uiMetricService } from '../../public/app/services/ui_metric'; -import { notificationService } from '../../public/app/services/notification'; -import { httpService } from '../../public/app/services/http'; -import { createUiStatsReporter } from '../../../../../../src/legacy/core_plugins/ui_metric/public'; -import { indexManagementStore } from '../../public/app/store'; +import { loadIndicesSuccess } from '../../public/application/store/actions'; +import { breadcrumbService } from '../../public/application/services/breadcrumbs'; +import { UiMetricService } from '../../public/application/services/ui_metric'; +import { notificationService } from '../../public/application/services/notification'; +import { httpService } from '../../public/application/services/http'; +import { setUiMetricService } from '../../public/application/services/api'; +import { indexManagementStore } from '../../public/application/store'; +import { setExtensionsService } from '../../public/application/store/selectors'; import { BASE_PATH, API_BASE_PATH } from '../../common/constants'; import { mountWithIntl } from '../../../../../test_utils/enzyme_helpers'; +import { ExtensionsService } from '../../public/services'; import sinon from 'sinon'; import { findTestSubject } from '@elastic/eui/lib/test'; /* eslint-disable @kbn/eslint/no-restricted-paths */ import { notificationServiceMock } from '../../../../../../src/core/public/notifications/notifications_service.mock'; -import { chromeServiceMock } from '../../../../../../src/core/public/chrome/chrome_service.mock'; jest.mock('ui/new_platform'); @@ -107,17 +109,29 @@ const namesText = rendered => { describe('index table', () => { beforeEach(() => { // Mock initialization of services + const services = { + extensionsService: new ExtensionsService(), + uiMetricService: new UiMetricService('index_management'), + }; + services.uiMetricService.setup({ reportUiStats() {} }); + setExtensionsService(services.extensionsService); + setUiMetricService(services.uiMetricService); + // @ts-ignore - httpService.init(mockHttpClient); - breadcrumbService.init(chromeServiceMock.createStartContract(), ''); - uiMetricService.init(createUiStatsReporter); - notificationService.init(notificationServiceMock.createStartContract()); + httpService.setup(mockHttpClient); + breadcrumbService.setup(() => undefined); + notificationService.setup(notificationServiceMock.createStartContract()); + + store = indexManagementStore(services); + + const appDependencies = { services, core: {}, plugins: {} }; - store = indexManagementStore(); component = ( - + + + ); @@ -141,6 +155,9 @@ describe('index table', () => { server.respondImmediately = true; }); afterEach(() => { + if (!server) { + return; + } server.restore(); }); @@ -294,6 +311,8 @@ describe('index table', () => { confirmButton.simulate('click'); snapshot(status(rendered, rowIndex)); }); + // Commenting the following 2 tests as it works in the browser (status changes to "closed" or "open") but the + // snapshot say the contrary. Need to be investigated. test('close index button works from context menu', done => { const modifiedIndices = indices.map(index => { return { diff --git a/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js b/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js index eb21459a3cbc7..0d6d5ee796627 100644 --- a/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js +++ b/x-pack/legacy/plugins/index_management/__jest__/lib/flatten_object.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { flattenObject } from '../../public/app/lib/flatten_object'; +import { flattenObject } from '../../public/application/lib/flatten_object'; describe('flatten_object', () => { test('it flattens an object', () => { const obj = { diff --git a/x-pack/legacy/plugins/index_management/public/app/index.tsx b/x-pack/legacy/plugins/index_management/public/app/index.tsx deleted file mode 100644 index 027839df2e20e..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/app/index.tsx +++ /dev/null @@ -1,36 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { Provider } from 'react-redux'; -import { render, unmountComponentAtNode } from 'react-dom'; - -import { CoreStart } from '../../../../../../src/core/public'; - -import { App } from './app'; -import { indexManagementStore } from './store'; - -export const mountReactApp = (elem: HTMLElement | null, { core }: { core: CoreStart }): void => { - if (elem) { - const { i18n } = core; - const { Context: I18nContext } = i18n; - - render( - - - - - , - elem - ); - } -}; - -export const unmountReactApp = (elem: HTMLElement | null) => { - if (elem) { - unmountComponentAtNode(elem); - } -}; diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/detail_panel.js b/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/detail_panel.js deleted file mode 100644 index 0bfd32581ef4a..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/detail_panel.js +++ /dev/null @@ -1,164 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { Component, Fragment } from 'react'; -import { Route } from 'react-router-dom'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutFooter, - EuiFlyoutHeader, - EuiSpacer, - EuiTabs, - EuiTab, - EuiTitle, -} from '@elastic/eui'; -import { renderBadges } from '../../../../lib/render_badges'; -import { INDEX_OPEN } from '../../../../../../common/constants'; -import { - TAB_SUMMARY, - TAB_SETTINGS, - TAB_MAPPING, - TAB_STATS, - TAB_EDIT_SETTINGS, -} from '../../../../constants'; -import { IndexActionsContextMenu } from '../index_actions_context_menu'; -import { ShowJson } from './show_json'; -import { Summary } from './summary'; -import { EditSettingsJson } from './edit_settings_json'; - -const tabToHumanizedMap = { - [TAB_SUMMARY]: ( - - ), - [TAB_SETTINGS]: ( - - ), - [TAB_MAPPING]: ( - - ), - [TAB_STATS]: ( - - ), - [TAB_EDIT_SETTINGS]: ( - - ), -}; - -const tabs = [TAB_SUMMARY, TAB_SETTINGS, TAB_MAPPING, TAB_STATS, TAB_EDIT_SETTINGS]; - -export class DetailPanel extends Component { - renderTabs() { - const { panelType, indexName, index, openDetailPanel } = this.props; - return tabs.map((tab, i) => { - const isSelected = tab === panelType; - return ( - openDetailPanel({ panelType: tab, indexName })} - isSelected={isSelected} - data-test-subj={`detailPanelTab${isSelected ? 'Selected' : ''}`} - disabled={tab === TAB_STATS && index.status !== INDEX_OPEN} - key={i} - > - {tabToHumanizedMap[tab]} - - ); - }); - } - render() { - const { panelType, indexName, index, closeDetailPanel } = this.props; - if (!panelType) { - return null; - } - let component = null; - switch (panelType) { - case TAB_EDIT_SETTINGS: - component = ; - break; - case TAB_MAPPING: - case TAB_SETTINGS: - case TAB_STATS: - component = ; - break; - default: - component = ; - } - const content = index ? ( - - {component} - - - - ( - - } - /> - )} - /> - - - - - ) : ( - - - - } - color="danger" - iconType="cross" - > - - - - ); - return ( - - - -

- {indexName} - {renderBadges(index)} -

-
- {index ? {this.renderTabs()} : null} -
- {content} -
- ); - } -} diff --git a/x-pack/legacy/plugins/index_management/public/app/services/ui_metric.ts b/x-pack/legacy/plugins/index_management/public/app/services/ui_metric.ts deleted file mode 100644 index 4ea98fff1d70a..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/app/services/ui_metric.ts +++ /dev/null @@ -1,25 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { UIM_APP_NAME } from '../../../common/constants'; -import { - createUiStatsReporter, - UiStatsMetricType, -} from '../../../../../../../src/legacy/core_plugins/ui_metric/public'; - -class UiMetricService { - track?: ReturnType; - - public init = (getReporter: typeof createUiStatsReporter): void => { - this.track = getReporter(UIM_APP_NAME); - }; - - public trackMetric = (type: 'loaded' | 'click' | 'count', eventName: string): void => { - if (!this.track) throw Error('UiMetricService not initialized.'); - return this.track(type as UiStatsMetricType, eventName); - }; -} - -export const uiMetricService = new UiMetricService(); diff --git a/x-pack/legacy/plugins/index_management/public/app/store/reducers/detail_panel.js b/x-pack/legacy/plugins/index_management/public/app/store/reducers/detail_panel.js deleted file mode 100644 index 9ba73a1c61b03..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/app/store/reducers/detail_panel.js +++ /dev/null @@ -1,89 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { handleActions } from 'redux-actions'; -import { - UIM_DETAIL_PANEL_SUMMARY_TAB, - UIM_DETAIL_PANEL_SETTINGS_TAB, - UIM_DETAIL_PANEL_MAPPING_TAB, - UIM_DETAIL_PANEL_STATS_TAB, - UIM_DETAIL_PANEL_EDIT_SETTINGS_TAB, -} from '../../../../common/constants'; -import { - TAB_SUMMARY, - TAB_SETTINGS, - TAB_MAPPING, - TAB_STATS, - TAB_EDIT_SETTINGS, -} from '../../constants'; -import { uiMetricService } from '../../services/ui_metric'; -import { openDetailPanel, closeDetailPanel } from '../actions/detail_panel'; -import { loadIndexDataSuccess } from '../actions/load_index_data'; -import { - updateIndexSettingsSuccess, - updateIndexSettingsError, -} from '../actions/update_index_settings'; -import { deleteIndicesSuccess } from '../actions/delete_indices'; - -const defaultState = {}; - -export const detailPanel = handleActions( - { - [deleteIndicesSuccess](state, action) { - const { indexNames } = action.payload; - const { indexName } = state; - if (indexNames.includes(indexName)) { - return {}; - } else { - return state; - } - }, - [openDetailPanel](state, action) { - const { panelType, indexName, title } = action.payload; - - const panelTypeToUiMetricMap = { - [TAB_SUMMARY]: UIM_DETAIL_PANEL_SUMMARY_TAB, - [TAB_SETTINGS]: UIM_DETAIL_PANEL_SETTINGS_TAB, - [TAB_MAPPING]: UIM_DETAIL_PANEL_MAPPING_TAB, - [TAB_STATS]: UIM_DETAIL_PANEL_STATS_TAB, - [TAB_EDIT_SETTINGS]: UIM_DETAIL_PANEL_EDIT_SETTINGS_TAB, - }; - - if (panelTypeToUiMetricMap[panelType]) { - uiMetricService.trackMetric('count', panelTypeToUiMetricMap[panelType]); - } - - return { - panelType: panelType || state.panelType || TAB_SUMMARY, - indexName, - title, - }; - }, - [closeDetailPanel]() { - return {}; - }, - [loadIndexDataSuccess](state, action) { - const { data } = action.payload; - const newState = { - ...state, - data, - }; - return newState; - }, - [updateIndexSettingsError](state, action) { - const { error } = action.payload; - const newState = { - ...state, - error, - }; - return newState; - }, - [updateIndexSettingsSuccess]() { - return {}; - }, - }, - defaultState -); diff --git a/x-pack/legacy/plugins/index_management/public/app/app.tsx b/x-pack/legacy/plugins/index_management/public/application/app.tsx similarity index 93% rename from x-pack/legacy/plugins/index_management/public/app/app.tsx rename to x-pack/legacy/plugins/index_management/public/application/app.tsx index 9ed824e184120..3b475f3baba04 100644 --- a/x-pack/legacy/plugins/index_management/public/app/app.tsx +++ b/x-pack/legacy/plugins/index_management/public/application/app.tsx @@ -11,9 +11,11 @@ import { IndexManagementHome } from './sections/home'; import { TemplateCreate } from './sections/template_create'; import { TemplateClone } from './sections/template_clone'; import { TemplateEdit } from './sections/template_edit'; -import { uiMetricService } from './services/ui_metric'; + +import { useServices } from './app_context'; export const App = () => { + const { uiMetricService } = useServices(); useEffect(() => uiMetricService.trackMetric('loaded', UIM_APP_LOAD), []); return ( diff --git a/x-pack/legacy/plugins/index_management/public/application/app_context.tsx b/x-pack/legacy/plugins/index_management/public/application/app_context.tsx new file mode 100644 index 0000000000000..12e0d362a2930 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/app_context.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { createContext, useContext } from 'react'; +import { CoreStart } from '../../../../../../src/core/public'; + +import { UsageCollectionSetup } from '../../../../../../src/plugins/usage_collection/public'; +import { IndexMgmtMetricsType } from '../types'; +import { UiMetricService, NotificationService, HttpService } from './services'; +import { ExtensionsService } from '../services'; + +const AppContext = createContext(undefined); + +export interface AppDependencies { + core: { + fatalErrors: CoreStart['fatalErrors']; + }; + plugins: { + usageCollection: UsageCollectionSetup; + }; + services: { + uiMetricService: UiMetricService; + extensionsService: ExtensionsService; + httpService: HttpService; + notificationService: NotificationService; + }; +} + +export const AppContextProvider = ({ + children, + value, +}: { + value: AppDependencies; + children: React.ReactNode; +}) => { + return {children}; +}; + +export const AppContextConsumer = AppContext.Consumer; + +export const useAppContext = () => { + const ctx = useContext(AppContext); + if (!ctx) { + throw new Error('"useAppContext" can only be called inside of AppContext.Provider!'); + } + return ctx; +}; + +export const useServices = () => useAppContext().services; diff --git a/x-pack/legacy/plugins/index_management/public/app/components/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/__jest__/client_integration/helpers/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/__jest__/client_integration/helpers/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/helpers/mappings_editor.helpers.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/_index.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/_index.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/_index.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/_index.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/code_block.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/code_block.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/code_block.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/configuration_form.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/configuration_form.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/configuration_form_schema.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/dynamic_mapping_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/dynamic_mapping_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/meta_field_section/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/meta_field_section/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/meta_field_section/meta_field_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/routing_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/routing_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/routing_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/source_field_section/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/source_field_section/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/configuration_form/source_field_section/source_field_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/_index.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/_index.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields_header.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/document_fields_header.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/document_fields_header.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/editor_toggle_controls.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzer_parameter_selects.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/analyzers_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/boost_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_number_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/coerce_shape_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/copy_to_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/doc_values_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/dynamic_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/eager_global_ordinals_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/enabled_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_absolute.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_frequency_filter_percentage.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/fielddata_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/format_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_malformed.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/ignore_z_value_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/index_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/locale_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/max_shingle_size_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/name_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/norms_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/null_value_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/orientation_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/path_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/relations_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/similarity_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/split_queries_on_whitespace_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/store_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/term_vector_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/field_parameters/type_parameter.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/_field_list_item.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/_field_list_item.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_field_list_item.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/_index.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/_index.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/create_field.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/alias_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/dense_vector_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/scaled_float_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/create_field/required_parameters_forms/token_count_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/delete_field_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_edit_field_form_row.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/_index.scss diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/advanced_parameters_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/basic_parameters_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_container.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_form_row.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/edit_field_header_form.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/field_description_section.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/edit_field/update_field_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/alias_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/binary_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/boolean_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/completion_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/date_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/dense_vector_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/flattened_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_point_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/geo_shape_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/ip_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/join_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/keyword_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/nested_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/numeric_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/object_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/range_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/search_as_you_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/shape_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/text_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/token_count_type.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/fields_list.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/fields_list.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/fields_list_item_container.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/modal_confirmation_delete_fields.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields_json_editor.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields_json_editor.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_json_editor.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields_tree_editor.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/fields_tree_editor.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields_tree_editor.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/search_fields/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/search_fields/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/search_fields/search_result.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/search_fields/search_result.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/document_fields/search_fields/search_result_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/fields_tree.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/fields_tree.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/fields_tree.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_from_json_button.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_from_json_button.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_from_json_button.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/multiple_mappings_warning.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/multiple_mappings_warning.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/multiple_mappings_warning.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form_schema.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/templates_form/templates_form_schema.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form_schema.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/tree/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/tree/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/tree/tree.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/tree/tree.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/tree/tree_item.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/components/tree/tree_item.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/components/tree/tree_item.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/data_types_definition.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/default_values.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/default_values.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/default_values.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/field_options.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/field_options.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/field_options_i18n.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/field_options_i18n.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/field_options_i18n.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/mappings_editor.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/mappings_editor.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/mappings_editor.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/parameters_definition.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/constants/parameters_definition.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/constants/parameters_definition.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index_settings_context.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/index_settings_context.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/index_settings_context.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/error_reporter.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/error_reporter.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/error_reporter.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.test.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.test.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/extract_mappings_definition.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/extract_mappings_definition.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.test.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.test.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/mappings_validator.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/mappings_validator.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/search_fields.test.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/search_fields.test.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/search_fields.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/search_fields.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/search_fields.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/serializers.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/serializers.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/serializers.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/utils.test.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/utils.test.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.test.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/utils.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/utils.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/utils.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/validators.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/lib/validators.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/lib/validators.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_editor.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_editor.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/mappings_state.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/mappings_state.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/reducer.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/reducer.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/reducer.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/shared_imports.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/shared_imports.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/shared_imports.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/types.ts b/x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/mappings_editor/types.ts rename to x-pack/legacy/plugins/index_management/public/application/components/mappings_editor/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/no_match/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/no_match/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/no_match/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/no_match/no_match.tsx b/x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/no_match/no_match.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/no_match/no_match.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/page_error/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/page_error/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/page_error/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/page_error/page_error_forbidden.tsx b/x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/page_error/page_error_forbidden.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/page_error/page_error_forbidden.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/section_error.tsx b/x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/section_error.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/section_error.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/section_loading.tsx b/x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/section_loading.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/section_loading.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_delete_modal.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_delete_modal.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_delete_modal.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/index.ts b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/index.ts rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_aliases.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_aliases.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_aliases.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_logistics.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_logistics.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_mappings.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_mappings.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_mappings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_review.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_review.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_review.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_settings.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/step_settings.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/step_settings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/use_json_step.ts b/x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/steps/use_json_step.ts rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/steps/use_json_step.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/template_form.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/template_form.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/template_form_schemas.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/template_form_schemas.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/template_steps.tsx b/x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/template_steps.tsx rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/template_steps.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/components/template_form/types.ts b/x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/components/template_form/types.ts rename to x-pack/legacy/plugins/index_management/public/application/components/template_form/types.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/constants/detail_panel_tabs.ts b/x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/constants/detail_panel_tabs.ts rename to x-pack/legacy/plugins/index_management/public/application/constants/detail_panel_tabs.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/constants/index.ts b/x-pack/legacy/plugins/index_management/public/application/constants/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/constants/index.ts rename to x-pack/legacy/plugins/index_management/public/application/constants/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/constants/refresh_intervals.ts b/x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/constants/refresh_intervals.ts rename to x-pack/legacy/plugins/index_management/public/application/constants/refresh_intervals.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/index.tsx b/x-pack/legacy/plugins/index_management/public/application/index.tsx new file mode 100644 index 0000000000000..b9859903f1434 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/index.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { Provider } from 'react-redux'; +import { render, unmountComponentAtNode } from 'react-dom'; + +import { CoreStart } from '../../../../../../src/core/public'; + +import { AppContextProvider, AppDependencies } from './app_context'; +import { App } from './app'; +import { indexManagementStore } from './store'; + +export const renderApp = ( + elem: HTMLElement | null, + { core, dependencies }: { core: CoreStart; dependencies: AppDependencies } +) => { + if (!elem) { + return () => undefined; + } + + const { i18n } = core; + const { Context: I18nContext } = i18n; + const { services } = dependencies; + + render( + + + + + + + , + elem + ); + + return () => { + unmountComponentAtNode(elem); + }; +}; + +export { AppDependencies }; diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/ace.js b/x-pack/legacy/plugins/index_management/public/application/lib/ace.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/lib/ace.js rename to x-pack/legacy/plugins/index_management/public/application/lib/ace.js diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/edit_settings.js b/x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/lib/edit_settings.js rename to x-pack/legacy/plugins/index_management/public/application/lib/edit_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/flatten_object.js b/x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/lib/flatten_object.js rename to x-pack/legacy/plugins/index_management/public/application/lib/flatten_object.js diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/flatten_panel_tree.js b/x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/lib/flatten_panel_tree.js rename to x-pack/legacy/plugins/index_management/public/application/lib/flatten_panel_tree.js diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/index_status_labels.js b/x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/lib/index_status_labels.js rename to x-pack/legacy/plugins/index_management/public/application/lib/index_status_labels.js diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/manage_angular_lifecycle.ts b/x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/lib/manage_angular_lifecycle.ts rename to x-pack/legacy/plugins/index_management/public/application/lib/manage_angular_lifecycle.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/lib/render_badges.js b/x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js similarity index 83% rename from x-pack/legacy/plugins/index_management/public/app/lib/render_badges.js rename to x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js index 175b88bd31f6c..fbca65fe5dba6 100644 --- a/x-pack/legacy/plugins/index_management/public/app/lib/render_badges.js +++ b/x-pack/legacy/plugins/index_management/public/application/lib/render_badges.js @@ -7,10 +7,10 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiBadge, EuiSearchBar } from '@elastic/eui'; -import { getBadgeExtensions } from '../../index_management_extensions'; -export const renderBadges = (index, filterChanged) => { + +export const renderBadges = (index, filterChanged, extensionsService) => { const badgeLabels = []; - getBadgeExtensions().forEach(({ matchIndex, label, color, filterExpression }) => { + extensionsService.badges.forEach(({ matchIndex, label, color, filterExpression }) => { if (matchIndex(index)) { const clickHandler = () => { filterChanged && diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/home.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/home.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/home.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/detail_panel.container.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/detail_panel.container.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.container.js diff --git a/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js new file mode 100644 index 0000000000000..e0128b1d51d02 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/detail_panel.js @@ -0,0 +1,167 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { Route } from 'react-router-dom'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiSpacer, + EuiTabs, + EuiTab, + EuiTitle, +} from '@elastic/eui'; +import { renderBadges } from '../../../../lib/render_badges'; +import { INDEX_OPEN } from '../../../../../../common/constants'; +import { + TAB_SUMMARY, + TAB_SETTINGS, + TAB_MAPPING, + TAB_STATS, + TAB_EDIT_SETTINGS, +} from '../../../../constants'; +import { IndexActionsContextMenu } from '../index_actions_context_menu'; +import { ShowJson } from './show_json'; +import { Summary } from './summary'; +import { EditSettingsJson } from './edit_settings_json'; +import { useServices } from '../../../../app_context'; + +const tabToHumanizedMap = { + [TAB_SUMMARY]: ( + + ), + [TAB_SETTINGS]: ( + + ), + [TAB_MAPPING]: ( + + ), + [TAB_STATS]: ( + + ), + [TAB_EDIT_SETTINGS]: ( + + ), +}; + +const tabs = [TAB_SUMMARY, TAB_SETTINGS, TAB_MAPPING, TAB_STATS, TAB_EDIT_SETTINGS]; + +export const DetailPanel = ({ panelType, indexName, index, openDetailPanel, closeDetailPanel }) => { + const { extensionsService } = useServices(); + + const renderTabs = () => { + return tabs.map((tab, i) => { + const isSelected = tab === panelType; + return ( + openDetailPanel({ panelType: tab, indexName })} + isSelected={isSelected} + data-test-subj={`detailPanelTab${isSelected ? 'Selected' : ''}`} + disabled={tab === TAB_STATS && index.status !== INDEX_OPEN} + key={i} + > + {tabToHumanizedMap[tab]} + + ); + }); + }; + + if (!panelType) { + return null; + } + + let component = null; + switch (panelType) { + case TAB_EDIT_SETTINGS: + component = ; + break; + case TAB_MAPPING: + case TAB_SETTINGS: + case TAB_STATS: + component = ; + break; + default: + component = ; + } + + const content = index ? ( + + {component} + + + + ( + + } + /> + )} + /> + + + + + ) : ( + + + + } + color="danger" + iconType="cross" + > + + + + ); + + return ( + + + +

+ {indexName} + {renderBadges(index, undefined, extensionsService)} +

+
+ {index ? {renderTabs()} : null} +
+ {content} +
+ ); +}; diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.container.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/edit_settings_json.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/edit_settings_json/index.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/edit_settings_json/index.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/edit_settings_json/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/index.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/index.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/show_json/index.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/show_json/index.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/show_json/show_json.container.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/show_json/show_json.container.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.container.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/show_json/show_json.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/show_json/show_json.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/show_json/show_json.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/index.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/index.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/summary.container.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/summary.container.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.container.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/summary.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js similarity index 72% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/summary.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js index a49dc6079e3c5..e49b3c353931e 100644 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/detail_panel/summary/summary.js +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/detail_panel/summary/summary.js @@ -6,7 +6,6 @@ import React, { Fragment } from 'react'; import { i18n } from '@kbn/i18n'; -import { healthToColor } from '../../../../../services'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, @@ -19,7 +18,9 @@ import { EuiSpacer, EuiTitle, } from '@elastic/eui'; -import { getSummaryExtensions } from '../../../../../../index_management_extensions'; +import { healthToColor } from '../../../../../services'; +import { AppContextConsumer } from '../../../../../app_context'; + const getHeaders = () => { return { health: i18n.translate('xpack.idxMgmt.summary.headers.healthHeader', { @@ -53,9 +54,9 @@ const getHeaders = () => { }; export class Summary extends React.PureComponent { - getAdditionalContent() { + getAdditionalContent(extensionsService) { const { index } = this.props; - const extensions = getSummaryExtensions(); + const extensions = extensionsService.summaries; return extensions.map((summaryExtension, i) => { return ( @@ -65,6 +66,7 @@ export class Summary extends React.PureComponent { ); }); } + buildRows() { const { index } = this.props; const headers = getHeaders(); @@ -99,26 +101,36 @@ export class Summary extends React.PureComponent { } render() { - const { left, right } = this.buildRows(); - const additionalContent = this.getAdditionalContent(); return ( - - -

- -

-
- - - - {left} - - - {right} - - - {additionalContent} -
+ + {({ services }) => { + const { left, right } = this.buildRows(); + const additionalContent = this.getAdditionalContent(services.extensionsService); + + return ( + + +

+ +

+
+ + + + {left} + + + {right} + + + {additionalContent} +
+ ); + }} +
); } } diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.container.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js similarity index 91% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js index 5ed651e3125cf..8f794ce1ed612 100644 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_actions_context_menu/index_actions_context_menu.js @@ -23,21 +23,9 @@ import { EuiCheckbox, } from '@elastic/eui'; -// We will be able to remove these after the NP migration is complete. -import { fatalError } from 'ui/notify'; -import { createUiStatsReporter } from '../../../../../../../../../../src/legacy/core_plugins/ui_metric/public'; -import { httpService } from '../../../../services/http'; -import { notificationService } from '../../../../services/notification'; - import { flattenPanelTree } from '../../../../lib/flatten_panel_tree'; import { INDEX_OPEN } from '../../../../../../common/constants'; -import { getActionExtensions } from '../../../../../index_management_extensions'; - -// We will be able to remove this after the NP migration is complete and we can just inject the -// NP fatalErrors service.. -const getNewPlatformCompatibleFatalErrorService = () => ({ - add: fatalError.bind(fatalError), -}); +import { AppContextConsumer } from '../../../../app_context'; export class IndexActionsContextMenu extends Component { constructor(props) { @@ -58,7 +46,11 @@ export class IndexActionsContextMenu extends Component { confirmAction = isActionConfirmed => { this.setState({ isActionConfirmed }); }; - panels() { + panels({ + core: { fatalErrors }, + services: { extensionsService, httpService, notificationService }, + plugins: { usageCollection }, + }) { const { closeIndices, openIndices, @@ -222,7 +214,7 @@ export class IndexActionsContextMenu extends Component { this.setState({ renderConfirmModal: this.renderConfirmDeleteModal }); }, }); - getActionExtensions().forEach(actionExtension => { + extensionsService.actions.forEach(actionExtension => { const actionExtensionDefinition = actionExtension({ indices, reloadIndices, @@ -231,12 +223,11 @@ export class IndexActionsContextMenu extends Component { // dependencies, but they're not available unless the app's "setup" lifecycle stage occurs. // Within the old platform, "setup" only occurs once the user actually visits the app. // Once ILM and IM have been moved out of legacy this hack won't be necessary. - createUiStatsReporter, + usageCollection, toasts: notificationService.toasts, - fatalErrors: getNewPlatformCompatibleFatalErrorService(), + fatalErrors, httpClient: httpService.httpClient, }); - if (actionExtensionDefinition) { const { buttonLabel, @@ -721,52 +712,61 @@ export class IndexActionsContextMenu extends Component { }; render() { - const { indexNames } = this.props; - const selectedIndexCount = indexNames.length; - const { - iconSide = 'right', - anchorPosition = 'rightUp', - label = i18n.translate('xpack.idxMgmt.indexActionsMenu.manageButtonLabel', { - defaultMessage: 'Manage {selectedIndexCount, plural, one {index} other {indices}}', - values: { selectedIndexCount }, - }), - iconType = 'arrowDown', - } = this.props; - const panels = this.panels(); - const button = ( - - {label} - - ); - return ( -
- {this.state.renderConfirmModal - ? this.state.renderConfirmModal(this.closeConfirmModal) - : null} - - - -
+ + {appDependencies => { + const { indexNames } = this.props; + const selectedIndexCount = indexNames.length; + const { + iconSide = 'right', + anchorPosition = 'rightUp', + label = i18n.translate('xpack.idxMgmt.indexActionsMenu.manageButtonLabel', { + defaultMessage: 'Manage {selectedIndexCount, plural, one {index} other {indices}}', + values: { selectedIndexCount }, + }), + iconType = 'arrowDown', + } = this.props; + + const panels = this.panels(appDependencies); + + const button = ( + + {label} + + ); + + return ( +
+ {this.state.renderConfirmModal + ? this.state.renderConfirmModal(this.closeConfirmModal) + : null} + + + +
+ ); + }} +
); } } diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_list.d.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_list.d.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.d.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_list.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_list.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_list.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index_table.container.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index_table.container.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.container.js diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index_table.js b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js similarity index 62% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index_table.js rename to x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js index e043323c5b290..b659e2ec86a9b 100644 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/index_list/index_table/index_table.js +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/index_list/index_table/index_table.js @@ -38,12 +38,7 @@ import { import { UIM_SHOW_DETAILS_CLICK } from '../../../../../../common/constants'; import { REFRESH_RATE_INDEX_LIST } from '../../../../constants'; import { healthToColor } from '../../../../services'; -import { uiMetricService } from '../../../../services/ui_metric'; -import { - getBannerExtensions, - getFilterExtensions, - getToggleExtensions, -} from '../../../../../index_management_extensions'; +import { AppContextConsumer } from '../../../../app_context'; import { renderBadges } from '../../../../lib/render_badges'; import { NoMatch, PageErrorForbidden } from '../../../../components'; import { IndexActionsContextMenu } from '../index_actions_context_menu'; @@ -153,9 +148,9 @@ export class IndexTable extends Component { this.setState({ filterError: null }); } }; - getFilters = () => { + getFilters = extensionsService => { const { allIndices } = this.props; - return getFilterExtensions().reduce((accum, filterExtension) => { + return extensionsService.filters.reduce((accum, filterExtension) => { const filtersToAdd = filterExtension(allIndices); return [...accum, ...filtersToAdd]; }, []); @@ -218,7 +213,7 @@ export class IndexTable extends Component { }); } - buildRowCell(fieldName, value, index) { + buildRowCell(fieldName, value, index, appServices) { const { openDetailPanel, filterChanged } = this.props; if (fieldName === 'health') { return {value}; @@ -228,20 +223,20 @@ export class IndexTable extends Component { { - uiMetricService.trackMetric('click', UIM_SHOW_DETAILS_CLICK); + appServices.uiMetricService.trackMetric('click', UIM_SHOW_DETAILS_CLICK); openDetailPanel(value); }} > {value} - {renderBadges(index, filterChanged)} + {renderBadges(index, filterChanged, appServices.extensionsService)}
); } return value; } - buildRowCells(index) { + buildRowCells(index, appServices) { return Object.keys(HEADERS).map(fieldName => { const { name } = index; const value = index[fieldName]; @@ -255,7 +250,9 @@ export class IndexTable extends Component { data-test-subj={`indexTableCell-${fieldName}`} >
- {this.buildRowCell(fieldName, value, index)} + + {this.buildRowCell(fieldName, value, index, appServices)} +
); @@ -268,7 +265,7 @@ export class IndexTable extends Component { className={'indTable__cell--' + fieldName} header={fieldName} > - {this.buildRowCell(fieldName, value, index)} + {this.buildRowCell(fieldName, value, index, appServices)} ); }); @@ -310,9 +307,9 @@ export class IndexTable extends Component { ); } - renderBanners() { + renderBanners(extensionsService) { const { allIndices = [], filterChanged } = this.props; - return getBannerExtensions().map((bannerExtension, i) => { + return extensionsService.banners.map((bannerExtension, i) => { const bannerData = bannerExtension(allIndices); if (!bannerData) { return null; @@ -335,7 +332,8 @@ export class IndexTable extends Component { ); }); } - buildRows() { + + buildRows(appServices) { const { indices = [], detailPanelIndexName } = this.props; return indices.map(index => { const { name } = index; @@ -360,7 +358,7 @@ export class IndexTable extends Component { })} /> - {this.buildRowCells(index)} + {this.buildRowCells(index, appServices)} ); }); @@ -383,6 +381,7 @@ export class IndexTable extends Component { onItemSelectionChanged = selectedIndices => { this.setState({ selectedIndices }); }; + renderToggleControl({ name, label }) { const { toggleNameToVisibleMap, toggleChanged } = this.props; return ( @@ -397,6 +396,7 @@ export class IndexTable extends Component { ); } + render() { const { filter, @@ -434,145 +434,157 @@ export class IndexTable extends Component { } return ( - - - - - - - - - - - {(indicesLoading && allIndices.length === 0) || indicesError ? null : ( - - {getToggleExtensions().map(toggle => { - return this.renderToggleControl(toggle); - })} - - showSystemIndicesChanged(event.target.checked)} - label={ + + {({ services }) => { + const { extensionsService } = services; + + return ( + + + + + - } - /> + + + + + {(indicesLoading && allIndices.length === 0) || indicesError ? null : ( + + {extensionsService.toggles.map(toggle => { + return this.renderToggleControl(toggle); + })} + + showSystemIndicesChanged(event.target.checked)} + label={ + + } + /> + + + )} - )} - - - - {this.renderBanners()} - {indicesError && this.renderError()} - - {atLeastOneItemSelected ? ( - - ( - { - this.setState({ selectedIndicesMap: {} }); - }} - /> + + {this.renderBanners(extensionsService)} + {indicesError && this.renderError()} + + {atLeastOneItemSelected ? ( + + ( + { + this.setState({ selectedIndicesMap: {} }); + }} + /> + )} + /> + + ) : null} + {(indicesLoading && allIndices.length === 0) || indicesError ? null : ( + + + 0 + ? this.getFilters(extensionsService) + : null + } + defaultQuery={filter} + query={filter} + box={{ + incremental: true, + placeholder: i18n.translate( + 'xpack.idxMgmt.indexTable.systemIndicesSearchInputPlaceholder', + { + defaultMessage: 'Search', + } + ), + }} + aria-label={i18n.translate( + 'xpack.idxMgmt.indexTable.systemIndicesSearchIndicesAriaLabel', + { + defaultMessage: 'Search indices', + } + )} + data-test-subj="indexTableFilterInput" + onChange={this.onFilterChanged} + /> + + + { + loadIndices(); + }} + iconType="refresh" + data-test-subj="reloadIndicesButton" + > + + + + )} - /> - - ) : null} - {(indicesLoading && allIndices.length === 0) || indicesError ? null : ( - - - 0 ? this.getFilters() : null} - defaultQuery={filter} - query={filter} - box={{ - incremental: true, - placeholder: i18n.translate( - 'xpack.idxMgmt.indexTable.systemIndicesSearchInputPlaceholder', - { - defaultMessage: 'Search', - } - ), - }} - aria-label={i18n.translate( - 'xpack.idxMgmt.indexTable.systemIndicesSearchIndicesAriaLabel', - { - defaultMessage: 'Search indices', - } - )} - data-test-subj="indexTableFilterInput" - onChange={this.onFilterChanged} - /> - - - { - loadIndices(); - }} - iconType="refresh" - data-test-subj="reloadIndicesButton" - > - - - + + {this.renderFilterError()} + + {indices.length > 0 ? ( +
+ + + + + + + + + + + {this.buildHeader()} + + {this.buildRows(services)} + +
+ ) : ( + emptyState + )} + + {indices.length > 0 ? this.renderPager() : null}
- )} - - {this.renderFilterError()} - - {indices.length > 0 ? ( -
- - - - - - - - - - - {this.buildHeader()} - - {this.buildRows()} - -
- ) : ( - emptyState - )} - - {indices.length > 0 ? this.renderPager() : null} - + ); + }} + ); } } diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_aliases.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_aliases.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_aliases.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_mappings.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_mappings.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_mappings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_settings.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_settings.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_settings.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/tabs/tab_summary.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/template_details.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx similarity index 99% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/template_details.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx index eaa0a331afcef..ced8bd97e744b 100644 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_details/template_details.tsx +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_details/template_details.tsx @@ -34,8 +34,8 @@ import { Template } from '../../../../../../common/types'; import { TemplateDeleteModal, SectionLoading, SectionError, Error } from '../../../../components'; import { loadIndexTemplate } from '../../../../services/api'; import { decodePath } from '../../../../services/routing'; -import { uiMetricService } from '../../../../services/ui_metric'; import { SendRequestResponse } from '../../../../../shared_imports'; +import { useServices } from '../../../../app_context'; import { TabSummary, TabMappings, TabSettings, TabAliases } from './tabs'; interface Props { @@ -101,6 +101,7 @@ export const TemplateDetails: React.FunctionComponent = ({ cloneTemplate, reload, }) => { + const { uiMetricService } = useServices(); const decodedTemplateName = decodePath(templateName); const { error, data: templateDetails, isLoading } = loadIndexTemplate(decodedTemplateName); // TS complains if we use destructuring here. Fixed in 3.6.0 (https://github.com/microsoft/TypeScript/pull/31711). diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_list.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_list.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx index 550fa941e1984..71c32e2e0177f 100644 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_list.tsx +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_list.tsx @@ -20,7 +20,7 @@ import { SectionError, SectionLoading, Error } from '../../../components'; import { TemplateTable } from './template_table'; import { loadIndexTemplates } from '../../../services/api'; import { Template } from '../../../../../common/types'; -import { uiMetricService } from '../../../services/ui_metric'; +import { useServices } from '../../../app_context'; import { getTemplateEditLink, getTemplateListLink, @@ -39,6 +39,7 @@ export const TemplateList: React.FunctionComponent { + const { uiMetricService } = useServices(); const { error, isLoading, data: templates, sendRequest: reload } = loadIndexTemplates(); let content; diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_table/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_table/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_table/template_table.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx similarity index 98% rename from x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_table/template_table.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx index 6070b25fb1ca2..72321ccc14888 100644 --- a/x-pack/legacy/plugins/index_management/public/app/sections/home/template_list/template_table/template_table.tsx +++ b/x-pack/legacy/plugins/index_management/public/application/sections/home/template_list/template_table/template_table.tsx @@ -11,7 +11,7 @@ import { EuiInMemoryTable, EuiIcon, EuiButton, EuiLink, EuiBasicTableColumn } fr import { TemplateListItem, Template } from '../../../../../../common/types'; import { BASE_PATH, UIM_TEMPLATE_SHOW_DETAILS_CLICK } from '../../../../../../common/constants'; import { TemplateDeleteModal } from '../../../../components'; -import { uiMetricService } from '../../../../services/ui_metric'; +import { useServices } from '../../../../app_context'; import { getTemplateDetailsLink } from '../../../../services/routing'; import { SendRequestResponse } from '../../../../../shared_imports'; @@ -28,6 +28,7 @@ export const TemplateTable: React.FunctionComponent = ({ editTemplate, cloneTemplate, }) => { + const { uiMetricService } = useServices(); const [selection, setSelection] = useState([]); const [templatesToDelete, setTemplatesToDelete] = useState>([]); diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/template_clone/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/template_clone/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/template_clone/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/template_clone/template_clone.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/template_clone/template_clone.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/template_clone/template_clone.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/template_create/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/template_create/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/template_create/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/template_create/template_create.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/template_create/template_create.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/template_create/template_create.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/template_edit/index.ts b/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/template_edit/index.ts rename to x-pack/legacy/plugins/index_management/public/application/sections/template_edit/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/sections/template_edit/template_edit.tsx b/x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/sections/template_edit/template_edit.tsx rename to x-pack/legacy/plugins/index_management/public/application/sections/template_edit/template_edit.tsx diff --git a/x-pack/legacy/plugins/index_management/public/app/services/api.ts b/x-pack/legacy/plugins/index_management/public/application/services/api.ts similarity index 94% rename from x-pack/legacy/plugins/index_management/public/app/services/api.ts rename to x-pack/legacy/plugins/index_management/public/application/services/api.ts index 8030a7f29d4ee..642fd441b353a 100644 --- a/x-pack/legacy/plugins/index_management/public/app/services/api.ts +++ b/x-pack/legacy/plugins/index_management/public/application/services/api.ts @@ -34,10 +34,19 @@ import { import { TAB_SETTINGS, TAB_MAPPING, TAB_STATS } from '../constants'; -import { uiMetricService } from './ui_metric'; import { useRequest, sendRequest } from './use_request'; import { httpService } from './http'; +import { UiMetricService } from './ui_metric'; import { Template } from '../../../common/types'; +import { IndexMgmtMetricsType } from '../../types'; + +// Temporary hack to provide the uiMetricService instance to this file. +// TODO: Refactor and export an ApiService instance through the app dependencies context +let uiMetricService: UiMetricService; +export const setUiMetricService = (_uiMetricService: UiMetricService) => { + uiMetricService = _uiMetricService; +}; +// End hack export async function loadIndices() { const response = await httpService.httpClient.get(`${API_BASE_PATH}/indices`); diff --git a/x-pack/legacy/plugins/index_management/public/app/services/breadcrumbs.ts b/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts similarity index 81% rename from x-pack/legacy/plugins/index_management/public/app/services/breadcrumbs.ts rename to x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts index d35f064a3bcc4..299491756560e 100644 --- a/x-pack/legacy/plugins/index_management/public/app/services/breadcrumbs.ts +++ b/x-pack/legacy/plugins/index_management/public/application/services/breadcrumbs.ts @@ -5,26 +5,25 @@ */ import { i18n } from '@kbn/i18n'; import { BASE_PATH } from '../../../common/constants'; -import { ChromeStart } from '../../../../../../../src/core/public'; +import { ManagementAppMountParams } from '../../../../../../../src/plugins/management/public'; + +type SetBreadcrumbs = ManagementAppMountParams['setBreadcrumbs']; class BreadcrumbService { - private chrome: ChromeStart | undefined; private breadcrumbs: { [key: string]: Array<{ text: string; href?: string; }>; } = { - management: [], home: [], }; + private setBreadcrumbsHandler?: SetBreadcrumbs; - public init(chrome: ChromeStart, managementBreadcrumb: any): void { - this.chrome = chrome; - this.breadcrumbs.management = [managementBreadcrumb]; + public setup(setBreadcrumbsHandler: SetBreadcrumbs): void { + this.setBreadcrumbsHandler = setBreadcrumbsHandler; this.breadcrumbs.home = [ - ...this.breadcrumbs.management, { text: i18n.translate('xpack.idxMgmt.breadcrumb.homeLabel', { defaultMessage: 'Index Management', @@ -72,6 +71,10 @@ class BreadcrumbService { } public setBreadcrumbs(type: string): void { + if (!this.setBreadcrumbsHandler) { + throw new Error(`BreadcrumbService#setup() must be called first!`); + } + const newBreadcrumbs = this.breadcrumbs[type] ? [...this.breadcrumbs[type]] : [...this.breadcrumbs.home]; @@ -88,9 +91,7 @@ class BreadcrumbService { href: undefined, }); - if (this.chrome) { - this.chrome.setBreadcrumbs(newBreadcrumbs); - } + this.setBreadcrumbsHandler(newBreadcrumbs); } } diff --git a/x-pack/legacy/plugins/index_management/public/app/services/documentation.ts b/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts similarity index 99% rename from x-pack/legacy/plugins/index_management/public/app/services/documentation.ts rename to x-pack/legacy/plugins/index_management/public/application/services/documentation.ts index 9bf0d983df161..e0f261e586b1e 100644 --- a/x-pack/legacy/plugins/index_management/public/app/services/documentation.ts +++ b/x-pack/legacy/plugins/index_management/public/application/services/documentation.ts @@ -12,7 +12,7 @@ class DocumentationService { private esDocsBase: string = ''; private kibanaDocsBase: string = ''; - public init(docLinks: DocLinksStart): void { + public setup(docLinks: DocLinksStart): void { const { DOC_LINK_VERSION, ELASTIC_WEBSITE_URL } = docLinks; const docsBase = `${ELASTIC_WEBSITE_URL}guide/en`; diff --git a/x-pack/legacy/plugins/index_management/public/app/services/health_to_color.ts b/x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/services/health_to_color.ts rename to x-pack/legacy/plugins/index_management/public/application/services/health_to_color.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/services/http.ts b/x-pack/legacy/plugins/index_management/public/application/services/http.ts similarity index 86% rename from x-pack/legacy/plugins/index_management/public/app/services/http.ts rename to x-pack/legacy/plugins/index_management/public/application/services/http.ts index 34f5abe5983cc..a6973c263f00f 100644 --- a/x-pack/legacy/plugins/index_management/public/app/services/http.ts +++ b/x-pack/legacy/plugins/index_management/public/application/services/http.ts @@ -6,10 +6,10 @@ import { HttpSetup } from '../../../../../../../src/core/public'; -class HttpService { +export class HttpService { private client: any; - public init(httpClient: HttpSetup): void { + public setup(httpClient: HttpSetup): void { this.client = httpClient; } diff --git a/x-pack/legacy/plugins/index_management/public/app/services/index.ts b/x-pack/legacy/plugins/index_management/public/application/services/index.ts similarity index 82% rename from x-pack/legacy/plugins/index_management/public/app/services/index.ts rename to x-pack/legacy/plugins/index_management/public/application/services/index.ts index bb5c854e5d96c..78ff8cb5c314a 100644 --- a/x-pack/legacy/plugins/index_management/public/app/services/index.ts +++ b/x-pack/legacy/plugins/index_management/public/application/services/index.ts @@ -25,3 +25,7 @@ export { } from './api'; export { healthToColor } from './health_to_color'; export { sortTable } from './sort_table'; + +export { UiMetricService } from './ui_metric'; +export { HttpService } from './http'; +export { NotificationService } from './notification'; diff --git a/x-pack/legacy/plugins/index_management/public/app/services/navigation.ts b/x-pack/legacy/plugins/index_management/public/application/services/navigation.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/services/navigation.ts rename to x-pack/legacy/plugins/index_management/public/application/services/navigation.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/services/notification.ts b/x-pack/legacy/plugins/index_management/public/application/services/notification.ts similarity index 91% rename from x-pack/legacy/plugins/index_management/public/app/services/notification.ts rename to x-pack/legacy/plugins/index_management/public/application/services/notification.ts index 85558a7023491..0971ca77c004b 100644 --- a/x-pack/legacy/plugins/index_management/public/app/services/notification.ts +++ b/x-pack/legacy/plugins/index_management/public/application/services/notification.ts @@ -6,10 +6,10 @@ import { NotificationsStart } from '../../../../../../../src/core/public'; -class NotificationService { +export class NotificationService { private _toasts: any; - public init(notifications: NotificationsStart): void { + public setup(notifications: NotificationsStart): void { this._toasts = notifications.toasts; } diff --git a/x-pack/legacy/plugins/index_management/public/app/services/routing.ts b/x-pack/legacy/plugins/index_management/public/application/services/routing.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/services/routing.ts rename to x-pack/legacy/plugins/index_management/public/application/services/routing.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/services/sort_table.ts b/x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/services/sort_table.ts rename to x-pack/legacy/plugins/index_management/public/application/services/sort_table.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts b/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts new file mode 100644 index 0000000000000..7c87ec9509085 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/services/ui_metric.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { UiStatsMetricType } from '@kbn/analytics'; +import { UsageCollectionSetup } from '../../../../../../../src/plugins/usage_collection/public'; +import { IndexMgmtMetricsType } from '../../types'; + +let uiMetricService: UiMetricService; + +export class UiMetricService { + private appName: string; + private usageCollection: UsageCollectionSetup | undefined; + + constructor(appName: string) { + this.appName = appName; + } + + public setup(usageCollection: UsageCollectionSetup) { + this.usageCollection = usageCollection; + } + + private track(type: T, name: string) { + if (!this.usageCollection) { + throw Error('UiMetricService not initialized.'); + } + this.usageCollection.reportUiStats(this.appName, type as UiStatsMetricType, name); + } + + public trackMetric(type: T, eventName: string) { + return this.track(type, eventName); + } +} + +/** + * To minimize the refactor to migrate to NP where all deps should be explicitely declared + * we will export here the instance created in our plugin.ts setup() so other parts of the code can access it. + * + * TODO: Refactor the api.ts (convert it to a class with setup()) and detail_panel.ts (reducer) to explicitely declare their dependency on the UiMetricService + * @param instance UiMetricService instance from our plugin.ts setup() + */ +export const setUiMetricServiceInstance = (instance: UiMetricService) => { + uiMetricService = instance; +}; + +export const getUiMetricServiceInstance = () => { + return uiMetricService; +}; diff --git a/x-pack/legacy/plugins/index_management/public/app/services/use_request.ts b/x-pack/legacy/plugins/index_management/public/application/services/use_request.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/services/use_request.ts rename to x-pack/legacy/plugins/index_management/public/application/services/use_request.ts diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/clear_cache_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/clear_cache_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/clear_cache_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/clear_row_status.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/clear_row_status.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/clear_row_status.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/close_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/close_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/close_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/delete_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/delete_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/delete_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/detail_panel.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/detail_panel.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/detail_panel.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/edit_index_settings.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/edit_index_settings.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/edit_index_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/extension_action.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/extension_action.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/extension_action.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/flush_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/flush_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/flush_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/forcemerge_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/forcemerge_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/forcemerge_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/freeze_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/freeze_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/freeze_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/index.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/index.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/index.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/index.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/load_index_data.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/load_index_data.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/load_index_data.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/load_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/load_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/load_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/open_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/open_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/open_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/refresh_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/refresh_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/refresh_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/reload_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/reload_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/reload_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/table_state.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/table_state.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/table_state.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/unfreeze_indices.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/unfreeze_indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/unfreeze_indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/actions/update_index_settings.js b/x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/actions/update_index_settings.js rename to x-pack/legacy/plugins/index_management/public/application/store/actions/update_index_settings.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/index.ts b/x-pack/legacy/plugins/index_management/public/application/store/index.ts similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/index.ts rename to x-pack/legacy/plugins/index_management/public/application/store/index.ts diff --git a/x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js b/x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js new file mode 100644 index 0000000000000..c38971a633954 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/store/reducers/detail_panel.js @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { handleActions } from 'redux-actions'; +import { + UIM_DETAIL_PANEL_SUMMARY_TAB, + UIM_DETAIL_PANEL_SETTINGS_TAB, + UIM_DETAIL_PANEL_MAPPING_TAB, + UIM_DETAIL_PANEL_STATS_TAB, + UIM_DETAIL_PANEL_EDIT_SETTINGS_TAB, +} from '../../../../common/constants'; +import { + TAB_SUMMARY, + TAB_SETTINGS, + TAB_MAPPING, + TAB_STATS, + TAB_EDIT_SETTINGS, +} from '../../constants'; +import { openDetailPanel, closeDetailPanel } from '../actions/detail_panel'; +import { loadIndexDataSuccess } from '../actions/load_index_data'; +import { + updateIndexSettingsSuccess, + updateIndexSettingsError, +} from '../actions/update_index_settings'; +import { deleteIndicesSuccess } from '../actions/delete_indices'; + +const defaultState = {}; + +export const getDetailPanelReducer = uiMetricService => + handleActions( + { + [deleteIndicesSuccess](state, action) { + const { indexNames } = action.payload; + const { indexName } = state; + if (indexNames.includes(indexName)) { + return {}; + } else { + return state; + } + }, + [openDetailPanel](state, action) { + const { panelType, indexName, title } = action.payload; + + const panelTypeToUiMetricMap = { + [TAB_SUMMARY]: UIM_DETAIL_PANEL_SUMMARY_TAB, + [TAB_SETTINGS]: UIM_DETAIL_PANEL_SETTINGS_TAB, + [TAB_MAPPING]: UIM_DETAIL_PANEL_MAPPING_TAB, + [TAB_STATS]: UIM_DETAIL_PANEL_STATS_TAB, + [TAB_EDIT_SETTINGS]: UIM_DETAIL_PANEL_EDIT_SETTINGS_TAB, + }; + + if (panelTypeToUiMetricMap[panelType]) { + uiMetricService.trackMetric('count', panelTypeToUiMetricMap[panelType]); + } + + return { + panelType: panelType || state.panelType || TAB_SUMMARY, + indexName, + title, + }; + }, + [closeDetailPanel]() { + return {}; + }, + [loadIndexDataSuccess](state, action) { + const { data } = action.payload; + const newState = { + ...state, + data, + }; + return newState; + }, + [updateIndexSettingsError](state, action) { + const { error } = action.payload; + const newState = { + ...state, + error, + }; + return newState; + }, + [updateIndexSettingsSuccess]() { + return {}; + }, + }, + defaultState + ); diff --git a/x-pack/legacy/plugins/index_management/public/app/store/store.d.ts b/x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js similarity index 82% rename from x-pack/legacy/plugins/index_management/public/app/store/store.d.ts rename to x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js index 4772f2124bfe0..ff2b94b0d36e9 100644 --- a/x-pack/legacy/plugins/index_management/public/app/store/store.d.ts +++ b/x-pack/legacy/plugins/index_management/public/application/store/reducers/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export declare function indexManagementStore(): any; +export { getReducer } from './index_management'; diff --git a/x-pack/legacy/plugins/index_management/public/app/store/reducers/index_management.js b/x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js similarity index 63% rename from x-pack/legacy/plugins/index_management/public/app/store/reducers/index_management.js rename to x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js index 14f270670c0d6..233e6f692ca70 100644 --- a/x-pack/legacy/plugins/index_management/public/app/store/reducers/index_management.js +++ b/x-pack/legacy/plugins/index_management/public/application/store/reducers/index_management.js @@ -5,14 +5,15 @@ */ import { combineReducers } from 'redux'; -import { detailPanel } from './detail_panel'; +import { getDetailPanelReducer } from './detail_panel'; import { indices } from './indices'; import { rowStatus } from './row_status'; import { tableState } from './table_state'; -export const indexManagement = combineReducers({ - indices, - rowStatus, - tableState, - detailPanel, -}); +export const getReducer = ({ uiMetricService }) => + combineReducers({ + indices, + rowStatus, + tableState, + detailPanel: getDetailPanelReducer(uiMetricService), + }); diff --git a/x-pack/legacy/plugins/index_management/public/app/store/reducers/indices.js b/x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/reducers/indices.js rename to x-pack/legacy/plugins/index_management/public/application/store/reducers/indices.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/reducers/row_status.js b/x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/reducers/row_status.js rename to x-pack/legacy/plugins/index_management/public/application/store/reducers/row_status.js diff --git a/x-pack/legacy/plugins/index_management/public/app/store/reducers/table_state.js b/x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js similarity index 100% rename from x-pack/legacy/plugins/index_management/public/app/store/reducers/table_state.js rename to x-pack/legacy/plugins/index_management/public/application/store/reducers/table_state.js diff --git a/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts b/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts new file mode 100644 index 0000000000000..999b11c5d6665 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.d.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { ExtensionsService } from '../../../services'; + +export declare function setExtensionsService(extensionsService: ExtensionsService): any; diff --git a/x-pack/legacy/plugins/index_management/public/app/store/selectors/index.js b/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js similarity index 93% rename from x-pack/legacy/plugins/index_management/public/app/store/selectors/index.js rename to x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js index 654e7276bbb08..3ddbf28651fa0 100644 --- a/x-pack/legacy/plugins/index_management/public/app/store/selectors/index.js +++ b/x-pack/legacy/plugins/index_management/public/application/store/selectors/index.js @@ -8,7 +8,15 @@ import { Pager, EuiSearchBar } from '@elastic/eui'; import { createSelector } from 'reselect'; import { indexStatusLabels } from '../../lib/index_status_labels'; import { sortTable } from '../../services'; -import { getToggleExtensions } from '../../../index_management_extensions'; + +// Temporary hack to provide the extensionsService instance to this file. +// TODO: Refactor and export all the app selectors through the app dependencies context + +let extensionsService; +export const setExtensionsService = _extensionsService => { + extensionsService = _extensionsService; +}; +// End hack export const getDetailPanelData = state => state.detailPanel.data; export const getDetailPanelError = state => state.detailPanel.error; @@ -46,7 +54,7 @@ export const hasSystemIndex = indexNames => { const defaultFilterFields = ['name']; const filterByToggles = (indices, toggleNameToVisibleMap) => { - const togglesByName = getToggleExtensions().reduce( + const togglesByName = extensionsService.toggles.reduce( (byName, toggle) => ({ ...byName, [toggle.name]: toggle, diff --git a/x-pack/legacy/plugins/index_management/public/application/store/store.d.ts b/x-pack/legacy/plugins/index_management/public/application/store/store.d.ts new file mode 100644 index 0000000000000..1568275badeee --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/application/store/store.d.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AppDependencies } from '../app_context'; + +export declare function indexManagementStore(services: AppDependencies['services']): any; diff --git a/x-pack/legacy/plugins/index_management/public/app/store/store.js b/x-pack/legacy/plugins/index_management/public/application/store/store.js similarity index 71% rename from x-pack/legacy/plugins/index_management/public/app/store/store.js rename to x-pack/legacy/plugins/index_management/public/application/store/store.js index 3e80274a96960..e103ff0778be1 100644 --- a/x-pack/legacy/plugins/index_management/public/app/store/store.js +++ b/x-pack/legacy/plugins/index_management/public/application/store/store.js @@ -6,19 +6,18 @@ import { createStore, applyMiddleware, compose } from 'redux'; import thunk from 'redux-thunk'; -import { getToggleExtensions } from '../../index_management_extensions'; import { defaultTableState } from './reducers/table_state'; -import { indexManagement } from './reducers/'; +import { getReducer } from './reducers/'; -export function indexManagementStore() { +export function indexManagementStore(services) { const toggleNameToVisibleMap = {}; - getToggleExtensions().forEach(toggleExtension => { + services.extensionsService.toggles.forEach(toggleExtension => { toggleNameToVisibleMap[toggleExtension.name] = false; }); const initialState = { tableState: { ...defaultTableState, toggleNameToVisibleMap } }; const enhancers = [applyMiddleware(thunk)]; window.__REDUX_DEVTOOLS_EXTENSION__ && enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); - return createStore(indexManagement, initialState, compose(...enhancers)); + return createStore(getReducer(services), initialState, compose(...enhancers)); } diff --git a/x-pack/legacy/plugins/index_management/public/index.scss b/x-pack/legacy/plugins/index_management/public/index.scss index 7128e4a207ca1..0fbf8ea5036c5 100644 --- a/x-pack/legacy/plugins/index_management/public/index.scss +++ b/x-pack/legacy/plugins/index_management/public/index.scss @@ -10,7 +10,7 @@ // indChart__legend--small // indChart__legend-isLoading -@import './app/components/mappings_editor/index'; +@import './application/components/mappings_editor/index'; .indTable { // The index table is a bespoke table and can't make use of EuiBasicTable's width settings diff --git a/x-pack/legacy/plugins/index_management/public/index.ts b/x-pack/legacy/plugins/index_management/public/index.ts index 3003c4e909956..16e7bf21aee98 100644 --- a/x-pack/legacy/plugins/index_management/public/index.ts +++ b/x-pack/legacy/plugins/index_management/public/index.ts @@ -3,43 +3,19 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from 'ui/new_platform'; +import { npSetup } from 'ui/new_platform'; -import { IndexMgmtPlugin } from './plugin'; -import { __LEGACYStart } from './legacy'; -import { - addSummaryExtension, - getSummaryExtensions, - addActionExtension, - getActionExtensions, - addBannerExtension, - getBannerExtensions, - addFilterExtension, - getFilterExtensions, - addToggleExtension, - getToggleExtensions, - addBadgeExtension, - getBadgeExtensions, -} from './index_management_extensions'; +import { IndexMgmtUIPlugin, IndexMgmtSetup } from './plugin'; + +/** @public */ +export { IndexMgmtSetup }; export const plugin = () => { - return new IndexMgmtPlugin(); + return new IndexMgmtUIPlugin(); }; -/** @public */ -export { - addSummaryExtension, - getSummaryExtensions, - addActionExtension, - getActionExtensions, - addBannerExtension, - getBannerExtensions, - addFilterExtension, - getFilterExtensions, - addToggleExtension, - getToggleExtensions, - addBadgeExtension, - getBadgeExtensions, -}; +// Temp. To be removed after moving to the "plugins" folder + +const { extensionsService } = plugin().setup(npSetup.core, npSetup.plugins); -plugin().start(npStart.core, npStart.plugins, __LEGACYStart); +export { extensionsService }; diff --git a/x-pack/legacy/plugins/index_management/public/index_management_extensions.ts b/x-pack/legacy/plugins/index_management/public/index_management_extensions.ts deleted file mode 100644 index 2f578e7c4a5a4..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/index_management_extensions.ts +++ /dev/null @@ -1,72 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { i18n } from '@kbn/i18n'; - -const summaryExtensions: any[] = []; -export const addSummaryExtension = (summaryExtension: any) => { - summaryExtensions.push(summaryExtension); -}; - -export const getSummaryExtensions = () => { - return summaryExtensions; -}; - -const actionExtensions: any[] = []; -export const addActionExtension = (actionExtension: any) => { - actionExtensions.push(actionExtension); -}; - -export const getActionExtensions = () => { - return actionExtensions; -}; - -const bannerExtensions: any[] = []; -export const addBannerExtension = (actionExtension: any) => { - bannerExtensions.push(actionExtension); -}; - -export const getBannerExtensions = () => { - return bannerExtensions; -}; - -const filterExtensions: any[] = []; -export const addFilterExtension = (filterExtension: any) => { - filterExtensions.push(filterExtension); -}; - -export const getFilterExtensions = () => { - return filterExtensions; -}; - -const toggleExtensions: any[] = []; -export const addToggleExtension = (toggleExtension: any) => { - toggleExtensions.push(toggleExtension); -}; - -export const getToggleExtensions = () => { - return toggleExtensions; -}; - -const badgeExtensions = [ - { - matchIndex: (index: { isFrozen: boolean }) => { - return index.isFrozen; - }, - label: i18n.translate('xpack.idxMgmt.frozenBadgeLabel', { - defaultMessage: 'Frozen', - }), - filterExpression: 'isFrozen:true', - color: 'primary', - }, -]; - -export const addBadgeExtension = (badgeExtension: any) => { - badgeExtensions.push(badgeExtension); -}; - -export const getBadgeExtensions = () => { - return badgeExtensions; -}; diff --git a/x-pack/legacy/plugins/index_management/public/legacy.ts b/x-pack/legacy/plugins/index_management/public/legacy.ts deleted file mode 100644 index eee24feff5a67..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/legacy.ts +++ /dev/null @@ -1,31 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { management, MANAGEMENT_BREADCRUMB } from 'ui/management'; -import { createUiStatsReporter } from '../../../../../src/legacy/core_plugins/ui_metric/public'; - -export interface LegacyStart { - management: { - getSection: typeof management.getSection; - constants: { - BREADCRUMB: typeof MANAGEMENT_BREADCRUMB; - }; - }; - uiMetric: { - createUiStatsReporter: typeof createUiStatsReporter; - }; -} - -export const __LEGACYStart = { - management: { - getSection: management.getSection.bind(management), - constants: { - BREADCRUMB: MANAGEMENT_BREADCRUMB, - }, - }, - uiMetric: { - createUiStatsReporter, - }, -}; diff --git a/x-pack/legacy/plugins/index_management/public/mocks.ts b/x-pack/legacy/plugins/index_management/public/mocks.ts new file mode 100644 index 0000000000000..7062e7bacace0 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/mocks.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { extensionsServiceMock } from './services/extensions_service.mock'; + +export { extensionsServiceMock } from './services/extensions_service.mock'; + +function createIdxManagementSetupMock() { + const mock = { + extensionsService: extensionsServiceMock, + }; + + return mock; +} + +export const indexManagementMock = { + createSetup: createIdxManagementSetupMock, +}; diff --git a/x-pack/legacy/plugins/index_management/public/plugin.ts b/x-pack/legacy/plugins/index_management/public/plugin.ts index 71ecdd7a1fbef..539324766cf95 100644 --- a/x-pack/legacy/plugins/index_management/public/plugin.ts +++ b/x-pack/legacy/plugins/index_management/public/plugin.ts @@ -3,32 +3,90 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { CoreStart } from '../../../../../src/core/public'; - -import { registerManagementSection } from './register_management_section'; -import { registerRoutes } from './register_routes'; -import { LegacyStart } from './legacy'; - -import { httpService } from './app/services/http'; -import { breadcrumbService } from './app/services/breadcrumbs'; -import { documentationService } from './app/services/documentation'; -import { notificationService } from './app/services/notification'; -import { uiMetricService } from './app/services/ui_metric'; - -export class IndexMgmtPlugin { - public start(core: CoreStart, plugins: {}, __LEGACY: LegacyStart) { - const { management, uiMetric } = __LEGACY; - const { http, chrome, docLinks, notifications } = core; - - // Initialize services - httpService.init(http); - breadcrumbService.init(chrome, management.constants.BREADCRUMB); - documentationService.init(docLinks); - notificationService.init(notifications); - uiMetricService.init(uiMetric.createUiStatsReporter); - - // Register management section and Angular route - registerManagementSection(management.getSection('elasticsearch')); - registerRoutes(core); +import { i18n } from '@kbn/i18n'; + +import { CoreSetup } from '../../../../../src/core/public'; +import { UsageCollectionSetup } from '../../../../../src/plugins/usage_collection/public'; +import { ManagementSetup } from '../../../../../src/plugins/management/public'; +import { UIM_APP_NAME } from '../common/constants'; + +import { AppDependencies } from './application'; +import { httpService } from './application/services/http'; +import { breadcrumbService } from './application/services/breadcrumbs'; +import { documentationService } from './application/services/documentation'; +import { notificationService } from './application/services/notification'; +import { UiMetricService } from './application/services/ui_metric'; + +import { setExtensionsService } from './application/store/selectors'; +import { setUiMetricService } from './application/services/api'; + +import { IndexMgmtMetricsType } from './types'; +import { ExtensionsService, ExtensionsSetup } from './services'; + +export interface IndexMgmtSetup { + extensionsService: ExtensionsSetup; +} + +interface PluginsDependencies { + usageCollection: UsageCollectionSetup; + management: ManagementSetup; +} + +export class IndexMgmtUIPlugin { + private uiMetricService = new UiMetricService(UIM_APP_NAME); + private extensionsService = new ExtensionsService(); + + constructor() { + // Temporary hack to provide the service instances in module files in order to avoid a big refactor + // For the selectors we should expose them through app dependencies and read them from there on each container component. + setExtensionsService(this.extensionsService); + setUiMetricService(this.uiMetricService); } + + public setup(coreSetup: CoreSetup, plugins: PluginsDependencies): IndexMgmtSetup { + const { http, notifications, getStartServices } = coreSetup; + const { usageCollection, management } = plugins; + + httpService.setup(http); + notificationService.setup(notifications); + this.uiMetricService.setup(usageCollection); + + management.sections.getSection('elasticsearch')!.registerApp({ + id: 'index_management', + title: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), + order: 1, + mount: async ({ element, setBreadcrumbs }) => { + const [core] = await getStartServices(); + const { docLinks, fatalErrors } = core; + + breadcrumbService.setup(setBreadcrumbs); + documentationService.setup(docLinks); + + const appDependencies: AppDependencies = { + core: { + fatalErrors, + }, + plugins: { + usageCollection, + }, + services: { + uiMetricService: this.uiMetricService, + extensionsService: this.extensionsService, + httpService, + notificationService, + }, + }; + + const { renderApp } = await import('./application'); + return renderApp(element, { core, dependencies: appDependencies }); + }, + }); + + return { + extensionsService: this.extensionsService.setup(), + }; + } + + public start() {} + public stop() {} } diff --git a/x-pack/legacy/plugins/index_management/public/register_management_section.ts b/x-pack/legacy/plugins/index_management/public/register_management_section.ts deleted file mode 100644 index f090b322583d2..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/register_management_section.ts +++ /dev/null @@ -1,19 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { i18n } from '@kbn/i18n'; -import { BASE_PATH } from '../common/constants'; - -export const registerManagementSection = (esSection: { - register: (section: string, config: {}) => void; -}) => { - esSection.register('index_management', { - visible: true, - display: i18n.translate('xpack.idxMgmt.appTitle', { defaultMessage: 'Index Management' }), - order: 1, - url: `#${BASE_PATH}indices`, - }); -}; diff --git a/x-pack/legacy/plugins/index_management/public/register_routes.ts b/x-pack/legacy/plugins/index_management/public/register_routes.ts deleted file mode 100644 index 378a53cf65555..0000000000000 --- a/x-pack/legacy/plugins/index_management/public/register_routes.ts +++ /dev/null @@ -1,34 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import routes from 'ui/routes'; - -import { CoreStart } from '../../../../../src/core/public'; - -import { mountReactApp, unmountReactApp } from './app'; -import { REACT_ROOT_ID } from './app/constants'; -import { BASE_PATH } from '../common/constants'; - -import template from './index.html'; -import { manageAngularLifecycle } from './app/lib/manage_angular_lifecycle'; - -let elem: HTMLElement | null; - -export const registerRoutes = (core: CoreStart) => { - routes.when(`${BASE_PATH}:view?/:action?/:id?`, { - template, - controller: ($scope: any, $route: any) => { - // clean up previously rendered React app if one exists - // this happens because of React Router redirects - unmountReactApp(elem); - - $scope.$$postDigest(() => { - elem = document.getElementById(REACT_ROOT_ID); - mountReactApp(elem, { core }); - manageAngularLifecycle($scope, $route, elem); - }); - }, - }); -}; diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts b/x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts new file mode 100644 index 0000000000000..3075a114eedd1 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/services/extensions_service.mock.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ExtensionsService, ExtensionsSetup } from './extensions_service'; + +export type ExtensionsSetupMock = jest.Mocked; + +const createServiceMock = (): ExtensionsSetupMock => ({ + addAction: jest.fn(), + addBadge: jest.fn(), + addBanner: jest.fn(), + addFilter: jest.fn(), + addSummary: jest.fn(), + addToggle: jest.fn(), +}); + +const createMock = () => { + const mocked: jest.Mocked> = { + setup: jest.fn(), + }; + mocked.setup.mockReturnValue(createServiceMock()); + return mocked; +}; + +export const extensionsServiceMock = { + create: createMock, + createSetupContract: createServiceMock, +}; diff --git a/x-pack/legacy/plugins/index_management/public/services/extensions_service.ts b/x-pack/legacy/plugins/index_management/public/services/extensions_service.ts new file mode 100644 index 0000000000000..22e0546beff99 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/services/extensions_service.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; + +export interface ExtensionsSetup { + addSummary(summary: any): void; + addAction(action: any): void; + addBanner(banner: any): void; + addFilter(filter: any): void; + addBadge(badge: any): void; + addToggle(toggle: any): void; +} + +export class ExtensionsService { + private _summaries: any[] = []; + private _actions: any[] = []; + private _banners: any[] = []; + private _filters: any[] = []; + private _badges: any[] = [ + { + matchIndex: (index: { isFrozen: boolean }) => { + return index.isFrozen; + }, + label: i18n.translate('xpack.idxMgmt.frozenBadgeLabel', { + defaultMessage: 'Frozen', + }), + filterExpression: 'isFrozen:true', + color: 'primary', + }, + ]; + private _toggles: any[] = []; + private service?: ExtensionsSetup; + + public setup(): ExtensionsSetup { + this.service = { + addAction: this.addAction.bind(this), + addBadge: this.addBadge.bind(this), + addBanner: this.addBanner.bind(this), + addFilter: this.addFilter.bind(this), + addSummary: this.addSummary.bind(this), + addToggle: this.addToggle.bind(this), + }; + + return this.service; + } + + private addSummary(summary: any) { + this._summaries.push(summary); + } + + private addAction(action: any) { + this._actions.push(action); + } + + private addBanner(banner: any) { + this._banners.push(banner); + } + + private addFilter(filter: any) { + this._filters.push(filter); + } + + private addBadge(badge: any) { + this._badges.push(badge); + } + + private addToggle(toggle: any) { + this._toggles.push(toggle); + } + + public get summaries() { + return this._summaries; + } + + public get actions() { + return this._actions; + } + + public get banners() { + return this._banners; + } + + public get filters() { + return this._filters; + } + + public get badges() { + return this._badges; + } + + public get toggles() { + return this._toggles; + } +} diff --git a/x-pack/legacy/plugins/index_management/public/services/index.ts b/x-pack/legacy/plugins/index_management/public/services/index.ts new file mode 100644 index 0000000000000..e5dfabe1d1190 --- /dev/null +++ b/x-pack/legacy/plugins/index_management/public/services/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ExtensionsService, ExtensionsSetup } from './extensions_service'; diff --git a/x-pack/legacy/plugins/index_management/public/app/store/reducers/index.js b/x-pack/legacy/plugins/index_management/public/types.ts similarity index 78% rename from x-pack/legacy/plugins/index_management/public/app/store/reducers/index.js rename to x-pack/legacy/plugins/index_management/public/types.ts index eeae581be9473..08fbf63450218 100644 --- a/x-pack/legacy/plugins/index_management/public/app/store/reducers/index.js +++ b/x-pack/legacy/plugins/index_management/public/types.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { indexManagement } from './index_management'; +export type IndexMgmtMetricsType = 'loaded' | 'click' | 'count'; diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts index 17597672a898e..64eb1f6436389 100644 --- a/x-pack/legacy/plugins/rollup/public/legacy.ts +++ b/x-pack/legacy/plugins/rollup/public/legacy.ts @@ -10,7 +10,7 @@ import { aggTypeFieldFilters } from 'ui/agg_types'; import { addSearchStrategy } from '../../../../../src/plugins/data/public'; import { RollupPlugin } from './plugin'; import { setup as management } from '../../../../../src/legacy/core_plugins/management/public/legacy'; -import { addBadgeExtension, addToggleExtension } from '../../index_management/public'; +import { extensionsService } from '../../index_management/public'; const plugin = new RollupPlugin(); @@ -20,8 +20,7 @@ export const setup = plugin.setup(npSetup.core, { aggTypeFilters, aggTypeFieldFilters, addSearchStrategy, - addBadgeExtension, - addToggleExtension, + indexManagementExtensions: extensionsService, managementLegacy: management, }, }); diff --git a/x-pack/legacy/plugins/rollup/public/plugin.ts b/x-pack/legacy/plugins/rollup/public/plugin.ts index 4dae078c90f57..90d7e2d9d0191 100644 --- a/x-pack/legacy/plugins/rollup/public/plugin.ts +++ b/x-pack/legacy/plugins/rollup/public/plugin.ts @@ -27,6 +27,7 @@ import { // @ts-ignore import { CRUD_APP_BASE_PATH } from './crud_app/constants'; import { ManagementSetup } from '../../../../../src/plugins/management/public'; +import { IndexMgmtSetup } from '../../index_management/public'; // @ts-ignore import { setEsBaseAndXPackBase, setHttp } from './crud_app/services'; import { setNotifications, setFatalErrors } from './kibana_services'; @@ -38,8 +39,7 @@ export interface RollupPluginSetupDependencies { aggTypeFieldFilters: AggTypeFieldFilters; addSearchStrategy: (searchStrategy: SearchStrategyProvider) => void; managementLegacy: ManagementSetupLegacy; - addBadgeExtension: (badgeExtension: any) => void; - addToggleExtension: (toggleExtension: any) => void; + indexManagementExtensions: IndexMgmtSetup['extensionsService']; }; home?: HomePublicPluginSetup; management: ManagementSetup; @@ -54,16 +54,15 @@ export class RollupPlugin implements Plugin { aggTypeFieldFilters, addSearchStrategy, managementLegacy, - addBadgeExtension, - addToggleExtension, + indexManagementExtensions, }, home, management, }: RollupPluginSetupDependencies ) { setFatalErrors(core.fatalErrors); - addBadgeExtension(rollupBadgeExtension); - addToggleExtension(rollupToggleExtension); + indexManagementExtensions.addBadge(rollupBadgeExtension); + indexManagementExtensions.addToggle(rollupToggleExtension); const isRollupIndexPatternsEnabled = core.uiSettings.get(CONFIG_ROLLUPS); From f4d29abf1cd8763396bb9fbb1e19df73f31132c8 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Mon, 17 Feb 2020 09:58:54 +0100 Subject: [PATCH 22/65] Bump redux dependencies (#53348) --- package.json | 31 +- packages/kbn-pm/dist/index.js | 5298 +++++++---------- .../public/utils/typed_react.ts | 47 - .../workpad_app/workpad_telemetry.test.tsx | 13 +- .../workpad/workpad_app/workpad_telemetry.tsx | 37 +- .../canvas/public/components/router/router.js | 6 +- .../sidebar/element_settings/index.tsx | 5 - .../field_manager/field_manager.test.tsx | 4 +- .../field_manager/field_manager.tsx | 2 + .../components/logging/log_time_controls.tsx | 2 +- .../waffle/waffle_time_controls.tsx | 5 +- .../infra/public/utils/redux_context.tsx | 10 +- .../infra/public/utils/typed_react.tsx | 13 +- .../layer_addpanel/index.js | 2 +- .../connected_components/map/mb/index.js | 2 +- .../layer_control/layer_toc/index.js | 2 +- .../population_view/metric_selection.tsx | 5 +- .../jobs/new_job/pages/new_job/wizard.tsx | 10 +- .../drag_drop_context_wrapper.tsx | 11 +- .../drag_and_drop/draggable_wrapper.tsx | 24 +- .../error_toast_dispatcher/index.tsx | 25 +- .../public/components/events_viewer/index.tsx | 67 +- .../components/fields_browser/index.test.tsx | 29 + .../components/fields_browser/index.tsx | 27 +- .../public/components/flyout/header/index.tsx | 91 +- .../siem/public/components/flyout/index.tsx | 44 +- .../public/components/flyout/pane/index.tsx | 25 +- .../components/open_timeline/helpers.ts | 10 +- .../public/components/open_timeline/index.tsx | 16 +- .../public/components/open_timeline/types.ts | 17 - .../hosts/authentications_table/index.tsx | 35 +- .../page/hosts/hosts_table/index.tsx | 39 +- .../hosts/uncommon_process_table/index.tsx | 35 +- .../page/network/network_dns_table/index.tsx | 30 +- .../__snapshots__/index.test.tsx.snap | 2 +- .../network/network_http_table/index.test.tsx | 2 +- .../page/network/network_http_table/index.tsx | 201 +- .../network_top_countries_table/index.tsx | 36 +- .../network_top_n_flow_table/index.tsx | 36 +- .../page/network/tls_table/index.tsx | 34 +- .../page/network/users_table/index.tsx | 29 +- .../components/recent_timelines/index.tsx | 20 +- .../public/components/search_bar/index.tsx | 43 +- .../components/super_date_picker/index.tsx | 41 +- .../timeline/auto_save_warning/index.tsx | 38 +- .../components/timeline/body/index.test.tsx | 6 - .../public/components/timeline/body/index.tsx | 1 - .../timeline/body/stateful_body.tsx | 74 +- .../timeline/fetch_kql_timeline.tsx | 32 +- .../siem/public/components/timeline/index.tsx | 128 +- .../components/timeline/refetch_timeline.tsx | 32 +- .../timeline/search_or_filter/index.tsx | 64 +- .../public/components/timeline/timeline.tsx | 2 +- .../public/containers/global_time/index.tsx | 30 +- .../public/containers/ip_overview/index.tsx | 14 +- .../containers/kpi_host_details/index.tsx | 14 +- .../public/containers/kpi_hosts/index.tsx | 14 +- .../public/containers/kpi_network/index.tsx | 14 +- .../overview/overview_host/index.tsx | 14 +- .../overview/overview_network/index.tsx | 74 +- .../siem/public/containers/timeline/index.tsx | 15 +- .../containers/uncommon_processes/index.tsx | 16 +- .../siem/public/containers/users/index.tsx | 25 +- .../components/signals/index.tsx | 45 +- .../detection_engine/detection_engine.tsx | 33 +- .../detection_engine/rules/details/index.tsx | 32 +- .../plugins/siem/public/pages/home/index.tsx | 4 +- .../pages/hosts/details/details_tabs.tsx | 4 +- .../siem/public/pages/hosts/details/index.tsx | 25 +- .../siem/public/pages/hosts/hosts.test.tsx | 9 - .../plugins/siem/public/pages/hosts/hosts.tsx | 27 +- .../plugins/siem/public/pages/hosts/index.tsx | 8 +- .../plugins/siem/public/pages/hosts/types.ts | 28 +- .../pages/network/ip_details/index.test.tsx | 2 +- .../public/pages/network/ip_details/index.tsx | 19 +- .../public/pages/network/ip_details/types.ts | 26 +- .../siem/public/pages/network/network.tsx | 14 +- .../siem/public/pages/network/types.ts | 14 +- .../siem/public/pages/overview/overview.tsx | 22 +- .../siem/public/store/inputs/actions.ts | 4 +- .../siem/public/store/inputs/helpers.ts | 4 +- .../siem/public/store/network/selectors.ts | 9 +- .../functional/charts/ping_histogram.tsx | 2 +- .../contexts/uptime_refresh_context.tsx | 1 + .../plugins/uptime/public/uptime_app.tsx | 1 + x-pack/package.json | 37 +- yarn.lock | 481 +- 87 files changed, 2967 insertions(+), 4954 deletions(-) diff --git a/package.json b/package.json index e801f75b976f8..e4fdaf32a014a 100644 --- a/package.json +++ b/package.json @@ -80,12 +80,14 @@ }, "resolutions": { "**/@types/node": "10.12.27", - "**/@types/react": "^16.9.13", + "**/@types/react": "^16.9.19", "**/@types/react-router": "^5.1.3", "**/@types/hapi": "^17.0.18", "**/@types/angular": "^1.6.56", + "**/@types/hoist-non-react-statics": "^3.3.1", "**/typescript": "3.7.2", "**/graphql-toolkit/lodash": "^4.17.13", + "**/hoist-non-react-statics": "^3.3.2", "**/isomorphic-git/**/base64-js": "^1.2.1", "**/image-diff/gm/debug": "^2.6.9", "**/react-dom": "^16.12.0", @@ -234,19 +236,19 @@ "react-input-range": "^1.3.0", "react-markdown": "^3.4.1", "react-monaco-editor": "~0.27.0", - "react-redux": "^5.1.2", + "react-redux": "^7.1.3", "react-resize-detector": "^4.2.0", "react-router-dom": "^5.1.2", "react-sizeme": "^2.3.6", "react-use": "^13.13.0", "reactcss": "1.2.3", - "redux": "4.0.0", - "redux-actions": "2.6.5", - "redux-thunk": "2.3.0", + "redux": "^4.0.5", + "redux-actions": "^2.6.5", + "redux-thunk": "^2.3.0", "regenerator-runtime": "^0.13.3", "regression": "2.0.1", "request": "^2.88.0", - "reselect": "^3.0.1", + "reselect": "^4.0.0", "resize-observer-polyfill": "^1.5.0", "rison-node": "1.0.2", "rxjs": "^6.5.3", @@ -318,7 +320,7 @@ "@types/deep-freeze-strict": "^1.1.0", "@types/delete-empty": "^2.0.0", "@types/elasticsearch": "^5.0.33", - "@types/enzyme": "^3.9.0", + "@types/enzyme": "^3.10.5", "@types/eslint": "^6.1.3", "@types/fetch-mock": "^7.3.1", "@types/flot": "^0.0.31", @@ -356,16 +358,15 @@ "@types/podium": "^1.0.0", "@types/prop-types": "^15.5.3", "@types/reach__router": "^1.2.6", - "@types/react": "^16.9.11", - "@types/react-dom": "^16.9.4", + "@types/react": "^16.9.19", + "@types/react-dom": "^16.9.5", "@types/react-grid-layout": "^0.16.7", - "@types/react-redux": "^6.0.6", + "@types/react-redux": "^7.1.7", "@types/react-resize-detector": "^4.0.1", "@types/react-router": "^5.1.3", "@types/react-router-dom": "^5.1.3", "@types/react-virtualized": "^9.18.7", "@types/recompose": "^0.30.6", - "@types/redux": "^3.6.31", "@types/redux-actions": "^2.6.1", "@types/request": "^2.48.2", "@types/selenium-webdriver": "^4.0.5", @@ -398,10 +399,10 @@ "classnames": "2.2.6", "dedent": "^0.7.0", "delete-empty": "^2.0.0", - "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.15.1", - "enzyme-adapter-utils": "^1.12.1", - "enzyme-to-json": "^3.4.3", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.2", + "enzyme-adapter-utils": "^1.13.0", + "enzyme-to-json": "^3.4.4", "eslint": "^6.8.0", "eslint-config-prettier": "^6.9.0", "eslint-plugin-babel": "^5.3.0", diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 314bcf31e6d05..2ccff3e9c3df2 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _cli__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "run", function() { return _cli__WEBPACK_IMPORTED_MODULE_0__["run"]; }); -/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(705); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(704); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); @@ -105,10 +105,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(516); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(579); +/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(578); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__["copyWorkspacePackages"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(580); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(579); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -152,7 +152,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(690); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(689); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(34); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -2506,9 +2506,9 @@ module.exports = require("path"); __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "commands", function() { return commands; }); /* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(587); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(687); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(688); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(586); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(686); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(687); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -2551,8 +2551,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(34); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(500); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(501); -/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(581); -/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(586); +/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(580); +/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(585); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -44027,7 +44027,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(515); /* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(516); -/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(579); +/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(578); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -47547,7 +47547,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(515); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(34); /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(517); -/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(564); +/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(563); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -48399,33 +48399,38 @@ function bugsTypos(bugs, warn) { /* 522 */ /***/ (function(module, exports) { -exports = module.exports = SemVer; - -// The debug function is excluded entirely from the minified version. -/* nomin */ var debug; -/* nomin */ if (typeof process === 'object' && - /* nomin */ process.env && - /* nomin */ process.env.NODE_DEBUG && - /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) - /* nomin */ debug = function() { - /* nomin */ var args = Array.prototype.slice.call(arguments, 0); - /* nomin */ args.unshift('SEMVER'); - /* nomin */ console.log.apply(console, args); - /* nomin */ }; -/* nomin */ else - /* nomin */ debug = function() {}; +exports = module.exports = SemVer + +var debug +/* istanbul ignore next */ +if (typeof process === 'object' && + process.env && + process.env.NODE_DEBUG && + /\bsemver\b/i.test(process.env.NODE_DEBUG)) { + debug = function () { + var args = Array.prototype.slice.call(arguments, 0) + args.unshift('SEMVER') + console.log.apply(console, args) + } +} else { + debug = function () {} +} // Note: this is the semver.org version of the spec that it implements // Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0'; +exports.SEMVER_SPEC_VERSION = '2.0.0' + +var MAX_LENGTH = 256 +var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || + /* istanbul ignore next */ 9007199254740991 -var MAX_LENGTH = 256; -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; +// Max safe segment length for coercion. +var MAX_SAFE_COMPONENT_LENGTH = 16 // The actual regexps go on exports.re -var re = exports.re = []; -var src = exports.src = []; -var R = 0; +var re = exports.re = [] +var src = exports.src = [] +var R = 0 // The following Regular Expressions can be used for tokenizing, // validating, and parsing SemVer version strings. @@ -48433,71 +48438,67 @@ var R = 0; // ## Numeric Identifier // A single `0`, or a non-zero digit followed by zero or more digits. -var NUMERICIDENTIFIER = R++; -src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; -var NUMERICIDENTIFIERLOOSE = R++; -src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; - +var NUMERICIDENTIFIER = R++ +src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' +var NUMERICIDENTIFIERLOOSE = R++ +src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' // ## Non-numeric Identifier // Zero or more digits, followed by a letter or hyphen, and then zero or // more letters, digits, or hyphens. -var NONNUMERICIDENTIFIER = R++; -src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; - +var NONNUMERICIDENTIFIER = R++ +src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' // ## Main Version // Three dot-separated numeric identifiers. -var MAINVERSION = R++; +var MAINVERSION = R++ src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')'; + '(' + src[NUMERICIDENTIFIER] + ')' -var MAINVERSIONLOOSE = R++; +var MAINVERSIONLOOSE = R++ src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; + '(' + src[NUMERICIDENTIFIERLOOSE] + ')' // ## Pre-release Version Identifier // A numeric identifier, or a non-numeric identifier. -var PRERELEASEIDENTIFIER = R++; +var PRERELEASEIDENTIFIER = R++ src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + - '|' + src[NONNUMERICIDENTIFIER] + ')'; + '|' + src[NONNUMERICIDENTIFIER] + ')' -var PRERELEASEIDENTIFIERLOOSE = R++; +var PRERELEASEIDENTIFIERLOOSE = R++ src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + - '|' + src[NONNUMERICIDENTIFIER] + ')'; - + '|' + src[NONNUMERICIDENTIFIER] + ')' // ## Pre-release Version // Hyphen, followed by one or more dot-separated pre-release version // identifiers. -var PRERELEASE = R++; +var PRERELEASE = R++ src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + - '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' -var PRERELEASELOOSE = R++; +var PRERELEASELOOSE = R++ src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' // ## Build Metadata Identifier // Any combination of digits, letters, or hyphens. -var BUILDIDENTIFIER = R++; -src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; +var BUILDIDENTIFIER = R++ +src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' // ## Build Metadata // Plus sign, followed by one or more period-separated build metadata // identifiers. -var BUILD = R++; +var BUILD = R++ src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + - '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; - + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' // ## Full Version String // A main version, followed optionally by a pre-release version and @@ -48508,774 +48509,870 @@ src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + // capturing group, because it should not ever be used in version // comparison. -var FULL = R++; +var FULL = R++ var FULLPLAIN = 'v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + - src[BUILD] + '?'; + src[BUILD] + '?' -src[FULL] = '^' + FULLPLAIN + '$'; +src[FULL] = '^' + FULLPLAIN + '$' // like full, but allows v1.2.3 and =1.2.3, which people do sometimes. // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty // common in the npm registry. var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + '?' + - src[BUILD] + '?'; + src[BUILD] + '?' -var LOOSE = R++; -src[LOOSE] = '^' + LOOSEPLAIN + '$'; +var LOOSE = R++ +src[LOOSE] = '^' + LOOSEPLAIN + '$' -var GTLT = R++; -src[GTLT] = '((?:<|>)?=?)'; +var GTLT = R++ +src[GTLT] = '((?:<|>)?=?)' // Something like "2.*" or "1.2.x". // Note that "x.x" is a valid xRange identifer, meaning "any version" // Only the first item is strictly required. -var XRANGEIDENTIFIERLOOSE = R++; -src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; -var XRANGEIDENTIFIER = R++; -src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; +var XRANGEIDENTIFIERLOOSE = R++ +src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' +var XRANGEIDENTIFIER = R++ +src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' -var XRANGEPLAIN = R++; +var XRANGEPLAIN = R++ src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:' + src[PRERELEASE] + ')?' + src[BUILD] + '?' + - ')?)?'; + ')?)?' -var XRANGEPLAINLOOSE = R++; +var XRANGEPLAINLOOSE = R++ src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:' + src[PRERELEASELOOSE] + ')?' + src[BUILD] + '?' + - ')?)?'; + ')?)?' + +var XRANGE = R++ +src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' +var XRANGELOOSE = R++ +src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' -var XRANGE = R++; -src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; -var XRANGELOOSE = R++; -src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; +// Coercion. +// Extract anything that could conceivably be a part of a valid semver +var COERCE = R++ +src[COERCE] = '(?:^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])' // Tilde ranges. // Meaning is "reasonably at or greater than" -var LONETILDE = R++; -src[LONETILDE] = '(?:~>?)'; +var LONETILDE = R++ +src[LONETILDE] = '(?:~>?)' -var TILDETRIM = R++; -src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; -re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); -var tildeTrimReplace = '$1~'; +var TILDETRIM = R++ +src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' +re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') +var tildeTrimReplace = '$1~' -var TILDE = R++; -src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; -var TILDELOOSE = R++; -src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; +var TILDE = R++ +src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' +var TILDELOOSE = R++ +src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' // Caret ranges. // Meaning is "at least and backwards compatible with" -var LONECARET = R++; -src[LONECARET] = '(?:\\^)'; +var LONECARET = R++ +src[LONECARET] = '(?:\\^)' -var CARETTRIM = R++; -src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; -re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); -var caretTrimReplace = '$1^'; +var CARETTRIM = R++ +src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' +re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') +var caretTrimReplace = '$1^' -var CARET = R++; -src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; -var CARETLOOSE = R++; -src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; +var CARET = R++ +src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' +var CARETLOOSE = R++ +src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' // A simple gt/lt/eq thing, or just "" to indicate "any version" -var COMPARATORLOOSE = R++; -src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; -var COMPARATOR = R++; -src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; - +var COMPARATORLOOSE = R++ +src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' +var COMPARATOR = R++ +src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' // An expression to strip any whitespace between the gtlt and the thing // it modifies, so that `> 1.2.3` ==> `>1.2.3` -var COMPARATORTRIM = R++; +var COMPARATORTRIM = R++ src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + - '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' // this one has to use the /g flag -re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); -var comparatorTrimReplace = '$1$2$3'; - +re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') +var comparatorTrimReplace = '$1$2$3' // Something like `1.2.3 - 1.2.4` // Note that these all use the loose form, because they'll be // checked against either the strict or loose comparator form // later. -var HYPHENRANGE = R++; +var HYPHENRANGE = R++ src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAIN] + ')' + - '\\s*$'; + '\\s*$' -var HYPHENRANGELOOSE = R++; +var HYPHENRANGELOOSE = R++ src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s*$'; + '\\s*$' // Star ranges basically just allow anything at all. -var STAR = R++; -src[STAR] = '(<|>)?=?\\s*\\*'; +var STAR = R++ +src[STAR] = '(<|>)?=?\\s*\\*' // Compile to actual regexp objects. // All are flag-free, unless they were created above with a flag. for (var i = 0; i < R; i++) { - debug(i, src[i]); - if (!re[i]) - re[i] = new RegExp(src[i]); + debug(i, src[i]) + if (!re[i]) { + re[i] = new RegExp(src[i]) + } } -exports.parse = parse; -function parse(version, loose) { - if (version instanceof SemVer) - return version; +exports.parse = parse +function parse (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + if (version instanceof SemVer) { + return version + } - if (typeof version !== 'string') - return null; + if (typeof version !== 'string') { + return null + } - if (version.length > MAX_LENGTH) - return null; + if (version.length > MAX_LENGTH) { + return null + } - var r = loose ? re[LOOSE] : re[FULL]; - if (!r.test(version)) - return null; + var r = options.loose ? re[LOOSE] : re[FULL] + if (!r.test(version)) { + return null + } try { - return new SemVer(version, loose); + return new SemVer(version, options) } catch (er) { - return null; + return null } } -exports.valid = valid; -function valid(version, loose) { - var v = parse(version, loose); - return v ? v.version : null; +exports.valid = valid +function valid (version, options) { + var v = parse(version, options) + return v ? v.version : null } - -exports.clean = clean; -function clean(version, loose) { - var s = parse(version.trim().replace(/^[=v]+/, ''), loose); - return s ? s.version : null; +exports.clean = clean +function clean (version, options) { + var s = parse(version.trim().replace(/^[=v]+/, ''), options) + return s ? s.version : null } -exports.SemVer = SemVer; +exports.SemVer = SemVer -function SemVer(version, loose) { +function SemVer (version, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } if (version instanceof SemVer) { - if (version.loose === loose) - return version; - else - version = version.version; + if (version.loose === options.loose) { + return version + } else { + version = version.version + } } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version); + throw new TypeError('Invalid Version: ' + version) } - if (version.length > MAX_LENGTH) + if (version.length > MAX_LENGTH) { throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + } + + if (!(this instanceof SemVer)) { + return new SemVer(version, options) + } - if (!(this instanceof SemVer)) - return new SemVer(version, loose); + debug('SemVer', version, options) + this.options = options + this.loose = !!options.loose - debug('SemVer', version, loose); - this.loose = loose; - var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); + var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) - if (!m) - throw new TypeError('Invalid Version: ' + version); + if (!m) { + throw new TypeError('Invalid Version: ' + version) + } - this.raw = version; + this.raw = version // these are actually numbers - this.major = +m[1]; - this.minor = +m[2]; - this.patch = +m[3]; + this.major = +m[1] + this.minor = +m[2] + this.patch = +m[3] - if (this.major > MAX_SAFE_INTEGER || this.major < 0) + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { throw new TypeError('Invalid major version') + } - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { throw new TypeError('Invalid minor version') + } - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { throw new TypeError('Invalid patch version') + } // numberify any prerelease numeric ids - if (!m[4]) - this.prerelease = []; - else - this.prerelease = m[4].split('.').map(function(id) { + if (!m[4]) { + this.prerelease = [] + } else { + this.prerelease = m[4].split('.').map(function (id) { if (/^[0-9]+$/.test(id)) { - var num = +id; - if (num >= 0 && num < MAX_SAFE_INTEGER) - return num; + var num = +id + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num + } } - return id; - }); + return id + }) + } - this.build = m[5] ? m[5].split('.') : []; - this.format(); + this.build = m[5] ? m[5].split('.') : [] + this.format() } -SemVer.prototype.format = function() { - this.version = this.major + '.' + this.minor + '.' + this.patch; - if (this.prerelease.length) - this.version += '-' + this.prerelease.join('.'); - return this.version; -}; +SemVer.prototype.format = function () { + this.version = this.major + '.' + this.minor + '.' + this.patch + if (this.prerelease.length) { + this.version += '-' + this.prerelease.join('.') + } + return this.version +} -SemVer.prototype.toString = function() { - return this.version; -}; +SemVer.prototype.toString = function () { + return this.version +} -SemVer.prototype.compare = function(other) { - debug('SemVer.compare', this.version, this.loose, other); - if (!(other instanceof SemVer)) - other = new SemVer(other, this.loose); +SemVer.prototype.compare = function (other) { + debug('SemVer.compare', this.version, this.options, other) + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } - return this.compareMain(other) || this.comparePre(other); -}; + return this.compareMain(other) || this.comparePre(other) +} -SemVer.prototype.compareMain = function(other) { - if (!(other instanceof SemVer)) - other = new SemVer(other, this.loose); +SemVer.prototype.compareMain = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch); -}; + compareIdentifiers(this.patch, other.patch) +} -SemVer.prototype.comparePre = function(other) { - if (!(other instanceof SemVer)) - other = new SemVer(other, this.loose); +SemVer.prototype.comparePre = function (other) { + if (!(other instanceof SemVer)) { + other = new SemVer(other, this.options) + } // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) - return -1; - else if (!this.prerelease.length && other.prerelease.length) - return 1; - else if (!this.prerelease.length && !other.prerelease.length) - return 0; + if (this.prerelease.length && !other.prerelease.length) { + return -1 + } else if (!this.prerelease.length && other.prerelease.length) { + return 1 + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0 + } - var i = 0; + var i = 0 do { - var a = this.prerelease[i]; - var b = other.prerelease[i]; - debug('prerelease compare', i, a, b); - if (a === undefined && b === undefined) - return 0; - else if (b === undefined) - return 1; - else if (a === undefined) - return -1; - else if (a === b) - continue; - else - return compareIdentifiers(a, b); - } while (++i); -}; + var a = this.prerelease[i] + var b = other.prerelease[i] + debug('prerelease compare', i, a, b) + if (a === undefined && b === undefined) { + return 0 + } else if (b === undefined) { + return 1 + } else if (a === undefined) { + return -1 + } else if (a === b) { + continue + } else { + return compareIdentifiers(a, b) + } + } while (++i) +} // preminor will bump the version up to the next minor release, and immediately // down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function(release, identifier) { +SemVer.prototype.inc = function (release, identifier) { switch (release) { case 'premajor': - this.prerelease.length = 0; - this.patch = 0; - this.minor = 0; - this.major++; - this.inc('pre', identifier); - break; + this.prerelease.length = 0 + this.patch = 0 + this.minor = 0 + this.major++ + this.inc('pre', identifier) + break case 'preminor': - this.prerelease.length = 0; - this.patch = 0; - this.minor++; - this.inc('pre', identifier); - break; + this.prerelease.length = 0 + this.patch = 0 + this.minor++ + this.inc('pre', identifier) + break case 'prepatch': // If this is already a prerelease, it will bump to the next version // drop any prereleases that might already exist, since they are not // relevant at this point. - this.prerelease.length = 0; - this.inc('patch', identifier); - this.inc('pre', identifier); - break; + this.prerelease.length = 0 + this.inc('patch', identifier) + this.inc('pre', identifier) + break // If the input is a non-prerelease version, this acts the same as // prepatch. case 'prerelease': - if (this.prerelease.length === 0) - this.inc('patch', identifier); - this.inc('pre', identifier); - break; + if (this.prerelease.length === 0) { + this.inc('patch', identifier) + } + this.inc('pre', identifier) + break case 'major': // If this is a pre-major version, bump up to the same major version. // Otherwise increment major. // 1.0.0-5 bumps to 1.0.0 // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) - this.major++; - this.minor = 0; - this.patch = 0; - this.prerelease = []; - break; + if (this.minor !== 0 || + this.patch !== 0 || + this.prerelease.length === 0) { + this.major++ + } + this.minor = 0 + this.patch = 0 + this.prerelease = [] + break case 'minor': // If this is a pre-minor version, bump up to the same minor version. // Otherwise increment minor. // 1.2.0-5 bumps to 1.2.0 // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) - this.minor++; - this.patch = 0; - this.prerelease = []; - break; + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++ + } + this.patch = 0 + this.prerelease = [] + break case 'patch': // If this is not a pre-release version, it will increment the patch. // If it is a pre-release it will bump up to the same patch version. // 1.2.0-5 patches to 1.2.0 // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) - this.patch++; - this.prerelease = []; - break; + if (this.prerelease.length === 0) { + this.patch++ + } + this.prerelease = [] + break // This probably shouldn't be used publicly. // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. case 'pre': - if (this.prerelease.length === 0) - this.prerelease = [0]; - else { - var i = this.prerelease.length; + if (this.prerelease.length === 0) { + this.prerelease = [0] + } else { + var i = this.prerelease.length while (--i >= 0) { if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++; - i = -2; + this.prerelease[i]++ + i = -2 } } - if (i === -1) // didn't increment anything - this.prerelease.push(0); + if (i === -1) { + // didn't increment anything + this.prerelease.push(0) + } } if (identifier) { // 1.2.0-beta.1 bumps to 1.2.0-beta.2, // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) - this.prerelease = [identifier, 0]; - } else - this.prerelease = [identifier, 0]; + if (isNaN(this.prerelease[1])) { + this.prerelease = [identifier, 0] + } + } else { + this.prerelease = [identifier, 0] + } } - break; + break default: - throw new Error('invalid increment argument: ' + release); + throw new Error('invalid increment argument: ' + release) } - this.format(); - this.raw = this.version; - return this; -}; + this.format() + this.raw = this.version + return this +} -exports.inc = inc; -function inc(version, release, loose, identifier) { - if (typeof(loose) === 'string') { - identifier = loose; - loose = undefined; +exports.inc = inc +function inc (version, release, loose, identifier) { + if (typeof (loose) === 'string') { + identifier = loose + loose = undefined } try { - return new SemVer(version, loose).inc(release, identifier).version; + return new SemVer(version, loose).inc(release, identifier).version } catch (er) { - return null; + return null } } -exports.diff = diff; -function diff(version1, version2) { +exports.diff = diff +function diff (version1, version2) { if (eq(version1, version2)) { - return null; + return null } else { - var v1 = parse(version1); - var v2 = parse(version2); + var v1 = parse(version1) + var v2 = parse(version2) + var prefix = '' if (v1.prerelease.length || v2.prerelease.length) { - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return 'pre'+key; - } - } - } - return 'prerelease'; + prefix = 'pre' + var defaultResult = 'prerelease' } for (var key in v1) { if (key === 'major' || key === 'minor' || key === 'patch') { if (v1[key] !== v2[key]) { - return key; + return prefix + key } } } + return defaultResult // may be undefined } } -exports.compareIdentifiers = compareIdentifiers; +exports.compareIdentifiers = compareIdentifiers -var numeric = /^[0-9]+$/; -function compareIdentifiers(a, b) { - var anum = numeric.test(a); - var bnum = numeric.test(b); +var numeric = /^[0-9]+$/ +function compareIdentifiers (a, b) { + var anum = numeric.test(a) + var bnum = numeric.test(b) if (anum && bnum) { - a = +a; - b = +b; + a = +a + b = +b } - return (anum && !bnum) ? -1 : - (bnum && !anum) ? 1 : - a < b ? -1 : - a > b ? 1 : - 0; + return a === b ? 0 + : (anum && !bnum) ? -1 + : (bnum && !anum) ? 1 + : a < b ? -1 + : 1 } -exports.rcompareIdentifiers = rcompareIdentifiers; -function rcompareIdentifiers(a, b) { - return compareIdentifiers(b, a); +exports.rcompareIdentifiers = rcompareIdentifiers +function rcompareIdentifiers (a, b) { + return compareIdentifiers(b, a) } -exports.major = major; -function major(a, loose) { - return new SemVer(a, loose).major; +exports.major = major +function major (a, loose) { + return new SemVer(a, loose).major } -exports.minor = minor; -function minor(a, loose) { - return new SemVer(a, loose).minor; +exports.minor = minor +function minor (a, loose) { + return new SemVer(a, loose).minor } -exports.patch = patch; -function patch(a, loose) { - return new SemVer(a, loose).patch; +exports.patch = patch +function patch (a, loose) { + return new SemVer(a, loose).patch } -exports.compare = compare; -function compare(a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)); +exports.compare = compare +function compare (a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)) } -exports.compareLoose = compareLoose; -function compareLoose(a, b) { - return compare(a, b, true); +exports.compareLoose = compareLoose +function compareLoose (a, b) { + return compare(a, b, true) } -exports.rcompare = rcompare; -function rcompare(a, b, loose) { - return compare(b, a, loose); +exports.rcompare = rcompare +function rcompare (a, b, loose) { + return compare(b, a, loose) } -exports.sort = sort; -function sort(list, loose) { - return list.sort(function(a, b) { - return exports.compare(a, b, loose); - }); +exports.sort = sort +function sort (list, loose) { + return list.sort(function (a, b) { + return exports.compare(a, b, loose) + }) } -exports.rsort = rsort; -function rsort(list, loose) { - return list.sort(function(a, b) { - return exports.rcompare(a, b, loose); - }); +exports.rsort = rsort +function rsort (list, loose) { + return list.sort(function (a, b) { + return exports.rcompare(a, b, loose) + }) } -exports.gt = gt; -function gt(a, b, loose) { - return compare(a, b, loose) > 0; +exports.gt = gt +function gt (a, b, loose) { + return compare(a, b, loose) > 0 } -exports.lt = lt; -function lt(a, b, loose) { - return compare(a, b, loose) < 0; +exports.lt = lt +function lt (a, b, loose) { + return compare(a, b, loose) < 0 } -exports.eq = eq; -function eq(a, b, loose) { - return compare(a, b, loose) === 0; +exports.eq = eq +function eq (a, b, loose) { + return compare(a, b, loose) === 0 } -exports.neq = neq; -function neq(a, b, loose) { - return compare(a, b, loose) !== 0; +exports.neq = neq +function neq (a, b, loose) { + return compare(a, b, loose) !== 0 } -exports.gte = gte; -function gte(a, b, loose) { - return compare(a, b, loose) >= 0; +exports.gte = gte +function gte (a, b, loose) { + return compare(a, b, loose) >= 0 } -exports.lte = lte; -function lte(a, b, loose) { - return compare(a, b, loose) <= 0; +exports.lte = lte +function lte (a, b, loose) { + return compare(a, b, loose) <= 0 } -exports.cmp = cmp; -function cmp(a, op, b, loose) { - var ret; +exports.cmp = cmp +function cmp (a, op, b, loose) { switch (op) { case '===': - if (typeof a === 'object') a = a.version; - if (typeof b === 'object') b = b.version; - ret = a === b; - break; - case '!==': - if (typeof a === 'object') a = a.version; - if (typeof b === 'object') b = b.version; - ret = a !== b; - break; - case '': case '=': case '==': ret = eq(a, b, loose); break; - case '!=': ret = neq(a, b, loose); break; - case '>': ret = gt(a, b, loose); break; - case '>=': ret = gte(a, b, loose); break; - case '<': ret = lt(a, b, loose); break; - case '<=': ret = lte(a, b, loose); break; - default: throw new TypeError('Invalid operator: ' + op); - } - return ret; -} - -exports.Comparator = Comparator; -function Comparator(comp, loose) { - if (comp instanceof Comparator) { - if (comp.loose === loose) - return comp; - else - comp = comp.value; - } - - if (!(this instanceof Comparator)) - return new Comparator(comp, loose); + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a === b - debug('comparator', comp, loose); - this.loose = loose; - this.parse(comp); + case '!==': + if (typeof a === 'object') + a = a.version + if (typeof b === 'object') + b = b.version + return a !== b - if (this.semver === ANY) - this.value = ''; - else - this.value = this.operator + this.semver.version; + case '': + case '=': + case '==': + return eq(a, b, loose) - debug('comp', this); -} + case '!=': + return neq(a, b, loose) -var ANY = {}; -Comparator.prototype.parse = function(comp) { - var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; - var m = comp.match(r); + case '>': + return gt(a, b, loose) - if (!m) - throw new TypeError('Invalid comparator: ' + comp); + case '>=': + return gte(a, b, loose) - this.operator = m[1]; - if (this.operator === '=') - this.operator = ''; + case '<': + return lt(a, b, loose) - // if it literally is just '>' or '' then allow anything. - if (!m[2]) - this.semver = ANY; - else - this.semver = new SemVer(m[2], this.loose); -}; + case '<=': + return lte(a, b, loose) -Comparator.prototype.toString = function() { - return this.value; -}; + default: + throw new TypeError('Invalid operator: ' + op) + } +} -Comparator.prototype.test = function(version) { - debug('Comparator.test', version, this.loose); +exports.Comparator = Comparator +function Comparator (comp, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } - if (this.semver === ANY) - return true; + if (comp instanceof Comparator) { + if (comp.loose === !!options.loose) { + return comp + } else { + comp = comp.value + } + } - if (typeof version === 'string') - version = new SemVer(version, this.loose); + if (!(this instanceof Comparator)) { + return new Comparator(comp, options) + } - return cmp(version, this.operator, this.semver, this.loose); -}; + debug('comparator', comp, options) + this.options = options + this.loose = !!options.loose + this.parse(comp) -Comparator.prototype.intersects = function(comp, loose) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required'); + if (this.semver === ANY) { + this.value = '' + } else { + this.value = this.operator + this.semver.version } - var rangeTmp; - + debug('comp', this) +} + +var ANY = {} +Comparator.prototype.parse = function (comp) { + var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var m = comp.match(r) + + if (!m) { + throw new TypeError('Invalid comparator: ' + comp) + } + + this.operator = m[1] + if (this.operator === '=') { + this.operator = '' + } + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) { + this.semver = ANY + } else { + this.semver = new SemVer(m[2], this.options.loose) + } +} + +Comparator.prototype.toString = function () { + return this.value +} + +Comparator.prototype.test = function (version) { + debug('Comparator.test', version, this.options.loose) + + if (this.semver === ANY) { + return true + } + + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } + + return cmp(version, this.operator, this.semver, this.options) +} + +Comparator.prototype.intersects = function (comp, options) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required') + } + + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } + + var rangeTmp + if (this.operator === '') { - rangeTmp = new Range(comp.value, loose); - return satisfies(this.value, rangeTmp, loose); + rangeTmp = new Range(comp.value, options) + return satisfies(this.value, rangeTmp, options) } else if (comp.operator === '') { - rangeTmp = new Range(this.value, loose); - return satisfies(comp.semver, rangeTmp, loose); + rangeTmp = new Range(this.value, options) + return satisfies(comp.semver, rangeTmp, options) } var sameDirectionIncreasing = (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>'); + (comp.operator === '>=' || comp.operator === '>') var sameDirectionDecreasing = (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<'); - var sameSemVer = this.semver.version === comp.semver.version; + (comp.operator === '<=' || comp.operator === '<') + var sameSemVer = this.semver.version === comp.semver.version var differentDirectionsInclusive = (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<='); + (comp.operator === '>=' || comp.operator === '<=') var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, loose) && + cmp(this.semver, '<', comp.semver, options) && ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')); + (comp.operator === '<=' || comp.operator === '<')) var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, loose) && + cmp(this.semver, '>', comp.semver, options) && ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')); + (comp.operator === '>=' || comp.operator === '>')) return sameDirectionIncreasing || sameDirectionDecreasing || (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan; -}; + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan +} +exports.Range = Range +function Range (range, options) { + if (!options || typeof options !== 'object') { + options = { + loose: !!options, + includePrerelease: false + } + } -exports.Range = Range; -function Range(range, loose) { if (range instanceof Range) { - if (range.loose === loose) { - return range; + if (range.loose === !!options.loose && + range.includePrerelease === !!options.includePrerelease) { + return range } else { - return new Range(range.raw, loose); + return new Range(range.raw, options) } } if (range instanceof Comparator) { - return new Range(range.value, loose); + return new Range(range.value, options) } - if (!(this instanceof Range)) - return new Range(range, loose); + if (!(this instanceof Range)) { + return new Range(range, options) + } - this.loose = loose; + this.options = options + this.loose = !!options.loose + this.includePrerelease = !!options.includePrerelease // First, split based on boolean or || - this.raw = range; - this.set = range.split(/\s*\|\|\s*/).map(function(range) { - return this.parseRange(range.trim()); - }, this).filter(function(c) { + this.raw = range + this.set = range.split(/\s*\|\|\s*/).map(function (range) { + return this.parseRange(range.trim()) + }, this).filter(function (c) { // throw out any that are not relevant for whatever reason - return c.length; - }); + return c.length + }) if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range); + throw new TypeError('Invalid SemVer Range: ' + range) } - this.format(); + this.format() } -Range.prototype.format = function() { - this.range = this.set.map(function(comps) { - return comps.join(' ').trim(); - }).join('||').trim(); - return this.range; -}; +Range.prototype.format = function () { + this.range = this.set.map(function (comps) { + return comps.join(' ').trim() + }).join('||').trim() + return this.range +} -Range.prototype.toString = function() { - return this.range; -}; +Range.prototype.toString = function () { + return this.range +} -Range.prototype.parseRange = function(range) { - var loose = this.loose; - range = range.trim(); - debug('range', range, loose); +Range.prototype.parseRange = function (range) { + var loose = this.options.loose + range = range.trim() // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; - range = range.replace(hr, hyphenReplace); - debug('hyphen replace', range); + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] + range = range.replace(hr, hyphenReplace) + debug('hyphen replace', range) // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); - debug('comparator trim', range, re[COMPARATORTRIM]); + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) + debug('comparator trim', range, re[COMPARATORTRIM]) // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace); + range = range.replace(re[TILDETRIM], tildeTrimReplace) // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace); + range = range.replace(re[CARETTRIM], caretTrimReplace) // normalize spaces - range = range.split(/\s+/).join(' '); + range = range.split(/\s+/).join(' ') // At this point, the range is completely trimmed and // ready to be split into comparators. - var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; - var set = range.split(' ').map(function(comp) { - return parseComparator(comp, loose); - }).join(' ').split(/\s+/); - if (this.loose) { + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] + var set = range.split(' ').map(function (comp) { + return parseComparator(comp, this.options) + }, this).join(' ').split(/\s+/) + if (this.options.loose) { // in loose mode, throw out any that are not valid comparators - set = set.filter(function(comp) { - return !!comp.match(compRe); - }); + set = set.filter(function (comp) { + return !!comp.match(compRe) + }) } - set = set.map(function(comp) { - return new Comparator(comp, loose); - }); + set = set.map(function (comp) { + return new Comparator(comp, this.options) + }, this) - return set; -}; + return set +} -Range.prototype.intersects = function(range, loose) { +Range.prototype.intersects = function (range, options) { if (!(range instanceof Range)) { - throw new TypeError('a Range is required'); + throw new TypeError('a Range is required') } - return this.set.some(function(thisComparators) { - return thisComparators.every(function(thisComparator) { - return range.set.some(function(rangeComparators) { - return rangeComparators.every(function(rangeComparator) { - return thisComparator.intersects(rangeComparator, loose); - }); - }); - }); - }); -}; + return this.set.some(function (thisComparators) { + return thisComparators.every(function (thisComparator) { + return range.set.some(function (rangeComparators) { + return rangeComparators.every(function (rangeComparator) { + return thisComparator.intersects(rangeComparator, options) + }) + }) + }) + }) +} // Mostly just for testing and legacy API reasons -exports.toComparators = toComparators; -function toComparators(range, loose) { - return new Range(range, loose).set.map(function(comp) { - return comp.map(function(c) { - return c.value; - }).join(' ').trim().split(' '); - }); +exports.toComparators = toComparators +function toComparators (range, options) { + return new Range(range, options).set.map(function (comp) { + return comp.map(function (c) { + return c.value + }).join(' ').trim().split(' ') + }) } // comprised of xranges, tildes, stars, and gtlt's at this point. // already replaced the hyphen ranges // turn into a set of JUST comparators. -function parseComparator(comp, loose) { - debug('comp', comp); - comp = replaceCarets(comp, loose); - debug('caret', comp); - comp = replaceTildes(comp, loose); - debug('tildes', comp); - comp = replaceXRanges(comp, loose); - debug('xrange', comp); - comp = replaceStars(comp, loose); - debug('stars', comp); - return comp; +function parseComparator (comp, options) { + debug('comp', comp, options) + comp = replaceCarets(comp, options) + debug('caret', comp) + comp = replaceTildes(comp, options) + debug('tildes', comp) + comp = replaceXRanges(comp, options) + debug('xrange', comp) + comp = replaceStars(comp, options) + debug('stars', comp) + return comp } -function isX(id) { - return !id || id.toLowerCase() === 'x' || id === '*'; +function isX (id) { + return !id || id.toLowerCase() === 'x' || id === '*' } // ~, ~> --> * (any, kinda silly) @@ -49284,39 +49381,38 @@ function isX(id) { // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes(comp, loose) { - return comp.trim().split(/\s+/).map(function(comp) { - return replaceTilde(comp, loose); - }).join(' '); -} - -function replaceTilde(comp, loose) { - var r = loose ? re[TILDELOOSE] : re[TILDE]; - return comp.replace(r, function(_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr); - var ret; - - if (isX(M)) - ret = ''; - else if (isX(m)) - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; - else if (isX(p)) +function replaceTildes (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceTilde(comp, options) + }).join(' ') +} + +function replaceTilde (comp, options) { + var r = options.loose ? re[TILDELOOSE] : re[TILDE] + return comp.replace(r, function (_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; - else if (pr) { - debug('replaceTilde pr', pr); - if (pr.charAt(0) !== '-') - pr = '-' + pr; - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0'; - } else + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else if (pr) { + debug('replaceTilde pr', pr) + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } else { // ~1.2.3 == >=1.2.3 <1.3.0 ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0'; + ' <' + M + '.' + (+m + 1) + '.0' + } - debug('tilde return', ret); - return ret; - }); + debug('tilde return', ret) + return ret + }) } // ^ --> * (any, kinda silly) @@ -49325,138 +49421,144 @@ function replaceTilde(comp, loose) { // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 // ^1.2.3 --> >=1.2.3 <2.0.0 // ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets(comp, loose) { - return comp.trim().split(/\s+/).map(function(comp) { - return replaceCaret(comp, loose); - }).join(' '); -} - -function replaceCaret(comp, loose) { - debug('caret', comp, loose); - var r = loose ? re[CARETLOOSE] : re[CARET]; - return comp.replace(r, function(_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr); - var ret; - - if (isX(M)) - ret = ''; - else if (isX(m)) - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; - else if (isX(p)) { - if (M === '0') - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; - else - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; +function replaceCarets (comp, options) { + return comp.trim().split(/\s+/).map(function (comp) { + return replaceCaret(comp, options) + }).join(' ') +} + +function replaceCaret (comp, options) { + debug('caret', comp, options) + var r = options.loose ? re[CARETLOOSE] : re[CARET] + return comp.replace(r, function (_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr) + var ret + + if (isX(M)) { + ret = '' + } else if (isX(m)) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' + } else if (isX(p)) { + if (M === '0') { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' + } else { + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' + } } else if (pr) { - debug('replaceCaret pr', pr); - if (pr.charAt(0) !== '-') - pr = '-' + pr; + debug('replaceCaret pr', pr) if (M === '0') { - if (m === '0') - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + m + '.' + (+p + 1); - else - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + M + '.' + (+m + 1) + '.0'; - } else - ret = '>=' + M + '.' + m + '.' + p + pr + - ' <' + (+M + 1) + '.0.0'; + if (m === '0') { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + m + '.' + (+p + 1) + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { + ret = '>=' + M + '.' + m + '.' + p + '-' + pr + + ' <' + (+M + 1) + '.0.0' + } } else { - debug('no pr'); + debug('no pr') if (M === '0') { - if (m === '0') + if (m === '0') { ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1); - else + ' <' + M + '.' + m + '.' + (+p + 1) + } else { ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0'; - } else + ' <' + M + '.' + (+m + 1) + '.0' + } + } else { ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0'; + ' <' + (+M + 1) + '.0.0' + } } - debug('caret return', ret); - return ret; - }); + debug('caret return', ret) + return ret + }) } -function replaceXRanges(comp, loose) { - debug('replaceXRanges', comp, loose); - return comp.split(/\s+/).map(function(comp) { - return replaceXRange(comp, loose); - }).join(' '); +function replaceXRanges (comp, options) { + debug('replaceXRanges', comp, options) + return comp.split(/\s+/).map(function (comp) { + return replaceXRange(comp, options) + }).join(' ') } -function replaceXRange(comp, loose) { - comp = comp.trim(); - var r = loose ? re[XRANGELOOSE] : re[XRANGE]; - return comp.replace(r, function(ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr); - var xM = isX(M); - var xm = xM || isX(m); - var xp = xm || isX(p); - var anyX = xp; +function replaceXRange (comp, options) { + comp = comp.trim() + var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] + return comp.replace(r, function (ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr) + var xM = isX(M) + var xm = xM || isX(m) + var xp = xm || isX(p) + var anyX = xp - if (gtlt === '=' && anyX) - gtlt = ''; + if (gtlt === '=' && anyX) { + gtlt = '' + } if (xM) { if (gtlt === '>' || gtlt === '<') { // nothing is allowed - ret = '<0.0.0'; + ret = '<0.0.0' } else { // nothing is forbidden - ret = '*'; + ret = '*' } } else if (gtlt && anyX) { + // we know patch is an x, because we have any x at all. // replace X with 0 - if (xm) - m = 0; - if (xp) - p = 0; + if (xm) { + m = 0 + } + p = 0 if (gtlt === '>') { // >1 => >=2.0.0 // >1.2 => >=1.3.0 // >1.2.3 => >= 1.2.4 - gtlt = '>='; + gtlt = '>=' if (xm) { - M = +M + 1; - m = 0; - p = 0; - } else if (xp) { - m = +m + 1; - p = 0; + M = +M + 1 + m = 0 + p = 0 + } else { + m = +m + 1 + p = 0 } } else if (gtlt === '<=') { // <=0.7.x is actually <0.8.0, since any 0.7.x should // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<'; - if (xm) - M = +M + 1; - else - m = +m + 1; + gtlt = '<' + if (xm) { + M = +M + 1 + } else { + m = +m + 1 + } } - ret = gtlt + M + '.' + m + '.' + p; + ret = gtlt + M + '.' + m + '.' + p } else if (xm) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' } else if (xp) { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' } - debug('xRange return', ret); + debug('xRange return', ret) - return ret; - }); + return ret + }) } // Because * is AND-ed with everything else in the comparator, // and '' means "any version", just remove the *s entirely. -function replaceStars(comp, loose) { - debug('replaceStars', comp, loose); +function replaceStars (comp, options) { + debug('replaceStars', comp, options) // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], ''); + return comp.trim().replace(re[STAR], '') } // This function is passed to string.replace(re[HYPHENRANGE]) @@ -49464,238 +49566,323 @@ function replaceStars(comp, loose) { // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do // 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - - if (isX(fM)) - from = ''; - else if (isX(fm)) - from = '>=' + fM + '.0.0'; - else if (isX(fp)) - from = '>=' + fM + '.' + fm + '.0'; - else - from = '>=' + from; - - if (isX(tM)) - to = ''; - else if (isX(tm)) - to = '<' + (+tM + 1) + '.0.0'; - else if (isX(tp)) - to = '<' + tM + '.' + (+tm + 1) + '.0'; - else if (tpr) - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; - else - to = '<=' + to; +function hyphenReplace ($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + if (isX(fM)) { + from = '' + } else if (isX(fm)) { + from = '>=' + fM + '.0.0' + } else if (isX(fp)) { + from = '>=' + fM + '.' + fm + '.0' + } else { + from = '>=' + from + } - return (from + ' ' + to).trim(); -} + if (isX(tM)) { + to = '' + } else if (isX(tm)) { + to = '<' + (+tM + 1) + '.0.0' + } else if (isX(tp)) { + to = '<' + tM + '.' + (+tm + 1) + '.0' + } else if (tpr) { + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr + } else { + to = '<=' + to + } + return (from + ' ' + to).trim() +} // if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function(version) { - if (!version) - return false; +Range.prototype.test = function (version) { + if (!version) { + return false + } - if (typeof version === 'string') - version = new SemVer(version, this.loose); + if (typeof version === 'string') { + version = new SemVer(version, this.options) + } for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version)) - return true; + if (testSet(this.set[i], version, this.options)) { + return true + } } - return false; -}; + return false +} -function testSet(set, version) { +function testSet (set, version, options) { for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) - return false; + if (!set[i].test(version)) { + return false + } } - if (version.prerelease.length) { + if (version.prerelease.length && !options.includePrerelease) { // Find the set of versions that are allowed to have prereleases // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 // That should allow `1.2.3-pr.2` to pass. // However, `1.2.4-alpha.notready` should NOT be allowed, // even though it's within the range set by the comparators. - for (var i = 0; i < set.length; i++) { - debug(set[i].semver); - if (set[i].semver === ANY) - continue; + for (i = 0; i < set.length; i++) { + debug(set[i].semver) + if (set[i].semver === ANY) { + continue + } if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver; + var allowed = set[i].semver if (allowed.major === version.major && allowed.minor === version.minor && - allowed.patch === version.patch) - return true; + allowed.patch === version.patch) { + return true + } } } // Version has a -pre, but it's not one of the ones we like. - return false; + return false } - return true; + return true } -exports.satisfies = satisfies; -function satisfies(version, range, loose) { +exports.satisfies = satisfies +function satisfies (version, range, options) { try { - range = new Range(range, loose); + range = new Range(range, options) } catch (er) { - return false; + return false } - return range.test(version); + return range.test(version) } -exports.maxSatisfying = maxSatisfying; -function maxSatisfying(versions, range, loose) { - var max = null; - var maxSV = null; +exports.maxSatisfying = maxSatisfying +function maxSatisfying (versions, range, options) { + var max = null + var maxSV = null try { - var rangeObj = new Range(range, loose); + var rangeObj = new Range(range, options) } catch (er) { - return null; + return null } versions.forEach(function (v) { - if (rangeObj.test(v)) { // satisfies(v, range, loose) - if (!max || maxSV.compare(v) === -1) { // compare(max, v, true) - max = v; - maxSV = new SemVer(max, loose); + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!max || maxSV.compare(v) === -1) { + // compare(max, v, true) + max = v + maxSV = new SemVer(max, options) } } }) - return max; + return max } -exports.minSatisfying = minSatisfying; -function minSatisfying(versions, range, loose) { - var min = null; - var minSV = null; +exports.minSatisfying = minSatisfying +function minSatisfying (versions, range, options) { + var min = null + var minSV = null try { - var rangeObj = new Range(range, loose); + var rangeObj = new Range(range, options) } catch (er) { - return null; + return null } versions.forEach(function (v) { - if (rangeObj.test(v)) { // satisfies(v, range, loose) - if (!min || minSV.compare(v) === 1) { // compare(min, v, true) - min = v; - minSV = new SemVer(min, loose); + if (rangeObj.test(v)) { + // satisfies(v, range, options) + if (!min || minSV.compare(v) === 1) { + // compare(min, v, true) + min = v + minSV = new SemVer(min, options) } } }) - return min; + return min +} + +exports.minVersion = minVersion +function minVersion (range, loose) { + range = new Range(range, loose) + + var minver = new SemVer('0.0.0') + if (range.test(minver)) { + return minver + } + + minver = new SemVer('0.0.0-0') + if (range.test(minver)) { + return minver + } + + minver = null + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i] + + comparators.forEach(function (comparator) { + // Clone to avoid manipulating the comparator's semver object. + var compver = new SemVer(comparator.semver.version) + switch (comparator.operator) { + case '>': + if (compver.prerelease.length === 0) { + compver.patch++ + } else { + compver.prerelease.push(0) + } + compver.raw = compver.format() + /* fallthrough */ + case '': + case '>=': + if (!minver || gt(minver, compver)) { + minver = compver + } + break + case '<': + case '<=': + /* Ignore maximum versions */ + break + /* istanbul ignore next */ + default: + throw new Error('Unexpected operation: ' + comparator.operator) + } + }) + } + + if (minver && range.test(minver)) { + return minver + } + + return null } -exports.validRange = validRange; -function validRange(range, loose) { +exports.validRange = validRange +function validRange (range, options) { try { // Return '*' instead of '' so that truthiness works. // This will throw if it's invalid anyway - return new Range(range, loose).range || '*'; + return new Range(range, options).range || '*' } catch (er) { - return null; + return null } } // Determine if version is less than all the versions possible in the range -exports.ltr = ltr; -function ltr(version, range, loose) { - return outside(version, range, '<', loose); +exports.ltr = ltr +function ltr (version, range, options) { + return outside(version, range, '<', options) } // Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr; -function gtr(version, range, loose) { - return outside(version, range, '>', loose); +exports.gtr = gtr +function gtr (version, range, options) { + return outside(version, range, '>', options) } -exports.outside = outside; -function outside(version, range, hilo, loose) { - version = new SemVer(version, loose); - range = new Range(range, loose); +exports.outside = outside +function outside (version, range, hilo, options) { + version = new SemVer(version, options) + range = new Range(range, options) - var gtfn, ltefn, ltfn, comp, ecomp; + var gtfn, ltefn, ltfn, comp, ecomp switch (hilo) { case '>': - gtfn = gt; - ltefn = lte; - ltfn = lt; - comp = '>'; - ecomp = '>='; - break; + gtfn = gt + ltefn = lte + ltfn = lt + comp = '>' + ecomp = '>=' + break case '<': - gtfn = lt; - ltefn = gte; - ltfn = gt; - comp = '<'; - ecomp = '<='; - break; + gtfn = lt + ltefn = gte + ltfn = gt + comp = '<' + ecomp = '<=' + break default: - throw new TypeError('Must provide a hilo val of "<" or ">"'); + throw new TypeError('Must provide a hilo val of "<" or ">"') } // If it satisifes the range it is not outside - if (satisfies(version, range, loose)) { - return false; + if (satisfies(version, range, options)) { + return false } // From now on, variable terms are as if we're in "gtr" mode. // but note that everything is flipped for the "ltr" function. for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i]; + var comparators = range.set[i] - var high = null; - var low = null; + var high = null + var low = null - comparators.forEach(function(comparator) { + comparators.forEach(function (comparator) { if (comparator.semver === ANY) { comparator = new Comparator('>=0.0.0') } - high = high || comparator; - low = low || comparator; - if (gtfn(comparator.semver, high.semver, loose)) { - high = comparator; - } else if (ltfn(comparator.semver, low.semver, loose)) { - low = comparator; + high = high || comparator + low = low || comparator + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator } - }); + }) // If the edge version comparator has a operator then our version // isn't outside it if (high.operator === comp || high.operator === ecomp) { - return false; + return false } // If the lowest version comparator has an operator and our version // is less than it then it isn't higher than the range if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) { - return false; + return false } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false; + return false } } - return true; + return true } -exports.prerelease = prerelease; -function prerelease(version, loose) { - var parsed = parse(version, loose); - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null; +exports.prerelease = prerelease +function prerelease (version, options) { + var parsed = parse(version, options) + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null } -exports.intersects = intersects; -function intersects(r1, r2, loose) { - r1 = new Range(r1, loose) - r2 = new Range(r2, loose) +exports.intersects = intersects +function intersects (r1, r2, options) { + r1 = new Range(r1, options) + r2 = new Range(r2, options) return r1.intersects(r2) } +exports.coerce = coerce +function coerce (version) { + if (version instanceof SemVer) { + return version + } + + if (typeof version !== 'string') { + return null + } + + var match = version.match(re[COERCE]) + + if (match == null) { + return null + } + + return parse(match[1] + + '.' + (match[2] || '0') + + '.' + (match[3] || '0')) +} + /***/ }), /* 523 */ @@ -52531,8 +52718,8 @@ const fs = __webpack_require__(546); const writeFileAtomic = __webpack_require__(550); const sortKeys = __webpack_require__(557); const makeDir = __webpack_require__(559); -const pify = __webpack_require__(562); -const detectIndent = __webpack_require__(563); +const pify = __webpack_require__(561); +const detectIndent = __webpack_require__(562); const init = (fn, filePath, data, options) => { if (!filePath) { @@ -54607,7 +54794,7 @@ module.exports = function (x) { const fs = __webpack_require__(23); const path = __webpack_require__(16); const pify = __webpack_require__(560); -const semver = __webpack_require__(561); +const semver = __webpack_require__(522); const defaults = { mode: 0o777 & (~process.umask()), @@ -54669,1647 +54856,158 @@ const makeDir = (input, options) => Promise.resolve().then(() => { if (error.code === 'ENOENT') { if (path.dirname(pth) === pth) { throw permissionError(pth); - } - - if (error.message.includes('null bytes')) { - throw error; - } - - return make(path.dirname(pth)).then(() => make(pth)); - } - - return stat(pth) - .then(stats => stats.isDirectory() ? pth : Promise.reject()) - .catch(() => { - throw error; - }); - }); - }; - - return make(path.resolve(input)); -}); - -module.exports = makeDir; -module.exports.default = makeDir; - -module.exports.sync = (input, options) => { - checkPath(input); - options = Object.assign({}, defaults, options); - - if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { - const pth = path.resolve(input); - - fs.mkdirSync(pth, { - mode: options.mode, - recursive: true - }); - - return pth; - } - - const make = pth => { - try { - options.fs.mkdirSync(pth, options.mode); - } catch (error) { - if (error.code === 'EPERM') { - throw error; - } - - if (error.code === 'ENOENT') { - if (path.dirname(pth) === pth) { - throw permissionError(pth); - } - - if (error.message.includes('null bytes')) { - throw error; - } - - make(path.dirname(pth)); - return make(pth); - } - - try { - if (!options.fs.statSync(pth).isDirectory()) { - throw new Error('The path is not a directory'); - } - } catch (_) { - throw error; - } - } - - return pth; - }; - - return make(path.resolve(input)); -}; - - -/***/ }), -/* 560 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -const processFn = (fn, options) => function (...args) { - const P = options.promiseModule; - - return new P((resolve, reject) => { - if (options.multiArgs) { - args.push((...result) => { - if (options.errorFirst) { - if (result[0]) { - reject(result); - } else { - result.shift(); - resolve(result); - } - } else { - resolve(result); - } - }); - } else if (options.errorFirst) { - args.push((error, result) => { - if (error) { - reject(error); - } else { - resolve(result); - } - }); - } else { - args.push(resolve); - } - - fn.apply(this, args); - }); -}; - -module.exports = (input, options) => { - options = Object.assign({ - exclude: [/.+(Sync|Stream)$/], - errorFirst: true, - promiseModule: Promise - }, options); - - const objType = typeof input; - if (!(input !== null && (objType === 'object' || objType === 'function'))) { - throw new TypeError(`Expected \`input\` to be a \`Function\` or \`Object\`, got \`${input === null ? 'null' : objType}\``); - } - - const filter = key => { - const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); - return options.include ? options.include.some(match) : !options.exclude.some(match); - }; - - let ret; - if (objType === 'function') { - ret = function (...args) { - return options.excludeMain ? input(...args) : processFn(input, options).apply(this, args); - }; - } else { - ret = Object.create(Object.getPrototypeOf(input)); - } - - for (const key in input) { // eslint-disable-line guard-for-in - const property = input[key]; - ret[key] = typeof property === 'function' && filter(key) ? processFn(property, options) : property; - } - - return ret; -}; - - -/***/ }), -/* 561 */ -/***/ (function(module, exports) { - -exports = module.exports = SemVer - -var debug -/* istanbul ignore next */ -if (typeof process === 'object' && - process.env && - process.env.NODE_DEBUG && - /\bsemver\b/i.test(process.env.NODE_DEBUG)) { - debug = function () { - var args = Array.prototype.slice.call(arguments, 0) - args.unshift('SEMVER') - console.log.apply(console, args) - } -} else { - debug = function () {} -} - -// Note: this is the semver.org version of the spec that it implements -// Not necessarily the package version of this code. -exports.SEMVER_SPEC_VERSION = '2.0.0' - -var MAX_LENGTH = 256 -var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || - /* istanbul ignore next */ 9007199254740991 - -// Max safe segment length for coercion. -var MAX_SAFE_COMPONENT_LENGTH = 16 - -// The actual regexps go on exports.re -var re = exports.re = [] -var src = exports.src = [] -var R = 0 - -// The following Regular Expressions can be used for tokenizing, -// validating, and parsing SemVer version strings. - -// ## Numeric Identifier -// A single `0`, or a non-zero digit followed by zero or more digits. - -var NUMERICIDENTIFIER = R++ -src[NUMERICIDENTIFIER] = '0|[1-9]\\d*' -var NUMERICIDENTIFIERLOOSE = R++ -src[NUMERICIDENTIFIERLOOSE] = '[0-9]+' - -// ## Non-numeric Identifier -// Zero or more digits, followed by a letter or hyphen, and then zero or -// more letters, digits, or hyphens. - -var NONNUMERICIDENTIFIER = R++ -src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*' - -// ## Main Version -// Three dot-separated numeric identifiers. - -var MAINVERSION = R++ -src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')\\.' + - '(' + src[NUMERICIDENTIFIER] + ')' - -var MAINVERSIONLOOSE = R++ -src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + - '(' + src[NUMERICIDENTIFIERLOOSE] + ')' - -// ## Pre-release Version Identifier -// A numeric identifier, or a non-numeric identifier. - -var PRERELEASEIDENTIFIER = R++ -src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + - '|' + src[NONNUMERICIDENTIFIER] + ')' - -var PRERELEASEIDENTIFIERLOOSE = R++ -src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + - '|' + src[NONNUMERICIDENTIFIER] + ')' - -// ## Pre-release Version -// Hyphen, followed by one or more dot-separated pre-release version -// identifiers. - -var PRERELEASE = R++ -src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + - '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))' - -var PRERELEASELOOSE = R++ -src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + - '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))' - -// ## Build Metadata Identifier -// Any combination of digits, letters, or hyphens. - -var BUILDIDENTIFIER = R++ -src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+' - -// ## Build Metadata -// Plus sign, followed by one or more period-separated build metadata -// identifiers. - -var BUILD = R++ -src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + - '(?:\\.' + src[BUILDIDENTIFIER] + ')*))' - -// ## Full Version String -// A main version, followed optionally by a pre-release version and -// build metadata. - -// Note that the only major, minor, patch, and pre-release sections of -// the version string are capturing groups. The build metadata is not a -// capturing group, because it should not ever be used in version -// comparison. - -var FULL = R++ -var FULLPLAIN = 'v?' + src[MAINVERSION] + - src[PRERELEASE] + '?' + - src[BUILD] + '?' - -src[FULL] = '^' + FULLPLAIN + '$' - -// like full, but allows v1.2.3 and =1.2.3, which people do sometimes. -// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty -// common in the npm registry. -var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + - src[PRERELEASELOOSE] + '?' + - src[BUILD] + '?' - -var LOOSE = R++ -src[LOOSE] = '^' + LOOSEPLAIN + '$' - -var GTLT = R++ -src[GTLT] = '((?:<|>)?=?)' - -// Something like "2.*" or "1.2.x". -// Note that "x.x" is a valid xRange identifer, meaning "any version" -// Only the first item is strictly required. -var XRANGEIDENTIFIERLOOSE = R++ -src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*' -var XRANGEIDENTIFIER = R++ -src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*' - -var XRANGEPLAIN = R++ -src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + - '(?:' + src[PRERELEASE] + ')?' + - src[BUILD] + '?' + - ')?)?' - -var XRANGEPLAINLOOSE = R++ -src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + - '(?:' + src[PRERELEASELOOSE] + ')?' + - src[BUILD] + '?' + - ')?)?' - -var XRANGE = R++ -src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$' -var XRANGELOOSE = R++ -src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$' - -// Coercion. -// Extract anything that could conceivably be a part of a valid semver -var COERCE = R++ -src[COERCE] = '(?:^|[^\\d])' + - '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + - '(?:$|[^\\d])' - -// Tilde ranges. -// Meaning is "reasonably at or greater than" -var LONETILDE = R++ -src[LONETILDE] = '(?:~>?)' - -var TILDETRIM = R++ -src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+' -re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g') -var tildeTrimReplace = '$1~' - -var TILDE = R++ -src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$' -var TILDELOOSE = R++ -src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$' - -// Caret ranges. -// Meaning is "at least and backwards compatible with" -var LONECARET = R++ -src[LONECARET] = '(?:\\^)' - -var CARETTRIM = R++ -src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+' -re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g') -var caretTrimReplace = '$1^' - -var CARET = R++ -src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$' -var CARETLOOSE = R++ -src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$' - -// A simple gt/lt/eq thing, or just "" to indicate "any version" -var COMPARATORLOOSE = R++ -src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$' -var COMPARATOR = R++ -src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$' - -// An expression to strip any whitespace between the gtlt and the thing -// it modifies, so that `> 1.2.3` ==> `>1.2.3` -var COMPARATORTRIM = R++ -src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + - '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')' - -// this one has to use the /g flag -re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g') -var comparatorTrimReplace = '$1$2$3' - -// Something like `1.2.3 - 1.2.4` -// Note that these all use the loose form, because they'll be -// checked against either the strict or loose comparator form -// later. -var HYPHENRANGE = R++ -src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAIN] + ')' + - '\\s*$' - -var HYPHENRANGELOOSE = R++ -src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s+-\\s+' + - '(' + src[XRANGEPLAINLOOSE] + ')' + - '\\s*$' - -// Star ranges basically just allow anything at all. -var STAR = R++ -src[STAR] = '(<|>)?=?\\s*\\*' - -// Compile to actual regexp objects. -// All are flag-free, unless they were created above with a flag. -for (var i = 0; i < R; i++) { - debug(i, src[i]) - if (!re[i]) { - re[i] = new RegExp(src[i]) - } -} - -exports.parse = parse -function parse (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (version instanceof SemVer) { - return version - } - - if (typeof version !== 'string') { - return null - } - - if (version.length > MAX_LENGTH) { - return null - } - - var r = options.loose ? re[LOOSE] : re[FULL] - if (!r.test(version)) { - return null - } - - try { - return new SemVer(version, options) - } catch (er) { - return null - } -} - -exports.valid = valid -function valid (version, options) { - var v = parse(version, options) - return v ? v.version : null -} - -exports.clean = clean -function clean (version, options) { - var s = parse(version.trim().replace(/^[=v]+/, ''), options) - return s ? s.version : null -} - -exports.SemVer = SemVer - -function SemVer (version, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - if (version instanceof SemVer) { - if (version.loose === options.loose) { - return version - } else { - version = version.version - } - } else if (typeof version !== 'string') { - throw new TypeError('Invalid Version: ' + version) - } - - if (version.length > MAX_LENGTH) { - throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') - } - - if (!(this instanceof SemVer)) { - return new SemVer(version, options) - } - - debug('SemVer', version, options) - this.options = options - this.loose = !!options.loose - - var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL]) - - if (!m) { - throw new TypeError('Invalid Version: ' + version) - } - - this.raw = version - - // these are actually numbers - this.major = +m[1] - this.minor = +m[2] - this.patch = +m[3] - - if (this.major > MAX_SAFE_INTEGER || this.major < 0) { - throw new TypeError('Invalid major version') - } - - if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { - throw new TypeError('Invalid minor version') - } - - if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { - throw new TypeError('Invalid patch version') - } - - // numberify any prerelease numeric ids - if (!m[4]) { - this.prerelease = [] - } else { - this.prerelease = m[4].split('.').map(function (id) { - if (/^[0-9]+$/.test(id)) { - var num = +id - if (num >= 0 && num < MAX_SAFE_INTEGER) { - return num - } - } - return id - }) - } - - this.build = m[5] ? m[5].split('.') : [] - this.format() -} - -SemVer.prototype.format = function () { - this.version = this.major + '.' + this.minor + '.' + this.patch - if (this.prerelease.length) { - this.version += '-' + this.prerelease.join('.') - } - return this.version -} - -SemVer.prototype.toString = function () { - return this.version -} - -SemVer.prototype.compare = function (other) { - debug('SemVer.compare', this.version, this.options, other) - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } - - return this.compareMain(other) || this.comparePre(other) -} - -SemVer.prototype.compareMain = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } - - return compareIdentifiers(this.major, other.major) || - compareIdentifiers(this.minor, other.minor) || - compareIdentifiers(this.patch, other.patch) -} - -SemVer.prototype.comparePre = function (other) { - if (!(other instanceof SemVer)) { - other = new SemVer(other, this.options) - } - - // NOT having a prerelease is > having one - if (this.prerelease.length && !other.prerelease.length) { - return -1 - } else if (!this.prerelease.length && other.prerelease.length) { - return 1 - } else if (!this.prerelease.length && !other.prerelease.length) { - return 0 - } - - var i = 0 - do { - var a = this.prerelease[i] - var b = other.prerelease[i] - debug('prerelease compare', i, a, b) - if (a === undefined && b === undefined) { - return 0 - } else if (b === undefined) { - return 1 - } else if (a === undefined) { - return -1 - } else if (a === b) { - continue - } else { - return compareIdentifiers(a, b) - } - } while (++i) -} - -// preminor will bump the version up to the next minor release, and immediately -// down to pre-release. premajor and prepatch work the same way. -SemVer.prototype.inc = function (release, identifier) { - switch (release) { - case 'premajor': - this.prerelease.length = 0 - this.patch = 0 - this.minor = 0 - this.major++ - this.inc('pre', identifier) - break - case 'preminor': - this.prerelease.length = 0 - this.patch = 0 - this.minor++ - this.inc('pre', identifier) - break - case 'prepatch': - // If this is already a prerelease, it will bump to the next version - // drop any prereleases that might already exist, since they are not - // relevant at this point. - this.prerelease.length = 0 - this.inc('patch', identifier) - this.inc('pre', identifier) - break - // If the input is a non-prerelease version, this acts the same as - // prepatch. - case 'prerelease': - if (this.prerelease.length === 0) { - this.inc('patch', identifier) - } - this.inc('pre', identifier) - break - - case 'major': - // If this is a pre-major version, bump up to the same major version. - // Otherwise increment major. - // 1.0.0-5 bumps to 1.0.0 - // 1.1.0 bumps to 2.0.0 - if (this.minor !== 0 || - this.patch !== 0 || - this.prerelease.length === 0) { - this.major++ - } - this.minor = 0 - this.patch = 0 - this.prerelease = [] - break - case 'minor': - // If this is a pre-minor version, bump up to the same minor version. - // Otherwise increment minor. - // 1.2.0-5 bumps to 1.2.0 - // 1.2.1 bumps to 1.3.0 - if (this.patch !== 0 || this.prerelease.length === 0) { - this.minor++ - } - this.patch = 0 - this.prerelease = [] - break - case 'patch': - // If this is not a pre-release version, it will increment the patch. - // If it is a pre-release it will bump up to the same patch version. - // 1.2.0-5 patches to 1.2.0 - // 1.2.0 patches to 1.2.1 - if (this.prerelease.length === 0) { - this.patch++ - } - this.prerelease = [] - break - // This probably shouldn't be used publicly. - // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. - case 'pre': - if (this.prerelease.length === 0) { - this.prerelease = [0] - } else { - var i = this.prerelease.length - while (--i >= 0) { - if (typeof this.prerelease[i] === 'number') { - this.prerelease[i]++ - i = -2 - } - } - if (i === -1) { - // didn't increment anything - this.prerelease.push(0) - } - } - if (identifier) { - // 1.2.0-beta.1 bumps to 1.2.0-beta.2, - // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 - if (this.prerelease[0] === identifier) { - if (isNaN(this.prerelease[1])) { - this.prerelease = [identifier, 0] - } - } else { - this.prerelease = [identifier, 0] - } - } - break - - default: - throw new Error('invalid increment argument: ' + release) - } - this.format() - this.raw = this.version - return this -} - -exports.inc = inc -function inc (version, release, loose, identifier) { - if (typeof (loose) === 'string') { - identifier = loose - loose = undefined - } - - try { - return new SemVer(version, loose).inc(release, identifier).version - } catch (er) { - return null - } -} - -exports.diff = diff -function diff (version1, version2) { - if (eq(version1, version2)) { - return null - } else { - var v1 = parse(version1) - var v2 = parse(version2) - var prefix = '' - if (v1.prerelease.length || v2.prerelease.length) { - prefix = 'pre' - var defaultResult = 'prerelease' - } - for (var key in v1) { - if (key === 'major' || key === 'minor' || key === 'patch') { - if (v1[key] !== v2[key]) { - return prefix + key - } - } - } - return defaultResult // may be undefined - } -} - -exports.compareIdentifiers = compareIdentifiers - -var numeric = /^[0-9]+$/ -function compareIdentifiers (a, b) { - var anum = numeric.test(a) - var bnum = numeric.test(b) - - if (anum && bnum) { - a = +a - b = +b - } - - return a === b ? 0 - : (anum && !bnum) ? -1 - : (bnum && !anum) ? 1 - : a < b ? -1 - : 1 -} - -exports.rcompareIdentifiers = rcompareIdentifiers -function rcompareIdentifiers (a, b) { - return compareIdentifiers(b, a) -} - -exports.major = major -function major (a, loose) { - return new SemVer(a, loose).major -} - -exports.minor = minor -function minor (a, loose) { - return new SemVer(a, loose).minor -} - -exports.patch = patch -function patch (a, loose) { - return new SemVer(a, loose).patch -} - -exports.compare = compare -function compare (a, b, loose) { - return new SemVer(a, loose).compare(new SemVer(b, loose)) -} - -exports.compareLoose = compareLoose -function compareLoose (a, b) { - return compare(a, b, true) -} - -exports.rcompare = rcompare -function rcompare (a, b, loose) { - return compare(b, a, loose) -} - -exports.sort = sort -function sort (list, loose) { - return list.sort(function (a, b) { - return exports.compare(a, b, loose) - }) -} - -exports.rsort = rsort -function rsort (list, loose) { - return list.sort(function (a, b) { - return exports.rcompare(a, b, loose) - }) -} - -exports.gt = gt -function gt (a, b, loose) { - return compare(a, b, loose) > 0 -} - -exports.lt = lt -function lt (a, b, loose) { - return compare(a, b, loose) < 0 -} - -exports.eq = eq -function eq (a, b, loose) { - return compare(a, b, loose) === 0 -} - -exports.neq = neq -function neq (a, b, loose) { - return compare(a, b, loose) !== 0 -} - -exports.gte = gte -function gte (a, b, loose) { - return compare(a, b, loose) >= 0 -} - -exports.lte = lte -function lte (a, b, loose) { - return compare(a, b, loose) <= 0 -} - -exports.cmp = cmp -function cmp (a, op, b, loose) { - switch (op) { - case '===': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a === b - - case '!==': - if (typeof a === 'object') - a = a.version - if (typeof b === 'object') - b = b.version - return a !== b - - case '': - case '=': - case '==': - return eq(a, b, loose) - - case '!=': - return neq(a, b, loose) - - case '>': - return gt(a, b, loose) - - case '>=': - return gte(a, b, loose) - - case '<': - return lt(a, b, loose) - - case '<=': - return lte(a, b, loose) - - default: - throw new TypeError('Invalid operator: ' + op) - } -} - -exports.Comparator = Comparator -function Comparator (comp, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (comp instanceof Comparator) { - if (comp.loose === !!options.loose) { - return comp - } else { - comp = comp.value - } - } - - if (!(this instanceof Comparator)) { - return new Comparator(comp, options) - } - - debug('comparator', comp, options) - this.options = options - this.loose = !!options.loose - this.parse(comp) - - if (this.semver === ANY) { - this.value = '' - } else { - this.value = this.operator + this.semver.version - } - - debug('comp', this) -} - -var ANY = {} -Comparator.prototype.parse = function (comp) { - var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var m = comp.match(r) - - if (!m) { - throw new TypeError('Invalid comparator: ' + comp) - } - - this.operator = m[1] - if (this.operator === '=') { - this.operator = '' - } - - // if it literally is just '>' or '' then allow anything. - if (!m[2]) { - this.semver = ANY - } else { - this.semver = new SemVer(m[2], this.options.loose) - } -} - -Comparator.prototype.toString = function () { - return this.value -} - -Comparator.prototype.test = function (version) { - debug('Comparator.test', version, this.options.loose) - - if (this.semver === ANY) { - return true - } - - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } - - return cmp(version, this.operator, this.semver, this.options) -} - -Comparator.prototype.intersects = function (comp, options) { - if (!(comp instanceof Comparator)) { - throw new TypeError('a Comparator is required') - } - - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - var rangeTmp - - if (this.operator === '') { - rangeTmp = new Range(comp.value, options) - return satisfies(this.value, rangeTmp, options) - } else if (comp.operator === '') { - rangeTmp = new Range(this.value, options) - return satisfies(comp.semver, rangeTmp, options) - } - - var sameDirectionIncreasing = - (this.operator === '>=' || this.operator === '>') && - (comp.operator === '>=' || comp.operator === '>') - var sameDirectionDecreasing = - (this.operator === '<=' || this.operator === '<') && - (comp.operator === '<=' || comp.operator === '<') - var sameSemVer = this.semver.version === comp.semver.version - var differentDirectionsInclusive = - (this.operator === '>=' || this.operator === '<=') && - (comp.operator === '>=' || comp.operator === '<=') - var oppositeDirectionsLessThan = - cmp(this.semver, '<', comp.semver, options) && - ((this.operator === '>=' || this.operator === '>') && - (comp.operator === '<=' || comp.operator === '<')) - var oppositeDirectionsGreaterThan = - cmp(this.semver, '>', comp.semver, options) && - ((this.operator === '<=' || this.operator === '<') && - (comp.operator === '>=' || comp.operator === '>')) - - return sameDirectionIncreasing || sameDirectionDecreasing || - (sameSemVer && differentDirectionsInclusive) || - oppositeDirectionsLessThan || oppositeDirectionsGreaterThan -} - -exports.Range = Range -function Range (range, options) { - if (!options || typeof options !== 'object') { - options = { - loose: !!options, - includePrerelease: false - } - } - - if (range instanceof Range) { - if (range.loose === !!options.loose && - range.includePrerelease === !!options.includePrerelease) { - return range - } else { - return new Range(range.raw, options) - } - } - - if (range instanceof Comparator) { - return new Range(range.value, options) - } - - if (!(this instanceof Range)) { - return new Range(range, options) - } - - this.options = options - this.loose = !!options.loose - this.includePrerelease = !!options.includePrerelease - - // First, split based on boolean or || - this.raw = range - this.set = range.split(/\s*\|\|\s*/).map(function (range) { - return this.parseRange(range.trim()) - }, this).filter(function (c) { - // throw out any that are not relevant for whatever reason - return c.length - }) - - if (!this.set.length) { - throw new TypeError('Invalid SemVer Range: ' + range) - } - - this.format() -} - -Range.prototype.format = function () { - this.range = this.set.map(function (comps) { - return comps.join(' ').trim() - }).join('||').trim() - return this.range -} - -Range.prototype.toString = function () { - return this.range -} - -Range.prototype.parseRange = function (range) { - var loose = this.options.loose - range = range.trim() - // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` - var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE] - range = range.replace(hr, hyphenReplace) - debug('hyphen replace', range) - // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` - range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace) - debug('comparator trim', range, re[COMPARATORTRIM]) - - // `~ 1.2.3` => `~1.2.3` - range = range.replace(re[TILDETRIM], tildeTrimReplace) - - // `^ 1.2.3` => `^1.2.3` - range = range.replace(re[CARETTRIM], caretTrimReplace) - - // normalize spaces - range = range.split(/\s+/).join(' ') - - // At this point, the range is completely trimmed and - // ready to be split into comparators. - - var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR] - var set = range.split(' ').map(function (comp) { - return parseComparator(comp, this.options) - }, this).join(' ').split(/\s+/) - if (this.options.loose) { - // in loose mode, throw out any that are not valid comparators - set = set.filter(function (comp) { - return !!comp.match(compRe) - }) - } - set = set.map(function (comp) { - return new Comparator(comp, this.options) - }, this) - - return set -} - -Range.prototype.intersects = function (range, options) { - if (!(range instanceof Range)) { - throw new TypeError('a Range is required') - } - - return this.set.some(function (thisComparators) { - return thisComparators.every(function (thisComparator) { - return range.set.some(function (rangeComparators) { - return rangeComparators.every(function (rangeComparator) { - return thisComparator.intersects(rangeComparator, options) - }) - }) - }) - }) -} - -// Mostly just for testing and legacy API reasons -exports.toComparators = toComparators -function toComparators (range, options) { - return new Range(range, options).set.map(function (comp) { - return comp.map(function (c) { - return c.value - }).join(' ').trim().split(' ') - }) -} - -// comprised of xranges, tildes, stars, and gtlt's at this point. -// already replaced the hyphen ranges -// turn into a set of JUST comparators. -function parseComparator (comp, options) { - debug('comp', comp, options) - comp = replaceCarets(comp, options) - debug('caret', comp) - comp = replaceTildes(comp, options) - debug('tildes', comp) - comp = replaceXRanges(comp, options) - debug('xrange', comp) - comp = replaceStars(comp, options) - debug('stars', comp) - return comp -} - -function isX (id) { - return !id || id.toLowerCase() === 'x' || id === '*' -} - -// ~, ~> --> * (any, kinda silly) -// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 -// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 -// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 -// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 -// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 -function replaceTildes (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceTilde(comp, options) - }).join(' ') -} - -function replaceTilde (comp, options) { - var r = options.loose ? re[TILDELOOSE] : re[TILDE] - return comp.replace(r, function (_, M, m, p, pr) { - debug('tilde', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - // ~1.2 == >=1.2.0 <1.3.0 - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else if (pr) { - debug('replaceTilde pr', pr) - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } else { - // ~1.2.3 == >=1.2.3 <1.3.0 - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - - debug('tilde return', ret) - return ret - }) -} - -// ^ --> * (any, kinda silly) -// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 -// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 -// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 -// ^1.2.3 --> >=1.2.3 <2.0.0 -// ^1.2.0 --> >=1.2.0 <2.0.0 -function replaceCarets (comp, options) { - return comp.trim().split(/\s+/).map(function (comp) { - return replaceCaret(comp, options) - }).join(' ') -} - -function replaceCaret (comp, options) { - debug('caret', comp, options) - var r = options.loose ? re[CARETLOOSE] : re[CARET] - return comp.replace(r, function (_, M, m, p, pr) { - debug('caret', comp, _, M, m, p, pr) - var ret - - if (isX(M)) { - ret = '' - } else if (isX(m)) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (isX(p)) { - if (M === '0') { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } else { - ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0' - } - } else if (pr) { - debug('replaceCaret pr', pr) - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + '-' + pr + - ' <' + (+M + 1) + '.0.0' - } - } else { - debug('no pr') - if (M === '0') { - if (m === '0') { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + m + '.' + (+p + 1) - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + M + '.' + (+m + 1) + '.0' - } - } else { - ret = '>=' + M + '.' + m + '.' + p + - ' <' + (+M + 1) + '.0.0' - } - } - - debug('caret return', ret) - return ret - }) -} - -function replaceXRanges (comp, options) { - debug('replaceXRanges', comp, options) - return comp.split(/\s+/).map(function (comp) { - return replaceXRange(comp, options) - }).join(' ') -} - -function replaceXRange (comp, options) { - comp = comp.trim() - var r = options.loose ? re[XRANGELOOSE] : re[XRANGE] - return comp.replace(r, function (ret, gtlt, M, m, p, pr) { - debug('xRange', comp, ret, gtlt, M, m, p, pr) - var xM = isX(M) - var xm = xM || isX(m) - var xp = xm || isX(p) - var anyX = xp - - if (gtlt === '=' && anyX) { - gtlt = '' - } - - if (xM) { - if (gtlt === '>' || gtlt === '<') { - // nothing is allowed - ret = '<0.0.0' - } else { - // nothing is forbidden - ret = '*' - } - } else if (gtlt && anyX) { - // we know patch is an x, because we have any x at all. - // replace X with 0 - if (xm) { - m = 0 - } - p = 0 - - if (gtlt === '>') { - // >1 => >=2.0.0 - // >1.2 => >=1.3.0 - // >1.2.3 => >= 1.2.4 - gtlt = '>=' - if (xm) { - M = +M + 1 - m = 0 - p = 0 - } else { - m = +m + 1 - p = 0 - } - } else if (gtlt === '<=') { - // <=0.7.x is actually <0.8.0, since any 0.7.x should - // pass. Similarly, <=7.x is actually <8.0.0, etc. - gtlt = '<' - if (xm) { - M = +M + 1 - } else { - m = +m + 1 - } - } - - ret = gtlt + M + '.' + m + '.' + p - } else if (xm) { - ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0' - } else if (xp) { - ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0' - } - - debug('xRange return', ret) - - return ret - }) -} - -// Because * is AND-ed with everything else in the comparator, -// and '' means "any version", just remove the *s entirely. -function replaceStars (comp, options) { - debug('replaceStars', comp, options) - // Looseness is ignored here. star is always as loose as it gets! - return comp.trim().replace(re[STAR], '') -} - -// This function is passed to string.replace(re[HYPHENRANGE]) -// M, m, patch, prerelease, build -// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 -// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do -// 1.2 - 3.4 => >=1.2.0 <3.5.0 -function hyphenReplace ($0, - from, fM, fm, fp, fpr, fb, - to, tM, tm, tp, tpr, tb) { - if (isX(fM)) { - from = '' - } else if (isX(fm)) { - from = '>=' + fM + '.0.0' - } else if (isX(fp)) { - from = '>=' + fM + '.' + fm + '.0' - } else { - from = '>=' + from - } - - if (isX(tM)) { - to = '' - } else if (isX(tm)) { - to = '<' + (+tM + 1) + '.0.0' - } else if (isX(tp)) { - to = '<' + tM + '.' + (+tm + 1) + '.0' - } else if (tpr) { - to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr - } else { - to = '<=' + to - } - - return (from + ' ' + to).trim() -} - -// if ANY of the sets match ALL of its comparators, then pass -Range.prototype.test = function (version) { - if (!version) { - return false - } - - if (typeof version === 'string') { - version = new SemVer(version, this.options) - } - - for (var i = 0; i < this.set.length; i++) { - if (testSet(this.set[i], version, this.options)) { - return true - } - } - return false -} - -function testSet (set, version, options) { - for (var i = 0; i < set.length; i++) { - if (!set[i].test(version)) { - return false - } - } - - if (version.prerelease.length && !options.includePrerelease) { - // Find the set of versions that are allowed to have prereleases - // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 - // That should allow `1.2.3-pr.2` to pass. - // However, `1.2.4-alpha.notready` should NOT be allowed, - // even though it's within the range set by the comparators. - for (i = 0; i < set.length; i++) { - debug(set[i].semver) - if (set[i].semver === ANY) { - continue - } - - if (set[i].semver.prerelease.length > 0) { - var allowed = set[i].semver - if (allowed.major === version.major && - allowed.minor === version.minor && - allowed.patch === version.patch) { - return true - } - } - } - - // Version has a -pre, but it's not one of the ones we like. - return false - } - - return true -} - -exports.satisfies = satisfies -function satisfies (version, range, options) { - try { - range = new Range(range, options) - } catch (er) { - return false - } - return range.test(version) -} - -exports.maxSatisfying = maxSatisfying -function maxSatisfying (versions, range, options) { - var max = null - var maxSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!max || maxSV.compare(v) === -1) { - // compare(max, v, true) - max = v - maxSV = new SemVer(max, options) - } - } - }) - return max -} - -exports.minSatisfying = minSatisfying -function minSatisfying (versions, range, options) { - var min = null - var minSV = null - try { - var rangeObj = new Range(range, options) - } catch (er) { - return null - } - versions.forEach(function (v) { - if (rangeObj.test(v)) { - // satisfies(v, range, options) - if (!min || minSV.compare(v) === 1) { - // compare(min, v, true) - min = v - minSV = new SemVer(min, options) - } - } - }) - return min -} + } -exports.minVersion = minVersion -function minVersion (range, loose) { - range = new Range(range, loose) + if (error.message.includes('null bytes')) { + throw error; + } - var minver = new SemVer('0.0.0') - if (range.test(minver)) { - return minver - } + return make(path.dirname(pth)).then(() => make(pth)); + } - minver = new SemVer('0.0.0-0') - if (range.test(minver)) { - return minver - } + return stat(pth) + .then(stats => stats.isDirectory() ? pth : Promise.reject()) + .catch(() => { + throw error; + }); + }); + }; - minver = null - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] + return make(path.resolve(input)); +}); - comparators.forEach(function (comparator) { - // Clone to avoid manipulating the comparator's semver object. - var compver = new SemVer(comparator.semver.version) - switch (comparator.operator) { - case '>': - if (compver.prerelease.length === 0) { - compver.patch++ - } else { - compver.prerelease.push(0) - } - compver.raw = compver.format() - /* fallthrough */ - case '': - case '>=': - if (!minver || gt(minver, compver)) { - minver = compver - } - break - case '<': - case '<=': - /* Ignore maximum versions */ - break - /* istanbul ignore next */ - default: - throw new Error('Unexpected operation: ' + comparator.operator) - } - }) - } +module.exports = makeDir; +module.exports.default = makeDir; - if (minver && range.test(minver)) { - return minver - } +module.exports.sync = (input, options) => { + checkPath(input); + options = Object.assign({}, defaults, options); - return null -} + if (useNativeRecursiveOption && options.fs.mkdirSync === fs.mkdirSync) { + const pth = path.resolve(input); -exports.validRange = validRange -function validRange (range, options) { - try { - // Return '*' instead of '' so that truthiness works. - // This will throw if it's invalid anyway - return new Range(range, options).range || '*' - } catch (er) { - return null - } -} + fs.mkdirSync(pth, { + mode: options.mode, + recursive: true + }); -// Determine if version is less than all the versions possible in the range -exports.ltr = ltr -function ltr (version, range, options) { - return outside(version, range, '<', options) -} + return pth; + } -// Determine if version is greater than all the versions possible in the range. -exports.gtr = gtr -function gtr (version, range, options) { - return outside(version, range, '>', options) -} + const make = pth => { + try { + options.fs.mkdirSync(pth, options.mode); + } catch (error) { + if (error.code === 'EPERM') { + throw error; + } -exports.outside = outside -function outside (version, range, hilo, options) { - version = new SemVer(version, options) - range = new Range(range, options) + if (error.code === 'ENOENT') { + if (path.dirname(pth) === pth) { + throw permissionError(pth); + } - var gtfn, ltefn, ltfn, comp, ecomp - switch (hilo) { - case '>': - gtfn = gt - ltefn = lte - ltfn = lt - comp = '>' - ecomp = '>=' - break - case '<': - gtfn = lt - ltefn = gte - ltfn = gt - comp = '<' - ecomp = '<=' - break - default: - throw new TypeError('Must provide a hilo val of "<" or ">"') - } + if (error.message.includes('null bytes')) { + throw error; + } - // If it satisifes the range it is not outside - if (satisfies(version, range, options)) { - return false - } + make(path.dirname(pth)); + return make(pth); + } - // From now on, variable terms are as if we're in "gtr" mode. - // but note that everything is flipped for the "ltr" function. + try { + if (!options.fs.statSync(pth).isDirectory()) { + throw new Error('The path is not a directory'); + } + } catch (_) { + throw error; + } + } - for (var i = 0; i < range.set.length; ++i) { - var comparators = range.set[i] + return pth; + }; - var high = null - var low = null + return make(path.resolve(input)); +}; - comparators.forEach(function (comparator) { - if (comparator.semver === ANY) { - comparator = new Comparator('>=0.0.0') - } - high = high || comparator - low = low || comparator - if (gtfn(comparator.semver, high.semver, options)) { - high = comparator - } else if (ltfn(comparator.semver, low.semver, options)) { - low = comparator - } - }) - // If the edge version comparator has a operator then our version - // isn't outside it - if (high.operator === comp || high.operator === ecomp) { - return false - } +/***/ }), +/* 560 */ +/***/ (function(module, exports, __webpack_require__) { - // If the lowest version comparator has an operator and our version - // is less than it then it isn't higher than the range - if ((!low.operator || low.operator === comp) && - ltefn(version, low.semver)) { - return false - } else if (low.operator === ecomp && ltfn(version, low.semver)) { - return false - } - } - return true -} +"use strict"; -exports.prerelease = prerelease -function prerelease (version, options) { - var parsed = parse(version, options) - return (parsed && parsed.prerelease.length) ? parsed.prerelease : null -} -exports.intersects = intersects -function intersects (r1, r2, options) { - r1 = new Range(r1, options) - r2 = new Range(r2, options) - return r1.intersects(r2) -} +const processFn = (fn, options) => function (...args) { + const P = options.promiseModule; -exports.coerce = coerce -function coerce (version) { - if (version instanceof SemVer) { - return version - } + return new P((resolve, reject) => { + if (options.multiArgs) { + args.push((...result) => { + if (options.errorFirst) { + if (result[0]) { + reject(result); + } else { + result.shift(); + resolve(result); + } + } else { + resolve(result); + } + }); + } else if (options.errorFirst) { + args.push((error, result) => { + if (error) { + reject(error); + } else { + resolve(result); + } + }); + } else { + args.push(resolve); + } - if (typeof version !== 'string') { - return null - } + fn.apply(this, args); + }); +}; - var match = version.match(re[COERCE]) +module.exports = (input, options) => { + options = Object.assign({ + exclude: [/.+(Sync|Stream)$/], + errorFirst: true, + promiseModule: Promise + }, options); - if (match == null) { - return null - } + const objType = typeof input; + if (!(input !== null && (objType === 'object' || objType === 'function'))) { + throw new TypeError(`Expected \`input\` to be a \`Function\` or \`Object\`, got \`${input === null ? 'null' : objType}\``); + } - return parse(match[1] + - '.' + (match[2] || '0') + - '.' + (match[3] || '0')) -} + const filter = key => { + const match = pattern => typeof pattern === 'string' ? key === pattern : pattern.test(key); + return options.include ? options.include.some(match) : !options.exclude.some(match); + }; + + let ret; + if (objType === 'function') { + ret = function (...args) { + return options.excludeMain ? input(...args) : processFn(input, options).apply(this, args); + }; + } else { + ret = Object.create(Object.getPrototypeOf(input)); + } + + for (const key in input) { // eslint-disable-line guard-for-in + const property = input[key]; + ret[key] = typeof property === 'function' && filter(key) ? processFn(property, options) : property; + } + + return ret; +}; /***/ }), -/* 562 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56384,7 +55082,7 @@ module.exports = (input, options) => { /***/ }), -/* 563 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56513,7 +55211,7 @@ module.exports = str => { /***/ }), -/* 564 */ +/* 563 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56522,7 +55220,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackage", function() { return runScriptInPackage; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runScriptInPackageStreaming", function() { return runScriptInPackageStreaming; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "yarnWorkspacesInfo", function() { return yarnWorkspacesInfo; }); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(565); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(564); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -56592,7 +55290,7 @@ async function yarnWorkspacesInfo(directory) { } /***/ }), -/* 565 */ +/* 564 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -56603,9 +55301,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(351); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var log_symbols__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(566); +/* harmony import */ var log_symbols__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(565); /* harmony import */ var log_symbols__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(log_symbols__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(571); +/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(570); /* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } @@ -56671,12 +55369,12 @@ function spawnStreaming(command, args, opts, { } /***/ }), -/* 566 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(567); +const chalk = __webpack_require__(566); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -56698,16 +55396,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 567 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(3); -const ansiStyles = __webpack_require__(568); -const stdoutColor = __webpack_require__(569).stdout; +const ansiStyles = __webpack_require__(567); +const stdoutColor = __webpack_require__(568).stdout; -const template = __webpack_require__(570); +const template = __webpack_require__(569); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -56933,7 +55631,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 568 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57106,7 +55804,7 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(5)(module))) /***/ }), -/* 569 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57248,7 +55946,7 @@ module.exports = { /***/ }), -/* 570 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57383,7 +56081,7 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 571 */ +/* 570 */ /***/ (function(module, exports, __webpack_require__) { // Copyright IBM Corp. 2014,2018. All Rights Reserved. @@ -57391,12 +56089,12 @@ module.exports = (chalk, tmp) => { // This file is licensed under the Apache License 2.0. // License text available at https://opensource.org/licenses/Apache-2.0 -module.exports = __webpack_require__(572); -module.exports.cli = __webpack_require__(576); +module.exports = __webpack_require__(571); +module.exports.cli = __webpack_require__(575); /***/ }), -/* 572 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57411,9 +56109,9 @@ var stream = __webpack_require__(27); var util = __webpack_require__(29); var fs = __webpack_require__(23); -var through = __webpack_require__(573); -var duplexer = __webpack_require__(574); -var StringDecoder = __webpack_require__(575).StringDecoder; +var through = __webpack_require__(572); +var duplexer = __webpack_require__(573); +var StringDecoder = __webpack_require__(574).StringDecoder; module.exports = Logger; @@ -57602,7 +56300,7 @@ function lineMerger(host) { /***/ }), -/* 573 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(27) @@ -57716,7 +56414,7 @@ function through (write, end, opts) { /***/ }), -/* 574 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(27) @@ -57809,13 +56507,13 @@ function duplex(writer, reader) { /***/ }), -/* 575 */ +/* 574 */ /***/ (function(module, exports) { module.exports = require("string_decoder"); /***/ }), -/* 576 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57826,11 +56524,11 @@ module.exports = require("string_decoder"); -var minimist = __webpack_require__(577); +var minimist = __webpack_require__(576); var path = __webpack_require__(16); -var Logger = __webpack_require__(572); -var pkg = __webpack_require__(578); +var Logger = __webpack_require__(571); +var pkg = __webpack_require__(577); module.exports = cli; @@ -57884,7 +56582,7 @@ function usage($0, p) { /***/ }), -/* 577 */ +/* 576 */ /***/ (function(module, exports) { module.exports = function (args, opts) { @@ -58126,13 +56824,13 @@ function isNumber (x) { /***/ }), -/* 578 */ +/* 577 */ /***/ (function(module) { module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham \",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); /***/ }), -/* 579 */ +/* 578 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58145,7 +56843,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(29); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(580); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(579); /* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20); /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(517); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(501); @@ -58240,7 +56938,7 @@ function packagesFromGlobPattern({ } /***/ }), -/* 580 */ +/* 579 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58309,7 +57007,7 @@ function getProjectPaths({ } /***/ }), -/* 581 */ +/* 580 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -58317,13 +57015,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAllChecksums", function() { return getAllChecksums; }); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(23); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(582); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(581); /* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(29); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(351); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(583); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(582); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -58540,19 +57238,19 @@ async function getAllChecksums(kbn, log) { } /***/ }), -/* 582 */ +/* 581 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), -/* 583 */ +/* 582 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readYarnLock", function() { return readYarnLock; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(584); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(583); /* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(20); /* @@ -58596,7 +57294,7 @@ async function readYarnLock(kbn) { } /***/ }), -/* 584 */ +/* 583 */ /***/ (function(module, exports, __webpack_require__) { module.exports = @@ -60155,7 +58853,7 @@ module.exports = invariant; /* 9 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(582); +module.exports = __webpack_require__(581); /***/ }), /* 10 */, @@ -62479,7 +61177,7 @@ function onceStrict (fn) { /* 63 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(585); +module.exports = __webpack_require__(584); /***/ }), /* 64 */, @@ -68874,13 +67572,13 @@ module.exports = process && support(supportLevel); /******/ ]); /***/ }), -/* 585 */ +/* 584 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 586 */ +/* 585 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -68977,7 +67675,7 @@ class BootstrapCacheFile { } /***/ }), -/* 587 */ +/* 586 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -68985,9 +67683,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(588); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(587); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(676); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(675); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ora__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(16); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__); @@ -69087,21 +67785,21 @@ const CleanCommand = { }; /***/ }), -/* 588 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(29); const path = __webpack_require__(16); -const globby = __webpack_require__(589); -const isGlob = __webpack_require__(606); -const slash = __webpack_require__(667); +const globby = __webpack_require__(588); +const isGlob = __webpack_require__(605); +const slash = __webpack_require__(666); const gracefulFs = __webpack_require__(22); -const isPathCwd = __webpack_require__(669); -const isPathInside = __webpack_require__(670); -const rimraf = __webpack_require__(671); -const pMap = __webpack_require__(672); +const isPathCwd = __webpack_require__(668); +const isPathInside = __webpack_require__(669); +const rimraf = __webpack_require__(670); +const pMap = __webpack_require__(671); const rimrafP = promisify(rimraf); @@ -69215,19 +67913,19 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 589 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const arrayUnion = __webpack_require__(590); -const merge2 = __webpack_require__(591); -const glob = __webpack_require__(592); -const fastGlob = __webpack_require__(597); -const dirGlob = __webpack_require__(663); -const gitignore = __webpack_require__(665); -const {FilterStream, UniqueStream} = __webpack_require__(668); +const arrayUnion = __webpack_require__(589); +const merge2 = __webpack_require__(590); +const glob = __webpack_require__(591); +const fastGlob = __webpack_require__(596); +const dirGlob = __webpack_require__(662); +const gitignore = __webpack_require__(664); +const {FilterStream, UniqueStream} = __webpack_require__(667); const DEFAULT_FILTER = () => false; @@ -69400,7 +68098,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 590 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69412,7 +68110,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 591 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -69526,7 +68224,7 @@ function pauseStreams (streams, options) { /***/ }), -/* 592 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -69575,13 +68273,13 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(593) +var inherits = __webpack_require__(592) var EE = __webpack_require__(379).EventEmitter var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var globSync = __webpack_require__(595) -var common = __webpack_require__(596) +var globSync = __webpack_require__(594) +var common = __webpack_require__(595) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -70322,7 +69020,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 593 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -70332,12 +69030,12 @@ try { module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(594); + module.exports = __webpack_require__(593); } /***/ }), -/* 594 */ +/* 593 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -70370,7 +69068,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 595 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -70380,12 +69078,12 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(592).Glob +var Glob = __webpack_require__(591).Glob var util = __webpack_require__(29) var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var common = __webpack_require__(596) +var common = __webpack_require__(595) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -70862,7 +69560,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 596 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -71108,17 +69806,17 @@ function childrenIgnored (self, path) { /***/ }), -/* 597 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(598); -const async_1 = __webpack_require__(626); -const stream_1 = __webpack_require__(659); -const sync_1 = __webpack_require__(660); -const settings_1 = __webpack_require__(662); -const utils = __webpack_require__(599); +const taskManager = __webpack_require__(597); +const async_1 = __webpack_require__(625); +const stream_1 = __webpack_require__(658); +const sync_1 = __webpack_require__(659); +const settings_1 = __webpack_require__(661); +const utils = __webpack_require__(598); function FastGlob(source, options) { try { assertPatternsInput(source); @@ -71176,13 +69874,13 @@ module.exports = FastGlob; /***/ }), -/* 598 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(599); +const utils = __webpack_require__(598); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -71250,28 +69948,28 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 599 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(600); +const array = __webpack_require__(599); exports.array = array; -const errno = __webpack_require__(601); +const errno = __webpack_require__(600); exports.errno = errno; -const fs = __webpack_require__(602); +const fs = __webpack_require__(601); exports.fs = fs; -const path = __webpack_require__(603); +const path = __webpack_require__(602); exports.path = path; -const pattern = __webpack_require__(604); +const pattern = __webpack_require__(603); exports.pattern = pattern; -const stream = __webpack_require__(625); +const stream = __webpack_require__(624); exports.stream = stream; /***/ }), -/* 600 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71284,7 +69982,7 @@ exports.flatten = flatten; /***/ }), -/* 601 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71297,7 +69995,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 602 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71322,7 +70020,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 603 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71343,16 +70041,16 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 604 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(16); -const globParent = __webpack_require__(605); -const isGlob = __webpack_require__(606); -const micromatch = __webpack_require__(608); +const globParent = __webpack_require__(604); +const isGlob = __webpack_require__(605); +const micromatch = __webpack_require__(607); const GLOBSTAR = '**'; function isStaticPattern(pattern) { return !isDynamicPattern(pattern); @@ -71441,13 +70139,13 @@ exports.matchAny = matchAny; /***/ }), -/* 605 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(606); +var isGlob = __webpack_require__(605); var pathPosixDirname = __webpack_require__(16).posix.dirname; var isWin32 = __webpack_require__(11).platform() === 'win32'; @@ -71482,7 +70180,7 @@ module.exports = function globParent(str) { /***/ }), -/* 606 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -71492,7 +70190,7 @@ module.exports = function globParent(str) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(607); +var isExtglob = __webpack_require__(606); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -71536,7 +70234,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 607 */ +/* 606 */ /***/ (function(module, exports) { /*! @@ -71562,16 +70260,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 608 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const util = __webpack_require__(29); -const braces = __webpack_require__(609); -const picomatch = __webpack_require__(619); -const utils = __webpack_require__(622); +const braces = __webpack_require__(608); +const picomatch = __webpack_require__(618); +const utils = __webpack_require__(621); const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); /** @@ -72036,16 +70734,16 @@ module.exports = micromatch; /***/ }), -/* 609 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(610); -const compile = __webpack_require__(612); -const expand = __webpack_require__(616); -const parse = __webpack_require__(617); +const stringify = __webpack_require__(609); +const compile = __webpack_require__(611); +const expand = __webpack_require__(615); +const parse = __webpack_require__(616); /** * Expand the given pattern or create a regex-compatible string. @@ -72213,13 +70911,13 @@ module.exports = braces; /***/ }), -/* 610 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(611); +const utils = __webpack_require__(610); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -72252,7 +70950,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 611 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72371,14 +71069,14 @@ exports.flatten = (...args) => { /***/ }), -/* 612 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(613); -const utils = __webpack_require__(611); +const fill = __webpack_require__(612); +const utils = __webpack_require__(610); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -72435,7 +71133,7 @@ module.exports = compile; /***/ }), -/* 613 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72449,7 +71147,7 @@ module.exports = compile; const util = __webpack_require__(29); -const toRegexRange = __webpack_require__(614); +const toRegexRange = __webpack_require__(613); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -72691,7 +71389,7 @@ module.exports = fill; /***/ }), -/* 614 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72704,7 +71402,7 @@ module.exports = fill; -const isNumber = __webpack_require__(615); +const isNumber = __webpack_require__(614); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -72986,7 +71684,7 @@ module.exports = toRegexRange; /***/ }), -/* 615 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73011,15 +71709,15 @@ module.exports = function(num) { /***/ }), -/* 616 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(613); -const stringify = __webpack_require__(610); -const utils = __webpack_require__(611); +const fill = __webpack_require__(612); +const stringify = __webpack_require__(609); +const utils = __webpack_require__(610); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -73131,13 +71829,13 @@ module.exports = expand; /***/ }), -/* 617 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(610); +const stringify = __webpack_require__(609); /** * Constants @@ -73159,7 +71857,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(618); +} = __webpack_require__(617); /** * parse @@ -73471,7 +72169,7 @@ module.exports = parse; /***/ }), -/* 618 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73535,26 +72233,26 @@ module.exports = { /***/ }), -/* 619 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(620); +module.exports = __webpack_require__(619); /***/ }), -/* 620 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const scan = __webpack_require__(621); -const parse = __webpack_require__(624); -const utils = __webpack_require__(622); +const scan = __webpack_require__(620); +const parse = __webpack_require__(623); +const utils = __webpack_require__(621); /** * Creates a matcher function from one or more glob patterns. The @@ -73857,7 +72555,7 @@ picomatch.toRegex = (source, options) => { * @return {Object} */ -picomatch.constants = __webpack_require__(623); +picomatch.constants = __webpack_require__(622); /** * Expose "picomatch" @@ -73867,13 +72565,13 @@ module.exports = picomatch; /***/ }), -/* 621 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(622); +const utils = __webpack_require__(621); const { CHAR_ASTERISK, /* * */ @@ -73891,7 +72589,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(623); +} = __webpack_require__(622); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -74093,7 +72791,7 @@ module.exports = (input, options) => { /***/ }), -/* 622 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74105,7 +72803,7 @@ const { REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL, REGEX_REMOVE_BACKSLASH -} = __webpack_require__(623); +} = __webpack_require__(622); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -74143,7 +72841,7 @@ exports.escapeLast = (input, char, lastIdx) => { /***/ }), -/* 623 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74329,14 +73027,14 @@ module.exports = { /***/ }), -/* 624 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(622); -const constants = __webpack_require__(623); +const utils = __webpack_require__(621); +const constants = __webpack_require__(622); /** * Constants @@ -75347,13 +74045,13 @@ module.exports = parse; /***/ }), -/* 625 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(591); +const merge2 = __webpack_require__(590); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -75365,14 +74063,14 @@ exports.merge = merge; /***/ }), -/* 626 */ +/* 625 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(627); -const provider_1 = __webpack_require__(654); +const stream_1 = __webpack_require__(626); +const provider_1 = __webpack_require__(653); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -75400,16 +74098,16 @@ exports.default = ProviderAsync; /***/ }), -/* 627 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(27); -const fsStat = __webpack_require__(628); -const fsWalk = __webpack_require__(633); -const reader_1 = __webpack_require__(653); +const fsStat = __webpack_require__(627); +const fsWalk = __webpack_require__(632); +const reader_1 = __webpack_require__(652); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -75462,15 +74160,15 @@ exports.default = ReaderStream; /***/ }), -/* 628 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(629); -const sync = __webpack_require__(630); -const settings_1 = __webpack_require__(631); +const async = __webpack_require__(628); +const sync = __webpack_require__(629); +const settings_1 = __webpack_require__(630); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -75493,7 +74191,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 629 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75531,7 +74229,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 630 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75560,13 +74258,13 @@ exports.read = read; /***/ }), -/* 631 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(632); +const fs = __webpack_require__(631); class Settings { constructor(_options = {}) { this._options = _options; @@ -75583,7 +74281,7 @@ exports.default = Settings; /***/ }), -/* 632 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75606,16 +74304,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 633 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(634); -const stream_1 = __webpack_require__(649); -const sync_1 = __webpack_require__(650); -const settings_1 = __webpack_require__(652); +const async_1 = __webpack_require__(633); +const stream_1 = __webpack_require__(648); +const sync_1 = __webpack_require__(649); +const settings_1 = __webpack_require__(651); exports.Settings = settings_1.default; function walk(dir, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -75645,13 +74343,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 634 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(635); +const async_1 = __webpack_require__(634); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -75682,17 +74380,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 635 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(379); -const fsScandir = __webpack_require__(636); -const fastq = __webpack_require__(645); -const common = __webpack_require__(647); -const reader_1 = __webpack_require__(648); +const fsScandir = __webpack_require__(635); +const fastq = __webpack_require__(644); +const common = __webpack_require__(646); +const reader_1 = __webpack_require__(647); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -75782,15 +74480,15 @@ exports.default = AsyncReader; /***/ }), -/* 636 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(637); -const sync = __webpack_require__(642); -const settings_1 = __webpack_require__(643); +const async = __webpack_require__(636); +const sync = __webpack_require__(641); +const settings_1 = __webpack_require__(642); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -75813,16 +74511,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 637 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(628); -const rpl = __webpack_require__(638); -const constants_1 = __webpack_require__(639); -const utils = __webpack_require__(640); +const fsStat = __webpack_require__(627); +const rpl = __webpack_require__(637); +const constants_1 = __webpack_require__(638); +const utils = __webpack_require__(639); function read(dir, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(dir, settings, callback); @@ -75911,7 +74609,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 638 */ +/* 637 */ /***/ (function(module, exports) { module.exports = runParallel @@ -75965,7 +74663,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 639 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75981,18 +74679,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = MAJOR_VERSION > 10 || (MAJOR_VERSIO /***/ }), -/* 640 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(641); +const fs = __webpack_require__(640); exports.fs = fs; /***/ }), -/* 641 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76017,15 +74715,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 642 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(628); -const constants_1 = __webpack_require__(639); -const utils = __webpack_require__(640); +const fsStat = __webpack_require__(627); +const constants_1 = __webpack_require__(638); +const utils = __webpack_require__(639); function read(dir, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(dir, settings); @@ -76076,15 +74774,15 @@ exports.readdir = readdir; /***/ }), -/* 643 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(16); -const fsStat = __webpack_require__(628); -const fs = __webpack_require__(644); +const fsStat = __webpack_require__(627); +const fs = __webpack_require__(643); class Settings { constructor(_options = {}) { this._options = _options; @@ -76107,7 +74805,7 @@ exports.default = Settings; /***/ }), -/* 644 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76132,13 +74830,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 645 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(646) +var reusify = __webpack_require__(645) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -76312,7 +75010,7 @@ module.exports = fastqueue /***/ }), -/* 646 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76352,7 +75050,7 @@ module.exports = reusify /***/ }), -/* 647 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76383,13 +75081,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 648 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(647); +const common = __webpack_require__(646); class Reader { constructor(_root, _settings) { this._root = _root; @@ -76401,14 +75099,14 @@ exports.default = Reader; /***/ }), -/* 649 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(27); -const async_1 = __webpack_require__(635); +const async_1 = __webpack_require__(634); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -76438,13 +75136,13 @@ exports.default = StreamProvider; /***/ }), -/* 650 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(651); +const sync_1 = __webpack_require__(650); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -76459,15 +75157,15 @@ exports.default = SyncProvider; /***/ }), -/* 651 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(636); -const common = __webpack_require__(647); -const reader_1 = __webpack_require__(648); +const fsScandir = __webpack_require__(635); +const common = __webpack_require__(646); +const reader_1 = __webpack_require__(647); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -76525,14 +75223,14 @@ exports.default = SyncReader; /***/ }), -/* 652 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(16); -const fsScandir = __webpack_require__(636); +const fsScandir = __webpack_require__(635); class Settings { constructor(_options = {}) { this._options = _options; @@ -76558,15 +75256,15 @@ exports.default = Settings; /***/ }), -/* 653 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(16); -const fsStat = __webpack_require__(628); -const utils = __webpack_require__(599); +const fsStat = __webpack_require__(627); +const utils = __webpack_require__(598); class Reader { constructor(_settings) { this._settings = _settings; @@ -76598,17 +75296,17 @@ exports.default = Reader; /***/ }), -/* 654 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(16); -const deep_1 = __webpack_require__(655); -const entry_1 = __webpack_require__(656); -const error_1 = __webpack_require__(657); -const entry_2 = __webpack_require__(658); +const deep_1 = __webpack_require__(654); +const entry_1 = __webpack_require__(655); +const error_1 = __webpack_require__(656); +const entry_2 = __webpack_require__(657); class Provider { constructor(_settings) { this._settings = _settings; @@ -76653,13 +75351,13 @@ exports.default = Provider; /***/ }), -/* 655 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(599); +const utils = __webpack_require__(598); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -76719,13 +75417,13 @@ exports.default = DeepFilter; /***/ }), -/* 656 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(599); +const utils = __webpack_require__(598); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -76780,13 +75478,13 @@ exports.default = EntryFilter; /***/ }), -/* 657 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(599); +const utils = __webpack_require__(598); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -76802,13 +75500,13 @@ exports.default = ErrorFilter; /***/ }), -/* 658 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(599); +const utils = __webpack_require__(598); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -76835,15 +75533,15 @@ exports.default = EntryTransformer; /***/ }), -/* 659 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(27); -const stream_2 = __webpack_require__(627); -const provider_1 = __webpack_require__(654); +const stream_2 = __webpack_require__(626); +const provider_1 = __webpack_require__(653); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -76871,14 +75569,14 @@ exports.default = ProviderStream; /***/ }), -/* 660 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(661); -const provider_1 = __webpack_require__(654); +const sync_1 = __webpack_require__(660); +const provider_1 = __webpack_require__(653); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -76901,15 +75599,15 @@ exports.default = ProviderSync; /***/ }), -/* 661 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(628); -const fsWalk = __webpack_require__(633); -const reader_1 = __webpack_require__(653); +const fsStat = __webpack_require__(627); +const fsWalk = __webpack_require__(632); +const reader_1 = __webpack_require__(652); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -76951,7 +75649,7 @@ exports.default = ReaderSync; /***/ }), -/* 662 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77011,13 +75709,13 @@ exports.default = Settings; /***/ }), -/* 663 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(664); +const pathType = __webpack_require__(663); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -77093,7 +75791,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 664 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77143,7 +75841,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 665 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77151,9 +75849,9 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); const {promisify} = __webpack_require__(29); const fs = __webpack_require__(23); const path = __webpack_require__(16); -const fastGlob = __webpack_require__(597); -const gitIgnore = __webpack_require__(666); -const slash = __webpack_require__(667); +const fastGlob = __webpack_require__(596); +const gitIgnore = __webpack_require__(665); +const slash = __webpack_require__(666); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -77267,7 +75965,7 @@ module.exports.sync = options => { /***/ }), -/* 666 */ +/* 665 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -77858,7 +76556,7 @@ if ( /***/ }), -/* 667 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77876,7 +76574,7 @@ module.exports = path => { /***/ }), -/* 668 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77929,7 +76627,7 @@ module.exports = { /***/ }), -/* 669 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77951,7 +76649,7 @@ module.exports = path_ => { /***/ }), -/* 670 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77979,7 +76677,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 671 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(30) @@ -77987,7 +76685,7 @@ const path = __webpack_require__(16) const fs = __webpack_require__(23) let glob = undefined try { - glob = __webpack_require__(592) + glob = __webpack_require__(591) } catch (_err) { // treat glob as optional. } @@ -78353,12 +77051,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 672 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(673); +const AggregateError = __webpack_require__(672); module.exports = async ( iterable, @@ -78441,13 +77139,13 @@ module.exports = async ( /***/ }), -/* 673 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(674); -const cleanStack = __webpack_require__(675); +const indentString = __webpack_require__(673); +const cleanStack = __webpack_require__(674); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -78495,7 +77193,7 @@ module.exports = AggregateError; /***/ }), -/* 674 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78537,7 +77235,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 675 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78584,15 +77282,15 @@ module.exports = (stack, options) => { /***/ }), -/* 676 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(677); -const cliCursor = __webpack_require__(681); -const cliSpinners = __webpack_require__(685); -const logSymbols = __webpack_require__(566); +const chalk = __webpack_require__(676); +const cliCursor = __webpack_require__(680); +const cliSpinners = __webpack_require__(684); +const logSymbols = __webpack_require__(565); class Ora { constructor(options) { @@ -78739,16 +77437,16 @@ module.exports.promise = (action, options) => { /***/ }), -/* 677 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(3); -const ansiStyles = __webpack_require__(678); -const stdoutColor = __webpack_require__(679).stdout; +const ansiStyles = __webpack_require__(677); +const stdoutColor = __webpack_require__(678).stdout; -const template = __webpack_require__(680); +const template = __webpack_require__(679); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -78974,7 +77672,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 678 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79147,7 +77845,7 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(5)(module))) /***/ }), -/* 679 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79289,7 +77987,7 @@ module.exports = { /***/ }), -/* 680 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79424,12 +78122,12 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 681 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(682); +const restoreCursor = __webpack_require__(681); let hidden = false; @@ -79470,12 +78168,12 @@ exports.toggle = (force, stream) => { /***/ }), -/* 682 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const onetime = __webpack_require__(683); +const onetime = __webpack_require__(682); const signalExit = __webpack_require__(377); module.exports = onetime(() => { @@ -79486,12 +78184,12 @@ module.exports = onetime(() => { /***/ }), -/* 683 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(684); +const mimicFn = __webpack_require__(683); module.exports = (fn, opts) => { // TODO: Remove this in v3 @@ -79532,7 +78230,7 @@ module.exports = (fn, opts) => { /***/ }), -/* 684 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79548,22 +78246,22 @@ module.exports = (to, from) => { /***/ }), -/* 685 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(686); +module.exports = __webpack_require__(685); /***/ }), -/* 686 */ +/* 685 */ /***/ (function(module) { module.exports = JSON.parse("{\"dots\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠹\",\"⠸\",\"⠼\",\"⠴\",\"⠦\",\"⠧\",\"⠇\",\"⠏\"]},\"dots2\":{\"interval\":80,\"frames\":[\"⣾\",\"⣽\",\"⣻\",\"⢿\",\"⡿\",\"⣟\",\"⣯\",\"⣷\"]},\"dots3\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠞\",\"⠖\",\"⠦\",\"⠴\",\"⠲\",\"⠳\",\"⠓\"]},\"dots4\":{\"interval\":80,\"frames\":[\"⠄\",\"⠆\",\"⠇\",\"⠋\",\"⠙\",\"⠸\",\"⠰\",\"⠠\",\"⠰\",\"⠸\",\"⠙\",\"⠋\",\"⠇\",\"⠆\"]},\"dots5\":{\"interval\":80,\"frames\":[\"⠋\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\"]},\"dots6\":{\"interval\":80,\"frames\":[\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠴\",\"⠲\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠚\",\"⠙\",\"⠉\",\"⠁\"]},\"dots7\":{\"interval\":80,\"frames\":[\"⠈\",\"⠉\",\"⠋\",\"⠓\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠖\",\"⠦\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\"]},\"dots8\":{\"interval\":80,\"frames\":[\"⠁\",\"⠁\",\"⠉\",\"⠙\",\"⠚\",\"⠒\",\"⠂\",\"⠂\",\"⠒\",\"⠲\",\"⠴\",\"⠤\",\"⠄\",\"⠄\",\"⠤\",\"⠠\",\"⠠\",\"⠤\",\"⠦\",\"⠖\",\"⠒\",\"⠐\",\"⠐\",\"⠒\",\"⠓\",\"⠋\",\"⠉\",\"⠈\",\"⠈\"]},\"dots9\":{\"interval\":80,\"frames\":[\"⢹\",\"⢺\",\"⢼\",\"⣸\",\"⣇\",\"⡧\",\"⡗\",\"⡏\"]},\"dots10\":{\"interval\":80,\"frames\":[\"⢄\",\"⢂\",\"⢁\",\"⡁\",\"⡈\",\"⡐\",\"⡠\"]},\"dots11\":{\"interval\":100,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⡀\",\"⢀\",\"⠠\",\"⠐\",\"⠈\"]},\"dots12\":{\"interval\":80,\"frames\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"line\":{\"interval\":130,\"frames\":[\"-\",\"\\\\\",\"|\",\"/\"]},\"line2\":{\"interval\":100,\"frames\":[\"⠂\",\"-\",\"–\",\"—\",\"–\",\"-\"]},\"pipe\":{\"interval\":100,\"frames\":[\"┤\",\"┘\",\"┴\",\"└\",\"├\",\"┌\",\"┬\",\"┐\"]},\"simpleDots\":{\"interval\":400,\"frames\":[\". \",\".. \",\"...\",\" \"]},\"simpleDotsScrolling\":{\"interval\":200,\"frames\":[\". \",\".. \",\"...\",\" ..\",\" .\",\" \"]},\"star\":{\"interval\":70,\"frames\":[\"✶\",\"✸\",\"✹\",\"✺\",\"✹\",\"✷\"]},\"star2\":{\"interval\":80,\"frames\":[\"+\",\"x\",\"*\"]},\"flip\":{\"interval\":70,\"frames\":[\"_\",\"_\",\"_\",\"-\",\"`\",\"`\",\"'\",\"´\",\"-\",\"_\",\"_\",\"_\"]},\"hamburger\":{\"interval\":100,\"frames\":[\"☱\",\"☲\",\"☴\"]},\"growVertical\":{\"interval\":120,\"frames\":[\"▁\",\"▃\",\"▄\",\"▅\",\"▆\",\"▇\",\"▆\",\"▅\",\"▄\",\"▃\"]},\"growHorizontal\":{\"interval\":120,\"frames\":[\"▏\",\"▎\",\"▍\",\"▌\",\"▋\",\"▊\",\"▉\",\"▊\",\"▋\",\"▌\",\"▍\",\"▎\"]},\"balloon\":{\"interval\":140,\"frames\":[\" \",\".\",\"o\",\"O\",\"@\",\"*\",\" \"]},\"balloon2\":{\"interval\":120,\"frames\":[\".\",\"o\",\"O\",\"°\",\"O\",\"o\",\".\"]},\"noise\":{\"interval\":100,\"frames\":[\"▓\",\"▒\",\"░\"]},\"bounce\":{\"interval\":120,\"frames\":[\"⠁\",\"⠂\",\"⠄\",\"⠂\"]},\"boxBounce\":{\"interval\":120,\"frames\":[\"▖\",\"▘\",\"▝\",\"▗\"]},\"boxBounce2\":{\"interval\":100,\"frames\":[\"▌\",\"▀\",\"▐\",\"▄\"]},\"triangle\":{\"interval\":50,\"frames\":[\"◢\",\"◣\",\"◤\",\"◥\"]},\"arc\":{\"interval\":100,\"frames\":[\"◜\",\"◠\",\"◝\",\"◞\",\"◡\",\"◟\"]},\"circle\":{\"interval\":120,\"frames\":[\"◡\",\"⊙\",\"◠\"]},\"squareCorners\":{\"interval\":180,\"frames\":[\"◰\",\"◳\",\"◲\",\"◱\"]},\"circleQuarters\":{\"interval\":120,\"frames\":[\"◴\",\"◷\",\"◶\",\"◵\"]},\"circleHalves\":{\"interval\":50,\"frames\":[\"◐\",\"◓\",\"◑\",\"◒\"]},\"squish\":{\"interval\":100,\"frames\":[\"╫\",\"╪\"]},\"toggle\":{\"interval\":250,\"frames\":[\"⊶\",\"⊷\"]},\"toggle2\":{\"interval\":80,\"frames\":[\"▫\",\"▪\"]},\"toggle3\":{\"interval\":120,\"frames\":[\"□\",\"■\"]},\"toggle4\":{\"interval\":100,\"frames\":[\"■\",\"□\",\"▪\",\"▫\"]},\"toggle5\":{\"interval\":100,\"frames\":[\"▮\",\"▯\"]},\"toggle6\":{\"interval\":300,\"frames\":[\"ဝ\",\"၀\"]},\"toggle7\":{\"interval\":80,\"frames\":[\"⦾\",\"⦿\"]},\"toggle8\":{\"interval\":100,\"frames\":[\"◍\",\"◌\"]},\"toggle9\":{\"interval\":100,\"frames\":[\"◉\",\"◎\"]},\"toggle10\":{\"interval\":100,\"frames\":[\"㊂\",\"㊀\",\"㊁\"]},\"toggle11\":{\"interval\":50,\"frames\":[\"⧇\",\"⧆\"]},\"toggle12\":{\"interval\":120,\"frames\":[\"☗\",\"☖\"]},\"toggle13\":{\"interval\":80,\"frames\":[\"=\",\"*\",\"-\"]},\"arrow\":{\"interval\":100,\"frames\":[\"←\",\"↖\",\"↑\",\"↗\",\"→\",\"↘\",\"↓\",\"↙\"]},\"arrow2\":{\"interval\":80,\"frames\":[\"⬆️ \",\"↗️ \",\"➡️ \",\"↘️ \",\"⬇️ \",\"↙️ \",\"⬅️ \",\"↖️ \"]},\"arrow3\":{\"interval\":120,\"frames\":[\"▹▹▹▹▹\",\"▸▹▹▹▹\",\"▹▸▹▹▹\",\"▹▹▸▹▹\",\"▹▹▹▸▹\",\"▹▹▹▹▸\"]},\"bouncingBar\":{\"interval\":80,\"frames\":[\"[ ]\",\"[= ]\",\"[== ]\",\"[=== ]\",\"[ ===]\",\"[ ==]\",\"[ =]\",\"[ ]\",\"[ =]\",\"[ ==]\",\"[ ===]\",\"[====]\",\"[=== ]\",\"[== ]\",\"[= ]\"]},\"bouncingBall\":{\"interval\":80,\"frames\":[\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ●)\",\"( ● )\",\"( ● )\",\"( ● )\",\"( ● )\",\"(● )\"]},\"smiley\":{\"interval\":200,\"frames\":[\"😄 \",\"😝 \"]},\"monkey\":{\"interval\":300,\"frames\":[\"🙈 \",\"🙈 \",\"🙉 \",\"🙊 \"]},\"hearts\":{\"interval\":100,\"frames\":[\"💛 \",\"💙 \",\"💜 \",\"💚 \",\"❤️ \"]},\"clock\":{\"interval\":100,\"frames\":[\"🕐 \",\"🕑 \",\"🕒 \",\"🕓 \",\"🕔 \",\"🕕 \",\"🕖 \",\"🕗 \",\"🕘 \",\"🕙 \",\"🕚 \"]},\"earth\":{\"interval\":180,\"frames\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"moon\":{\"interval\":80,\"frames\":[\"🌑 \",\"🌒 \",\"🌓 \",\"🌔 \",\"🌕 \",\"🌖 \",\"🌗 \",\"🌘 \"]},\"runner\":{\"interval\":140,\"frames\":[\"🚶 \",\"🏃 \"]},\"pong\":{\"interval\":80,\"frames\":[\"▐⠂ ▌\",\"▐⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂▌\",\"▐ ⠠▌\",\"▐ ⡀▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐ ⠠ ▌\",\"▐ ⠂ ▌\",\"▐ ⠈ ▌\",\"▐ ⠂ ▌\",\"▐ ⠠ ▌\",\"▐ ⡀ ▌\",\"▐⠠ ▌\"]},\"shark\":{\"interval\":120,\"frames\":[\"▐|\\\\____________▌\",\"▐_|\\\\___________▌\",\"▐__|\\\\__________▌\",\"▐___|\\\\_________▌\",\"▐____|\\\\________▌\",\"▐_____|\\\\_______▌\",\"▐______|\\\\______▌\",\"▐_______|\\\\_____▌\",\"▐________|\\\\____▌\",\"▐_________|\\\\___▌\",\"▐__________|\\\\__▌\",\"▐___________|\\\\_▌\",\"▐____________|\\\\▌\",\"▐____________/|▌\",\"▐___________/|_▌\",\"▐__________/|__▌\",\"▐_________/|___▌\",\"▐________/|____▌\",\"▐_______/|_____▌\",\"▐______/|______▌\",\"▐_____/|_______▌\",\"▐____/|________▌\",\"▐___/|_________▌\",\"▐__/|__________▌\",\"▐_/|___________▌\",\"▐/|____________▌\"]},\"dqpb\":{\"interval\":100,\"frames\":[\"d\",\"q\",\"p\",\"b\"]},\"weather\":{\"interval\":100,\"frames\":[\"☀️ \",\"☀️ \",\"☀️ \",\"🌤 \",\"⛅️ \",\"🌥 \",\"☁️ \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"🌧 \",\"🌨 \",\"⛈ \",\"🌨 \",\"🌧 \",\"🌨 \",\"☁️ \",\"🌥 \",\"⛅️ \",\"🌤 \",\"☀️ \",\"☀️ \"]},\"christmas\":{\"interval\":400,\"frames\":[\"🌲\",\"🎄\"]}}"); /***/ }), -/* 687 */ +/* 686 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -79623,7 +78321,7 @@ const RunCommand = { }; /***/ }), -/* 688 */ +/* 687 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -79634,7 +78332,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(34); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(500); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(689); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(688); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -79718,7 +78416,7 @@ const WatchCommand = { }; /***/ }), -/* 689 */ +/* 688 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -79792,7 +78490,7 @@ function waitUntilWatchIsReady(stream, opts = {}) { } /***/ }), -/* 690 */ +/* 689 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -79800,15 +78498,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "runCommand", function() { return runCommand; }); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var indent_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(691); +/* harmony import */ var indent_string__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(690); /* harmony import */ var indent_string__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(indent_string__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var wrap_ansi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(692); +/* harmony import */ var wrap_ansi__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(691); /* harmony import */ var wrap_ansi__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(wrap_ansi__WEBPACK_IMPORTED_MODULE_2__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(515); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(34); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(501); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(699); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(700); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(698); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(699); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -79896,7 +78594,7 @@ function toArray(value) { } /***/ }), -/* 691 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79930,13 +78628,13 @@ module.exports = (str, count, opts) => { /***/ }), -/* 692 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringWidth = __webpack_require__(693); -const stripAnsi = __webpack_require__(697); +const stringWidth = __webpack_require__(692); +const stripAnsi = __webpack_require__(696); const ESCAPES = new Set([ '\u001B', @@ -80130,13 +78828,13 @@ module.exports = (str, cols, opts) => { /***/ }), -/* 693 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stripAnsi = __webpack_require__(694); -const isFullwidthCodePoint = __webpack_require__(696); +const stripAnsi = __webpack_require__(693); +const isFullwidthCodePoint = __webpack_require__(695); module.exports = str => { if (typeof str !== 'string' || str.length === 0) { @@ -80173,18 +78871,18 @@ module.exports = str => { /***/ }), -/* 694 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(695); +const ansiRegex = __webpack_require__(694); module.exports = input => typeof input === 'string' ? input.replace(ansiRegex(), '') : input; /***/ }), -/* 695 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80201,7 +78899,7 @@ module.exports = () => { /***/ }), -/* 696 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80254,18 +78952,18 @@ module.exports = x => { /***/ }), -/* 697 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(698); +const ansiRegex = __webpack_require__(697); module.exports = input => typeof input === 'string' ? input.replace(ansiRegex(), '') : input; /***/ }), -/* 698 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80282,7 +78980,7 @@ module.exports = () => { /***/ }), -/* 699 */ +/* 698 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -80435,7 +79133,7 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 700 */ +/* 699 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -80443,10 +79141,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Kibana", function() { return Kibana; }); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(16); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(701); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(700); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(501); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(580); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(579); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } @@ -80578,15 +79276,15 @@ class Kibana { } /***/ }), -/* 701 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const minimatch = __webpack_require__(505); -const arrayUnion = __webpack_require__(702); -const arrayDiffer = __webpack_require__(703); -const arrify = __webpack_require__(704); +const arrayUnion = __webpack_require__(701); +const arrayDiffer = __webpack_require__(702); +const arrify = __webpack_require__(703); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -80610,7 +79308,7 @@ module.exports = (list, patterns, options = {}) => { /***/ }), -/* 702 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80622,7 +79320,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 703 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80637,7 +79335,7 @@ module.exports = arrayDiffer; /***/ }), -/* 704 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80667,15 +79365,15 @@ module.exports = arrify; /***/ }), -/* 705 */ +/* 704 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(706); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(705); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(929); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(928); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -80700,19 +79398,19 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 706 */ +/* 705 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(707); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(706); /* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cpy__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(588); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(587); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(16); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(580); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(579); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(34); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(517); @@ -80848,7 +79546,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 707 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80856,13 +79554,13 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(379); const path = __webpack_require__(16); const os = __webpack_require__(11); -const pAll = __webpack_require__(708); -const arrify = __webpack_require__(710); -const globby = __webpack_require__(711); -const isGlob = __webpack_require__(606); -const cpFile = __webpack_require__(914); -const junk = __webpack_require__(926); -const CpyError = __webpack_require__(927); +const pAll = __webpack_require__(707); +const arrify = __webpack_require__(709); +const globby = __webpack_require__(710); +const isGlob = __webpack_require__(605); +const cpFile = __webpack_require__(913); +const junk = __webpack_require__(925); +const CpyError = __webpack_require__(926); const defaultOptions = { ignoreJunk: true @@ -80981,12 +79679,12 @@ module.exports = (source, destination, { /***/ }), -/* 708 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(709); +const pMap = __webpack_require__(708); module.exports = (iterable, options) => pMap(iterable, element => element(), options); // TODO: Remove this for the next major release @@ -80994,7 +79692,7 @@ module.exports.default = module.exports; /***/ }), -/* 709 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81073,7 +79771,7 @@ module.exports.default = pMap; /***/ }), -/* 710 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81103,17 +79801,17 @@ module.exports = arrify; /***/ }), -/* 711 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const arrayUnion = __webpack_require__(712); -const glob = __webpack_require__(714); -const fastGlob = __webpack_require__(719); -const dirGlob = __webpack_require__(907); -const gitignore = __webpack_require__(910); +const arrayUnion = __webpack_require__(711); +const glob = __webpack_require__(713); +const fastGlob = __webpack_require__(718); +const dirGlob = __webpack_require__(906); +const gitignore = __webpack_require__(909); const DEFAULT_FILTER = () => false; @@ -81258,12 +79956,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 712 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(713); +var arrayUniq = __webpack_require__(712); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -81271,7 +79969,7 @@ module.exports = function () { /***/ }), -/* 713 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81340,7 +80038,7 @@ if ('Set' in global) { /***/ }), -/* 714 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -81389,13 +80087,13 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(715) +var inherits = __webpack_require__(714) var EE = __webpack_require__(379).EventEmitter var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var globSync = __webpack_require__(717) -var common = __webpack_require__(718) +var globSync = __webpack_require__(716) +var common = __webpack_require__(717) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -82136,7 +80834,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 715 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -82146,12 +80844,12 @@ try { module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(716); + module.exports = __webpack_require__(715); } /***/ }), -/* 716 */ +/* 715 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -82184,7 +80882,7 @@ if (typeof Object.create === 'function') { /***/ }), -/* 717 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync @@ -82194,12 +80892,12 @@ var fs = __webpack_require__(23) var rp = __webpack_require__(503) var minimatch = __webpack_require__(505) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(714).Glob +var Glob = __webpack_require__(713).Glob var util = __webpack_require__(29) var path = __webpack_require__(16) var assert = __webpack_require__(30) var isAbsolute = __webpack_require__(511) -var common = __webpack_require__(718) +var common = __webpack_require__(717) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -82676,7 +81374,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 718 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -82922,10 +81620,10 @@ function childrenIgnored (self, path) { /***/ }), -/* 719 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(720); +const pkg = __webpack_require__(719); module.exports = pkg.async; module.exports.default = pkg.async; @@ -82938,19 +81636,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 720 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(721); -var taskManager = __webpack_require__(722); -var reader_async_1 = __webpack_require__(878); -var reader_stream_1 = __webpack_require__(902); -var reader_sync_1 = __webpack_require__(903); -var arrayUtils = __webpack_require__(905); -var streamUtils = __webpack_require__(906); +var optionsManager = __webpack_require__(720); +var taskManager = __webpack_require__(721); +var reader_async_1 = __webpack_require__(877); +var reader_stream_1 = __webpack_require__(901); +var reader_sync_1 = __webpack_require__(902); +var arrayUtils = __webpack_require__(904); +var streamUtils = __webpack_require__(905); /** * Synchronous API. */ @@ -83016,7 +81714,7 @@ function isString(source) { /***/ }), -/* 721 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83054,13 +81752,13 @@ exports.prepare = prepare; /***/ }), -/* 722 */ +/* 721 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(723); +var patternUtils = __webpack_require__(722); /** * Generate tasks based on parent directory of each pattern. */ @@ -83151,16 +81849,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 723 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var globParent = __webpack_require__(724); -var isGlob = __webpack_require__(727); -var micromatch = __webpack_require__(728); +var globParent = __webpack_require__(723); +var isGlob = __webpack_require__(726); +var micromatch = __webpack_require__(727); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -83306,15 +82004,15 @@ exports.matchAny = matchAny; /***/ }), -/* 724 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(16); -var isglob = __webpack_require__(725); -var pathDirname = __webpack_require__(726); +var isglob = __webpack_require__(724); +var pathDirname = __webpack_require__(725); var isWin32 = __webpack_require__(11).platform() === 'win32'; module.exports = function globParent(str) { @@ -83337,7 +82035,7 @@ module.exports = function globParent(str) { /***/ }), -/* 725 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -83347,7 +82045,7 @@ module.exports = function globParent(str) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(607); +var isExtglob = __webpack_require__(606); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { @@ -83368,7 +82066,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 726 */ +/* 725 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83518,7 +82216,7 @@ module.exports.win32 = win32; /***/ }), -/* 727 */ +/* 726 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -83528,7 +82226,7 @@ module.exports.win32 = win32; * Released under the MIT License. */ -var isExtglob = __webpack_require__(607); +var isExtglob = __webpack_require__(606); var chars = { '{': '}', '(': ')', '[': ']'}; module.exports = function isGlob(str, options) { @@ -83570,7 +82268,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 728 */ +/* 727 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83581,18 +82279,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(29); -var braces = __webpack_require__(729); -var toRegex = __webpack_require__(831); -var extend = __webpack_require__(839); +var braces = __webpack_require__(728); +var toRegex = __webpack_require__(830); +var extend = __webpack_require__(838); /** * Local dependencies */ -var compilers = __webpack_require__(842); -var parsers = __webpack_require__(874); -var cache = __webpack_require__(875); -var utils = __webpack_require__(876); +var compilers = __webpack_require__(841); +var parsers = __webpack_require__(873); +var cache = __webpack_require__(874); +var utils = __webpack_require__(875); var MAX_LENGTH = 1024 * 64; /** @@ -84454,7 +83152,7 @@ module.exports = micromatch; /***/ }), -/* 729 */ +/* 728 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84464,18 +83162,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(730); -var unique = __webpack_require__(742); -var extend = __webpack_require__(739); +var toRegex = __webpack_require__(729); +var unique = __webpack_require__(741); +var extend = __webpack_require__(738); /** * Local dependencies */ -var compilers = __webpack_require__(743); -var parsers = __webpack_require__(758); -var Braces = __webpack_require__(768); -var utils = __webpack_require__(744); +var compilers = __webpack_require__(742); +var parsers = __webpack_require__(757); +var Braces = __webpack_require__(767); +var utils = __webpack_require__(743); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -84779,15 +83477,15 @@ module.exports = braces; /***/ }), -/* 730 */ +/* 729 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(731); -var extend = __webpack_require__(739); -var not = __webpack_require__(741); +var define = __webpack_require__(730); +var extend = __webpack_require__(738); +var not = __webpack_require__(740); var MAX_LENGTH = 1024 * 64; /** @@ -84934,7 +83632,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 731 */ +/* 730 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84947,7 +83645,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(732); +var isDescriptor = __webpack_require__(731); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -84972,7 +83670,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 732 */ +/* 731 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84985,9 +83683,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(733); -var isAccessor = __webpack_require__(734); -var isData = __webpack_require__(737); +var typeOf = __webpack_require__(732); +var isAccessor = __webpack_require__(733); +var isData = __webpack_require__(736); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -85001,7 +83699,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 733 */ +/* 732 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -85154,7 +83852,7 @@ function isBuffer(val) { /***/ }), -/* 734 */ +/* 733 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85167,7 +83865,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(735); +var typeOf = __webpack_require__(734); // accessor descriptor properties var accessor = { @@ -85230,10 +83928,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 735 */ +/* 734 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(736); +var isBuffer = __webpack_require__(735); var toString = Object.prototype.toString; /** @@ -85352,7 +84050,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 736 */ +/* 735 */ /***/ (function(module, exports) { /*! @@ -85379,7 +84077,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 737 */ +/* 736 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85392,7 +84090,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(738); +var typeOf = __webpack_require__(737); // data descriptor properties var data = { @@ -85441,10 +84139,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 738 */ +/* 737 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(736); +var isBuffer = __webpack_require__(735); var toString = Object.prototype.toString; /** @@ -85563,13 +84261,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 739 */ +/* 738 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(740); +var isObject = __webpack_require__(739); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -85603,7 +84301,7 @@ function hasOwn(obj, key) { /***/ }), -/* 740 */ +/* 739 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85623,13 +84321,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 741 */ +/* 740 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(739); +var extend = __webpack_require__(738); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -85696,7 +84394,7 @@ module.exports = toRegex; /***/ }), -/* 742 */ +/* 741 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -85746,13 +84444,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 743 */ +/* 742 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(744); +var utils = __webpack_require__(743); module.exports = function(braces, options) { braces.compiler @@ -86035,25 +84733,25 @@ function hasQueue(node) { /***/ }), -/* 744 */ +/* 743 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(745); +var splitString = __webpack_require__(744); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(739); -utils.flatten = __webpack_require__(751); -utils.isObject = __webpack_require__(749); -utils.fillRange = __webpack_require__(752); -utils.repeat = __webpack_require__(757); -utils.unique = __webpack_require__(742); +utils.extend = __webpack_require__(738); +utils.flatten = __webpack_require__(750); +utils.isObject = __webpack_require__(748); +utils.fillRange = __webpack_require__(751); +utils.repeat = __webpack_require__(756); +utils.unique = __webpack_require__(741); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -86385,7 +85083,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 745 */ +/* 744 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86398,7 +85096,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(746); +var extend = __webpack_require__(745); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -86563,14 +85261,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 746 */ +/* 745 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(747); -var assignSymbols = __webpack_require__(750); +var isExtendable = __webpack_require__(746); +var assignSymbols = __webpack_require__(749); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -86630,7 +85328,7 @@ function isEnum(obj, key) { /***/ }), -/* 747 */ +/* 746 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86643,7 +85341,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(748); +var isPlainObject = __webpack_require__(747); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -86651,7 +85349,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 748 */ +/* 747 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86664,7 +85362,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(749); +var isObject = __webpack_require__(748); function isObjectObject(o) { return isObject(o) === true @@ -86695,7 +85393,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 749 */ +/* 748 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86714,7 +85412,7 @@ module.exports = function isObject(val) { /***/ }), -/* 750 */ +/* 749 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86761,7 +85459,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 751 */ +/* 750 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86790,7 +85488,7 @@ function flat(arr, res) { /***/ }), -/* 752 */ +/* 751 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -86804,10 +85502,10 @@ function flat(arr, res) { var util = __webpack_require__(29); -var isNumber = __webpack_require__(753); -var extend = __webpack_require__(739); -var repeat = __webpack_require__(755); -var toRegex = __webpack_require__(756); +var isNumber = __webpack_require__(752); +var extend = __webpack_require__(738); +var repeat = __webpack_require__(754); +var toRegex = __webpack_require__(755); /** * Return a range of numbers or letters. @@ -87005,7 +85703,7 @@ module.exports = fillRange; /***/ }), -/* 753 */ +/* 752 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87018,7 +85716,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(754); +var typeOf = __webpack_require__(753); module.exports = function isNumber(num) { var type = typeOf(num); @@ -87034,10 +85732,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 754 */ +/* 753 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(736); +var isBuffer = __webpack_require__(735); var toString = Object.prototype.toString; /** @@ -87156,7 +85854,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 755 */ +/* 754 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87233,7 +85931,7 @@ function repeat(str, num) { /***/ }), -/* 756 */ +/* 755 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87246,8 +85944,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(755); -var isNumber = __webpack_require__(753); +var repeat = __webpack_require__(754); +var isNumber = __webpack_require__(752); var cache = {}; function toRegexRange(min, max, options) { @@ -87534,7 +86232,7 @@ module.exports = toRegexRange; /***/ }), -/* 757 */ +/* 756 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -87559,14 +86257,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 758 */ +/* 757 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(759); -var utils = __webpack_require__(744); +var Node = __webpack_require__(758); +var utils = __webpack_require__(743); /** * Braces parsers @@ -87926,15 +86624,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 759 */ +/* 758 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(749); -var define = __webpack_require__(760); -var utils = __webpack_require__(767); +var isObject = __webpack_require__(748); +var define = __webpack_require__(759); +var utils = __webpack_require__(766); var ownNames; /** @@ -88425,7 +87123,7 @@ exports = module.exports = Node; /***/ }), -/* 760 */ +/* 759 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88438,7 +87136,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(761); +var isDescriptor = __webpack_require__(760); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -88463,7 +87161,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 761 */ +/* 760 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88476,9 +87174,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(762); -var isAccessor = __webpack_require__(763); -var isData = __webpack_require__(765); +var typeOf = __webpack_require__(761); +var isAccessor = __webpack_require__(762); +var isData = __webpack_require__(764); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -88492,7 +87190,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 762 */ +/* 761 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -88627,7 +87325,7 @@ function isBuffer(val) { /***/ }), -/* 763 */ +/* 762 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88640,7 +87338,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(764); +var typeOf = __webpack_require__(763); // accessor descriptor properties var accessor = { @@ -88703,7 +87401,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 764 */ +/* 763 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -88838,7 +87536,7 @@ function isBuffer(val) { /***/ }), -/* 765 */ +/* 764 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -88851,7 +87549,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(766); +var typeOf = __webpack_require__(765); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -88894,7 +87592,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 766 */ +/* 765 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -89029,13 +87727,13 @@ function isBuffer(val) { /***/ }), -/* 767 */ +/* 766 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(754); +var typeOf = __webpack_require__(753); var utils = module.exports; /** @@ -90055,17 +88753,17 @@ function assert(val, message) { /***/ }), -/* 768 */ +/* 767 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(739); -var Snapdragon = __webpack_require__(769); -var compilers = __webpack_require__(743); -var parsers = __webpack_require__(758); -var utils = __webpack_require__(744); +var extend = __webpack_require__(738); +var Snapdragon = __webpack_require__(768); +var compilers = __webpack_require__(742); +var parsers = __webpack_require__(757); +var utils = __webpack_require__(743); /** * Customize Snapdragon parser and renderer @@ -90166,17 +88864,17 @@ module.exports = Braces; /***/ }), -/* 769 */ +/* 768 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(770); -var define = __webpack_require__(731); -var Compiler = __webpack_require__(799); -var Parser = __webpack_require__(828); -var utils = __webpack_require__(808); +var Base = __webpack_require__(769); +var define = __webpack_require__(730); +var Compiler = __webpack_require__(798); +var Parser = __webpack_require__(827); +var utils = __webpack_require__(807); var regexCache = {}; var cache = {}; @@ -90347,20 +89045,20 @@ module.exports.Parser = Parser; /***/ }), -/* 770 */ +/* 769 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var define = __webpack_require__(771); -var CacheBase = __webpack_require__(772); -var Emitter = __webpack_require__(773); -var isObject = __webpack_require__(749); -var merge = __webpack_require__(790); -var pascal = __webpack_require__(793); -var cu = __webpack_require__(794); +var define = __webpack_require__(770); +var CacheBase = __webpack_require__(771); +var Emitter = __webpack_require__(772); +var isObject = __webpack_require__(748); +var merge = __webpack_require__(789); +var pascal = __webpack_require__(792); +var cu = __webpack_require__(793); /** * Optionally define a custom `cache` namespace to use. @@ -90789,7 +89487,7 @@ module.exports.namespace = namespace; /***/ }), -/* 771 */ +/* 770 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -90802,7 +89500,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(761); +var isDescriptor = __webpack_require__(760); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -90827,21 +89525,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 772 */ +/* 771 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(749); -var Emitter = __webpack_require__(773); -var visit = __webpack_require__(774); -var toPath = __webpack_require__(777); -var union = __webpack_require__(778); -var del = __webpack_require__(782); -var get = __webpack_require__(780); -var has = __webpack_require__(787); -var set = __webpack_require__(781); +var isObject = __webpack_require__(748); +var Emitter = __webpack_require__(772); +var visit = __webpack_require__(773); +var toPath = __webpack_require__(776); +var union = __webpack_require__(777); +var del = __webpack_require__(781); +var get = __webpack_require__(779); +var has = __webpack_require__(786); +var set = __webpack_require__(780); /** * Create a `Cache` constructor that when instantiated will @@ -91095,7 +89793,7 @@ module.exports.namespace = namespace; /***/ }), -/* 773 */ +/* 772 */ /***/ (function(module, exports, __webpack_require__) { @@ -91264,7 +89962,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 774 */ +/* 773 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91277,8 +89975,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(775); -var mapVisit = __webpack_require__(776); +var visit = __webpack_require__(774); +var mapVisit = __webpack_require__(775); module.exports = function(collection, method, val) { var result; @@ -91301,7 +89999,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 775 */ +/* 774 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91314,7 +90012,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(749); +var isObject = __webpack_require__(748); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -91341,14 +90039,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 776 */ +/* 775 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var visit = __webpack_require__(775); +var visit = __webpack_require__(774); /** * Map `visit` over an array of objects. @@ -91385,7 +90083,7 @@ function isObject(val) { /***/ }), -/* 777 */ +/* 776 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91398,7 +90096,7 @@ function isObject(val) { -var typeOf = __webpack_require__(754); +var typeOf = __webpack_require__(753); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -91425,16 +90123,16 @@ function filter(arr) { /***/ }), -/* 778 */ +/* 777 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(740); -var union = __webpack_require__(779); -var get = __webpack_require__(780); -var set = __webpack_require__(781); +var isObject = __webpack_require__(739); +var union = __webpack_require__(778); +var get = __webpack_require__(779); +var set = __webpack_require__(780); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -91462,7 +90160,7 @@ function arrayify(val) { /***/ }), -/* 779 */ +/* 778 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91498,7 +90196,7 @@ module.exports = function union(init) { /***/ }), -/* 780 */ +/* 779 */ /***/ (function(module, exports) { /*! @@ -91554,7 +90252,7 @@ function toString(val) { /***/ }), -/* 781 */ +/* 780 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91567,10 +90265,10 @@ function toString(val) { -var split = __webpack_require__(745); -var extend = __webpack_require__(739); -var isPlainObject = __webpack_require__(748); -var isObject = __webpack_require__(740); +var split = __webpack_require__(744); +var extend = __webpack_require__(738); +var isPlainObject = __webpack_require__(747); +var isObject = __webpack_require__(739); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -91616,7 +90314,7 @@ function isValidKey(key) { /***/ }), -/* 782 */ +/* 781 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91629,8 +90327,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(749); -var has = __webpack_require__(783); +var isObject = __webpack_require__(748); +var has = __webpack_require__(782); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -91655,7 +90353,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 783 */ +/* 782 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91668,9 +90366,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(784); -var hasValues = __webpack_require__(786); -var get = __webpack_require__(780); +var isObject = __webpack_require__(783); +var hasValues = __webpack_require__(785); +var get = __webpack_require__(779); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -91681,7 +90379,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 784 */ +/* 783 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91694,7 +90392,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(785); +var isArray = __webpack_require__(784); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -91702,7 +90400,7 @@ module.exports = function isObject(val) { /***/ }), -/* 785 */ +/* 784 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -91713,7 +90411,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 786 */ +/* 785 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91756,7 +90454,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 787 */ +/* 786 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91769,9 +90467,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(749); -var hasValues = __webpack_require__(788); -var get = __webpack_require__(780); +var isObject = __webpack_require__(748); +var hasValues = __webpack_require__(787); +var get = __webpack_require__(779); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -91779,7 +90477,7 @@ module.exports = function(val, prop) { /***/ }), -/* 788 */ +/* 787 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -91792,8 +90490,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(789); -var isNumber = __webpack_require__(753); +var typeOf = __webpack_require__(788); +var isNumber = __webpack_require__(752); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -91846,10 +90544,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 789 */ +/* 788 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(736); +var isBuffer = __webpack_require__(735); var toString = Object.prototype.toString; /** @@ -91971,14 +90669,14 @@ module.exports = function kindOf(val) { /***/ }), -/* 790 */ +/* 789 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(791); -var forIn = __webpack_require__(792); +var isExtendable = __webpack_require__(790); +var forIn = __webpack_require__(791); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -92042,7 +90740,7 @@ module.exports = mixinDeep; /***/ }), -/* 791 */ +/* 790 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92055,7 +90753,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(748); +var isPlainObject = __webpack_require__(747); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -92063,7 +90761,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 792 */ +/* 791 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92086,7 +90784,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 793 */ +/* 792 */ /***/ (function(module, exports) { /*! @@ -92113,14 +90811,14 @@ module.exports = pascalcase; /***/ }), -/* 794 */ +/* 793 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(29); -var utils = __webpack_require__(795); +var utils = __webpack_require__(794); /** * Expose class utils @@ -92485,7 +91183,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 795 */ +/* 794 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92499,10 +91197,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(779); -utils.define = __webpack_require__(731); -utils.isObj = __webpack_require__(749); -utils.staticExtend = __webpack_require__(796); +utils.union = __webpack_require__(778); +utils.define = __webpack_require__(730); +utils.isObj = __webpack_require__(748); +utils.staticExtend = __webpack_require__(795); /** @@ -92513,7 +91211,7 @@ module.exports = utils; /***/ }), -/* 796 */ +/* 795 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92526,8 +91224,8 @@ module.exports = utils; -var copy = __webpack_require__(797); -var define = __webpack_require__(731); +var copy = __webpack_require__(796); +var define = __webpack_require__(730); var util = __webpack_require__(29); /** @@ -92610,15 +91308,15 @@ module.exports = extend; /***/ }), -/* 797 */ +/* 796 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(754); -var copyDescriptor = __webpack_require__(798); -var define = __webpack_require__(731); +var typeOf = __webpack_require__(753); +var copyDescriptor = __webpack_require__(797); +var define = __webpack_require__(730); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -92791,7 +91489,7 @@ module.exports.has = has; /***/ }), -/* 798 */ +/* 797 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -92879,16 +91577,16 @@ function isObject(val) { /***/ }), -/* 799 */ +/* 798 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(800); -var define = __webpack_require__(731); -var debug = __webpack_require__(802)('snapdragon:compiler'); -var utils = __webpack_require__(808); +var use = __webpack_require__(799); +var define = __webpack_require__(730); +var debug = __webpack_require__(801)('snapdragon:compiler'); +var utils = __webpack_require__(807); /** * Create a new `Compiler` with the given `options`. @@ -93042,7 +91740,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(827); + var sourcemaps = __webpack_require__(826); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -93063,7 +91761,7 @@ module.exports = Compiler; /***/ }), -/* 800 */ +/* 799 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -93076,7 +91774,7 @@ module.exports = Compiler; -var utils = __webpack_require__(801); +var utils = __webpack_require__(800); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -93191,7 +91889,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 801 */ +/* 800 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -93205,8 +91903,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(731); -utils.isObject = __webpack_require__(749); +utils.define = __webpack_require__(730); +utils.isObject = __webpack_require__(748); utils.isString = function(val) { @@ -93221,7 +91919,7 @@ module.exports = utils; /***/ }), -/* 802 */ +/* 801 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -93230,14 +91928,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(803); + module.exports = __webpack_require__(802); } else { - module.exports = __webpack_require__(806); + module.exports = __webpack_require__(805); } /***/ }), -/* 803 */ +/* 802 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -93246,7 +91944,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(804); +exports = module.exports = __webpack_require__(803); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -93428,7 +92126,7 @@ function localstorage() { /***/ }), -/* 804 */ +/* 803 */ /***/ (function(module, exports, __webpack_require__) { @@ -93444,7 +92142,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(805); +exports.humanize = __webpack_require__(804); /** * The currently active debug mode names, and names to skip. @@ -93636,7 +92334,7 @@ function coerce(val) { /***/ }), -/* 805 */ +/* 804 */ /***/ (function(module, exports) { /** @@ -93794,7 +92492,7 @@ function plural(ms, n, name) { /***/ }), -/* 806 */ +/* 805 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -93810,7 +92508,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(804); +exports = module.exports = __webpack_require__(803); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -93989,7 +92687,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(807); + var net = __webpack_require__(806); stream = new net.Socket({ fd: fd, readable: false, @@ -94048,13 +92746,13 @@ exports.enable(load()); /***/ }), -/* 807 */ +/* 806 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 808 */ +/* 807 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -94064,9 +92762,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(739); -exports.SourceMap = __webpack_require__(809); -exports.sourceMapResolve = __webpack_require__(820); +exports.extend = __webpack_require__(738); +exports.SourceMap = __webpack_require__(808); +exports.sourceMapResolve = __webpack_require__(819); /** * Convert backslash in the given string to forward slashes @@ -94109,7 +92807,7 @@ exports.last = function(arr, n) { /***/ }), -/* 809 */ +/* 808 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -94117,13 +92815,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(810).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(816).SourceMapConsumer; -exports.SourceNode = __webpack_require__(819).SourceNode; +exports.SourceMapGenerator = __webpack_require__(809).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(815).SourceMapConsumer; +exports.SourceNode = __webpack_require__(818).SourceNode; /***/ }), -/* 810 */ +/* 809 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94133,10 +92831,10 @@ exports.SourceNode = __webpack_require__(819).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(811); -var util = __webpack_require__(813); -var ArraySet = __webpack_require__(814).ArraySet; -var MappingList = __webpack_require__(815).MappingList; +var base64VLQ = __webpack_require__(810); +var util = __webpack_require__(812); +var ArraySet = __webpack_require__(813).ArraySet; +var MappingList = __webpack_require__(814).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -94545,7 +93243,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 811 */ +/* 810 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94585,7 +93283,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(812); +var base64 = __webpack_require__(811); // A single base 64 digit can contain 6 bits of data. For the base 64 variable // length quantities we use in the source map spec, the first bit is the sign, @@ -94691,7 +93389,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 812 */ +/* 811 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -94764,7 +93462,7 @@ exports.decode = function (charCode) { /***/ }), -/* 813 */ +/* 812 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95187,7 +93885,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 814 */ +/* 813 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95197,7 +93895,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(813); +var util = __webpack_require__(812); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -95314,7 +94012,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 815 */ +/* 814 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95324,7 +94022,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(813); +var util = __webpack_require__(812); /** * Determine whether mappingB is after mappingA with respect to generated @@ -95399,7 +94097,7 @@ exports.MappingList = MappingList; /***/ }), -/* 816 */ +/* 815 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -95409,11 +94107,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(813); -var binarySearch = __webpack_require__(817); -var ArraySet = __webpack_require__(814).ArraySet; -var base64VLQ = __webpack_require__(811); -var quickSort = __webpack_require__(818).quickSort; +var util = __webpack_require__(812); +var binarySearch = __webpack_require__(816); +var ArraySet = __webpack_require__(813).ArraySet; +var base64VLQ = __webpack_require__(810); +var quickSort = __webpack_require__(817).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -96487,7 +95185,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 817 */ +/* 816 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -96604,7 +95302,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 818 */ +/* 817 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -96724,7 +95422,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 819 */ +/* 818 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -96734,8 +95432,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(810).SourceMapGenerator; -var util = __webpack_require__(813); +var SourceMapGenerator = __webpack_require__(809).SourceMapGenerator; +var util = __webpack_require__(812); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -97143,17 +95841,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 820 */ +/* 819 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(821) -var resolveUrl = __webpack_require__(822) -var decodeUriComponent = __webpack_require__(823) -var urix = __webpack_require__(825) -var atob = __webpack_require__(826) +var sourceMappingURL = __webpack_require__(820) +var resolveUrl = __webpack_require__(821) +var decodeUriComponent = __webpack_require__(822) +var urix = __webpack_require__(824) +var atob = __webpack_require__(825) @@ -97451,7 +96149,7 @@ module.exports = { /***/ }), -/* 821 */ +/* 820 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -97514,7 +96212,7 @@ void (function(root, factory) { /***/ }), -/* 822 */ +/* 821 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -97532,13 +96230,13 @@ module.exports = resolveUrl /***/ }), -/* 823 */ +/* 822 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(824) +var decodeUriComponent = __webpack_require__(823) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -97549,7 +96247,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 824 */ +/* 823 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97650,7 +96348,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 825 */ +/* 824 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -97673,7 +96371,7 @@ module.exports = urix /***/ }), -/* 826 */ +/* 825 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97687,7 +96385,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 827 */ +/* 826 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -97695,8 +96393,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(23); var path = __webpack_require__(16); -var define = __webpack_require__(731); -var utils = __webpack_require__(808); +var define = __webpack_require__(730); +var utils = __webpack_require__(807); /** * Expose `mixin()`. @@ -97839,19 +96537,19 @@ exports.comment = function(node) { /***/ }), -/* 828 */ +/* 827 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(800); +var use = __webpack_require__(799); var util = __webpack_require__(29); -var Cache = __webpack_require__(829); -var define = __webpack_require__(731); -var debug = __webpack_require__(802)('snapdragon:parser'); -var Position = __webpack_require__(830); -var utils = __webpack_require__(808); +var Cache = __webpack_require__(828); +var define = __webpack_require__(730); +var debug = __webpack_require__(801)('snapdragon:parser'); +var Position = __webpack_require__(829); +var utils = __webpack_require__(807); /** * Create a new `Parser` with the given `input` and `options`. @@ -98379,7 +97077,7 @@ module.exports = Parser; /***/ }), -/* 829 */ +/* 828 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -98486,13 +97184,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 830 */ +/* 829 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(731); +var define = __webpack_require__(730); /** * Store position for a node @@ -98507,16 +97205,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 831 */ +/* 830 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(832); -var define = __webpack_require__(838); -var extend = __webpack_require__(839); -var not = __webpack_require__(841); +var safe = __webpack_require__(831); +var define = __webpack_require__(837); +var extend = __webpack_require__(838); +var not = __webpack_require__(840); var MAX_LENGTH = 1024 * 64; /** @@ -98669,10 +97367,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 832 */ +/* 831 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(833); +var parse = __webpack_require__(832); var types = parse.types; module.exports = function (re, opts) { @@ -98718,13 +97416,13 @@ function isRegExp (x) { /***/ }), -/* 833 */ +/* 832 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(834); -var types = __webpack_require__(835); -var sets = __webpack_require__(836); -var positions = __webpack_require__(837); +var util = __webpack_require__(833); +var types = __webpack_require__(834); +var sets = __webpack_require__(835); +var positions = __webpack_require__(836); module.exports = function(regexpStr) { @@ -99006,11 +97704,11 @@ module.exports.types = types; /***/ }), -/* 834 */ +/* 833 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(835); -var sets = __webpack_require__(836); +var types = __webpack_require__(834); +var sets = __webpack_require__(835); // All of these are private and only used by randexp. @@ -99123,7 +97821,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 835 */ +/* 834 */ /***/ (function(module, exports) { module.exports = { @@ -99139,10 +97837,10 @@ module.exports = { /***/ }), -/* 836 */ +/* 835 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(835); +var types = __webpack_require__(834); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -99227,10 +97925,10 @@ exports.anyChar = function() { /***/ }), -/* 837 */ +/* 836 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(835); +var types = __webpack_require__(834); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -99250,7 +97948,7 @@ exports.end = function() { /***/ }), -/* 838 */ +/* 837 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99263,8 +97961,8 @@ exports.end = function() { -var isobject = __webpack_require__(749); -var isDescriptor = __webpack_require__(761); +var isobject = __webpack_require__(748); +var isDescriptor = __webpack_require__(760); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -99295,14 +97993,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 839 */ +/* 838 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(840); -var assignSymbols = __webpack_require__(750); +var isExtendable = __webpack_require__(839); +var assignSymbols = __webpack_require__(749); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -99362,7 +98060,7 @@ function isEnum(obj, key) { /***/ }), -/* 840 */ +/* 839 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99375,7 +98073,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(748); +var isPlainObject = __webpack_require__(747); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -99383,14 +98081,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 841 */ +/* 840 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(839); -var safe = __webpack_require__(832); +var extend = __webpack_require__(838); +var safe = __webpack_require__(831); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -99462,14 +98160,14 @@ module.exports = toRegex; /***/ }), -/* 842 */ +/* 841 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(843); -var extglob = __webpack_require__(858); +var nanomatch = __webpack_require__(842); +var extglob = __webpack_require__(857); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -99546,7 +98244,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 843 */ +/* 842 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -99557,17 +98255,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(29); -var toRegex = __webpack_require__(730); -var extend = __webpack_require__(844); +var toRegex = __webpack_require__(729); +var extend = __webpack_require__(843); /** * Local dependencies */ -var compilers = __webpack_require__(846); -var parsers = __webpack_require__(847); -var cache = __webpack_require__(850); -var utils = __webpack_require__(852); +var compilers = __webpack_require__(845); +var parsers = __webpack_require__(846); +var cache = __webpack_require__(849); +var utils = __webpack_require__(851); var MAX_LENGTH = 1024 * 64; /** @@ -100391,14 +99089,14 @@ module.exports = nanomatch; /***/ }), -/* 844 */ +/* 843 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(845); -var assignSymbols = __webpack_require__(750); +var isExtendable = __webpack_require__(844); +var assignSymbols = __webpack_require__(749); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -100458,7 +99156,7 @@ function isEnum(obj, key) { /***/ }), -/* 845 */ +/* 844 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100471,7 +99169,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(748); +var isPlainObject = __webpack_require__(747); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -100479,7 +99177,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 846 */ +/* 845 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -100825,15 +99523,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 847 */ +/* 846 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(741); -var toRegex = __webpack_require__(730); -var isOdd = __webpack_require__(848); +var regexNot = __webpack_require__(740); +var toRegex = __webpack_require__(729); +var isOdd = __webpack_require__(847); /** * Characters to use in negation regex (we want to "not" match @@ -101219,7 +99917,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 848 */ +/* 847 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101232,7 +99930,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(849); +var isNumber = __webpack_require__(848); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -101246,7 +99944,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 849 */ +/* 848 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101274,14 +99972,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 850 */ +/* 849 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(851))(); +module.exports = new (__webpack_require__(850))(); /***/ }), -/* 851 */ +/* 850 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101294,7 +99992,7 @@ module.exports = new (__webpack_require__(851))(); -var MapCache = __webpack_require__(829); +var MapCache = __webpack_require__(828); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -101416,7 +100114,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 852 */ +/* 851 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101429,14 +100127,14 @@ var path = __webpack_require__(16); * Module dependencies */ -var isWindows = __webpack_require__(853)(); -var Snapdragon = __webpack_require__(769); -utils.define = __webpack_require__(854); -utils.diff = __webpack_require__(855); -utils.extend = __webpack_require__(844); -utils.pick = __webpack_require__(856); -utils.typeOf = __webpack_require__(857); -utils.unique = __webpack_require__(742); +var isWindows = __webpack_require__(852)(); +var Snapdragon = __webpack_require__(768); +utils.define = __webpack_require__(853); +utils.diff = __webpack_require__(854); +utils.extend = __webpack_require__(843); +utils.pick = __webpack_require__(855); +utils.typeOf = __webpack_require__(856); +utils.unique = __webpack_require__(741); /** * Returns true if the given value is effectively an empty string @@ -101802,7 +100500,7 @@ utils.unixify = function(options) { /***/ }), -/* 853 */ +/* 852 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -101830,7 +100528,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 854 */ +/* 853 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101843,8 +100541,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(749); -var isDescriptor = __webpack_require__(761); +var isobject = __webpack_require__(748); +var isDescriptor = __webpack_require__(760); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -101875,7 +100573,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 855 */ +/* 854 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101929,7 +100627,7 @@ function diffArray(one, two) { /***/ }), -/* 856 */ +/* 855 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -101942,7 +100640,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(749); +var isObject = __webpack_require__(748); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -101971,7 +100669,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 857 */ +/* 856 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -102106,7 +100804,7 @@ function isBuffer(val) { /***/ }), -/* 858 */ +/* 857 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102116,18 +100814,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(739); -var unique = __webpack_require__(742); -var toRegex = __webpack_require__(730); +var extend = __webpack_require__(738); +var unique = __webpack_require__(741); +var toRegex = __webpack_require__(729); /** * Local dependencies */ -var compilers = __webpack_require__(859); -var parsers = __webpack_require__(870); -var Extglob = __webpack_require__(873); -var utils = __webpack_require__(872); +var compilers = __webpack_require__(858); +var parsers = __webpack_require__(869); +var Extglob = __webpack_require__(872); +var utils = __webpack_require__(871); var MAX_LENGTH = 1024 * 64; /** @@ -102444,13 +101142,13 @@ module.exports = extglob; /***/ }), -/* 859 */ +/* 858 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(860); +var brackets = __webpack_require__(859); /** * Extglob compilers @@ -102620,7 +101318,7 @@ module.exports = function(extglob) { /***/ }), -/* 860 */ +/* 859 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102630,17 +101328,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(861); -var parsers = __webpack_require__(863); +var compilers = __webpack_require__(860); +var parsers = __webpack_require__(862); /** * Module dependencies */ -var debug = __webpack_require__(865)('expand-brackets'); -var extend = __webpack_require__(739); -var Snapdragon = __webpack_require__(769); -var toRegex = __webpack_require__(730); +var debug = __webpack_require__(864)('expand-brackets'); +var extend = __webpack_require__(738); +var Snapdragon = __webpack_require__(768); +var toRegex = __webpack_require__(729); /** * Parses the given POSIX character class `pattern` and returns a @@ -102838,13 +101536,13 @@ module.exports = brackets; /***/ }), -/* 861 */ +/* 860 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(862); +var posix = __webpack_require__(861); module.exports = function(brackets) { brackets.compiler @@ -102932,7 +101630,7 @@ module.exports = function(brackets) { /***/ }), -/* 862 */ +/* 861 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102961,14 +101659,14 @@ module.exports = { /***/ }), -/* 863 */ +/* 862 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(864); -var define = __webpack_require__(731); +var utils = __webpack_require__(863); +var define = __webpack_require__(730); /** * Text regex @@ -103187,14 +101885,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 864 */ +/* 863 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(730); -var regexNot = __webpack_require__(741); +var toRegex = __webpack_require__(729); +var regexNot = __webpack_require__(740); var cached; /** @@ -103228,7 +101926,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 865 */ +/* 864 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -103237,14 +101935,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(866); + module.exports = __webpack_require__(865); } else { - module.exports = __webpack_require__(869); + module.exports = __webpack_require__(868); } /***/ }), -/* 866 */ +/* 865 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -103253,7 +101951,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(867); +exports = module.exports = __webpack_require__(866); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -103435,7 +102133,7 @@ function localstorage() { /***/ }), -/* 867 */ +/* 866 */ /***/ (function(module, exports, __webpack_require__) { @@ -103451,7 +102149,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(868); +exports.humanize = __webpack_require__(867); /** * The currently active debug mode names, and names to skip. @@ -103643,7 +102341,7 @@ function coerce(val) { /***/ }), -/* 868 */ +/* 867 */ /***/ (function(module, exports) { /** @@ -103801,7 +102499,7 @@ function plural(ms, n, name) { /***/ }), -/* 869 */ +/* 868 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -103817,7 +102515,7 @@ var util = __webpack_require__(29); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(867); +exports = module.exports = __webpack_require__(866); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -103996,7 +102694,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(807); + var net = __webpack_require__(806); stream = new net.Socket({ fd: fd, readable: false, @@ -104055,15 +102753,15 @@ exports.enable(load()); /***/ }), -/* 870 */ +/* 869 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(860); -var define = __webpack_require__(871); -var utils = __webpack_require__(872); +var brackets = __webpack_require__(859); +var define = __webpack_require__(870); +var utils = __webpack_require__(871); /** * Characters to use in text regex (we want to "not" match @@ -104218,7 +102916,7 @@ module.exports = parsers; /***/ }), -/* 871 */ +/* 870 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104231,7 +102929,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(761); +var isDescriptor = __webpack_require__(760); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -104256,14 +102954,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 872 */ +/* 871 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(741); -var Cache = __webpack_require__(851); +var regex = __webpack_require__(740); +var Cache = __webpack_require__(850); /** * Utils @@ -104332,7 +103030,7 @@ utils.createRegex = function(str) { /***/ }), -/* 873 */ +/* 872 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104342,16 +103040,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(769); -var define = __webpack_require__(871); -var extend = __webpack_require__(739); +var Snapdragon = __webpack_require__(768); +var define = __webpack_require__(870); +var extend = __webpack_require__(738); /** * Local dependencies */ -var compilers = __webpack_require__(859); -var parsers = __webpack_require__(870); +var compilers = __webpack_require__(858); +var parsers = __webpack_require__(869); /** * Customize Snapdragon parser and renderer @@ -104417,16 +103115,16 @@ module.exports = Extglob; /***/ }), -/* 874 */ +/* 873 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(858); -var nanomatch = __webpack_require__(843); -var regexNot = __webpack_require__(741); -var toRegex = __webpack_require__(831); +var extglob = __webpack_require__(857); +var nanomatch = __webpack_require__(842); +var regexNot = __webpack_require__(740); +var toRegex = __webpack_require__(830); var not; /** @@ -104507,14 +103205,14 @@ function textRegex(pattern) { /***/ }), -/* 875 */ +/* 874 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(851))(); +module.exports = new (__webpack_require__(850))(); /***/ }), -/* 876 */ +/* 875 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104527,13 +103225,13 @@ var path = __webpack_require__(16); * Module dependencies */ -var Snapdragon = __webpack_require__(769); -utils.define = __webpack_require__(838); -utils.diff = __webpack_require__(855); -utils.extend = __webpack_require__(839); -utils.pick = __webpack_require__(856); -utils.typeOf = __webpack_require__(877); -utils.unique = __webpack_require__(742); +var Snapdragon = __webpack_require__(768); +utils.define = __webpack_require__(837); +utils.diff = __webpack_require__(854); +utils.extend = __webpack_require__(838); +utils.pick = __webpack_require__(855); +utils.typeOf = __webpack_require__(876); +utils.unique = __webpack_require__(741); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -104830,7 +103528,7 @@ utils.unixify = function(options) { /***/ }), -/* 877 */ +/* 876 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -104965,7 +103663,7 @@ function isBuffer(val) { /***/ }), -/* 878 */ +/* 877 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104984,9 +103682,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(879); -var reader_1 = __webpack_require__(892); -var fs_stream_1 = __webpack_require__(896); +var readdir = __webpack_require__(878); +var reader_1 = __webpack_require__(891); +var fs_stream_1 = __webpack_require__(895); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -105047,15 +103745,15 @@ exports.default = ReaderAsync; /***/ }), -/* 879 */ +/* 878 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(880); -const readdirAsync = __webpack_require__(888); -const readdirStream = __webpack_require__(891); +const readdirSync = __webpack_require__(879); +const readdirAsync = __webpack_require__(887); +const readdirStream = __webpack_require__(890); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -105139,7 +103837,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 880 */ +/* 879 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105147,11 +103845,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(881); +const DirectoryReader = __webpack_require__(880); let syncFacade = { - fs: __webpack_require__(886), - forEach: __webpack_require__(887), + fs: __webpack_require__(885), + forEach: __webpack_require__(886), sync: true }; @@ -105180,7 +103878,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 881 */ +/* 880 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105189,9 +103887,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(27).Readable; const EventEmitter = __webpack_require__(379).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(882); -const stat = __webpack_require__(884); -const call = __webpack_require__(885); +const normalizeOptions = __webpack_require__(881); +const stat = __webpack_require__(883); +const call = __webpack_require__(884); /** * Asynchronously reads the contents of a directory and streams the results @@ -105567,14 +104265,14 @@ module.exports = DirectoryReader; /***/ }), -/* 882 */ +/* 881 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(883); +const globToRegExp = __webpack_require__(882); module.exports = normalizeOptions; @@ -105751,7 +104449,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 883 */ +/* 882 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -105888,13 +104586,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 884 */ +/* 883 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(885); +const call = __webpack_require__(884); module.exports = stat; @@ -105969,7 +104667,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 885 */ +/* 884 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106030,14 +104728,14 @@ function callOnce (fn) { /***/ }), -/* 886 */ +/* 885 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(885); +const call = __webpack_require__(884); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -106101,7 +104799,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 887 */ +/* 886 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106130,7 +104828,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 888 */ +/* 887 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106138,12 +104836,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(889); -const DirectoryReader = __webpack_require__(881); +const maybe = __webpack_require__(888); +const DirectoryReader = __webpack_require__(880); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(890), + forEach: __webpack_require__(889), async: true }; @@ -106185,7 +104883,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 889 */ +/* 888 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106212,7 +104910,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 890 */ +/* 889 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106248,7 +104946,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 891 */ +/* 890 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106256,11 +104954,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(881); +const DirectoryReader = __webpack_require__(880); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(890), + forEach: __webpack_require__(889), async: true }; @@ -106280,16 +104978,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 892 */ +/* 891 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(893); -var entry_1 = __webpack_require__(895); -var pathUtil = __webpack_require__(894); +var deep_1 = __webpack_require__(892); +var entry_1 = __webpack_require__(894); +var pathUtil = __webpack_require__(893); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -106355,14 +105053,14 @@ exports.default = Reader; /***/ }), -/* 893 */ +/* 892 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(894); -var patternUtils = __webpack_require__(723); +var pathUtils = __webpack_require__(893); +var patternUtils = __webpack_require__(722); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -106445,7 +105143,7 @@ exports.default = DeepFilter; /***/ }), -/* 894 */ +/* 893 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106476,14 +105174,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 895 */ +/* 894 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(894); -var patternUtils = __webpack_require__(723); +var pathUtils = __webpack_require__(893); +var patternUtils = __webpack_require__(722); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -106568,7 +105266,7 @@ exports.default = EntryFilter; /***/ }), -/* 896 */ +/* 895 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106588,8 +105286,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var fsStat = __webpack_require__(897); -var fs_1 = __webpack_require__(901); +var fsStat = __webpack_require__(896); +var fs_1 = __webpack_require__(900); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -106639,14 +105337,14 @@ exports.default = FileSystemStream; /***/ }), -/* 897 */ +/* 896 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(898); -const statProvider = __webpack_require__(900); +const optionsManager = __webpack_require__(897); +const statProvider = __webpack_require__(899); /** * Asynchronous API. */ @@ -106677,13 +105375,13 @@ exports.statSync = statSync; /***/ }), -/* 898 */ +/* 897 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(899); +const fsAdapter = __webpack_require__(898); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -106696,7 +105394,7 @@ exports.prepare = prepare; /***/ }), -/* 899 */ +/* 898 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106719,7 +105417,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 900 */ +/* 899 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106771,7 +105469,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 901 */ +/* 900 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106802,7 +105500,7 @@ exports.default = FileSystem; /***/ }), -/* 902 */ +/* 901 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106822,9 +105520,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var readdir = __webpack_require__(879); -var reader_1 = __webpack_require__(892); -var fs_stream_1 = __webpack_require__(896); +var readdir = __webpack_require__(878); +var reader_1 = __webpack_require__(891); +var fs_stream_1 = __webpack_require__(895); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -106892,7 +105590,7 @@ exports.default = ReaderStream; /***/ }), -/* 903 */ +/* 902 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106911,9 +105609,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(879); -var reader_1 = __webpack_require__(892); -var fs_sync_1 = __webpack_require__(904); +var readdir = __webpack_require__(878); +var reader_1 = __webpack_require__(891); +var fs_sync_1 = __webpack_require__(903); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -106973,7 +105671,7 @@ exports.default = ReaderSync; /***/ }), -/* 904 */ +/* 903 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106992,8 +105690,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(897); -var fs_1 = __webpack_require__(901); +var fsStat = __webpack_require__(896); +var fs_1 = __webpack_require__(900); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -107039,7 +105737,7 @@ exports.default = FileSystemSync; /***/ }), -/* 905 */ +/* 904 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107055,13 +105753,13 @@ exports.flatten = flatten; /***/ }), -/* 906 */ +/* 905 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(591); +var merge2 = __webpack_require__(590); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ @@ -107076,13 +105774,13 @@ exports.merge = merge; /***/ }), -/* 907 */ +/* 906 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(908); +const pathType = __webpack_require__(907); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -107148,13 +105846,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 908 */ +/* 907 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(909); +const pify = __webpack_require__(908); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -107197,7 +105895,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 909 */ +/* 908 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107288,17 +105986,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 910 */ +/* 909 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); const path = __webpack_require__(16); -const fastGlob = __webpack_require__(719); -const gitIgnore = __webpack_require__(911); -const pify = __webpack_require__(912); -const slash = __webpack_require__(913); +const fastGlob = __webpack_require__(718); +const gitIgnore = __webpack_require__(910); +const pify = __webpack_require__(911); +const slash = __webpack_require__(912); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -107396,7 +106094,7 @@ module.exports.sync = options => { /***/ }), -/* 911 */ +/* 910 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -107865,7 +106563,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 912 */ +/* 911 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107940,7 +106638,7 @@ module.exports = (input, options) => { /***/ }), -/* 913 */ +/* 912 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107958,17 +106656,17 @@ module.exports = input => { /***/ }), -/* 914 */ +/* 913 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const pEvent = __webpack_require__(915); -const CpFileError = __webpack_require__(918); -const fs = __webpack_require__(922); -const ProgressEmitter = __webpack_require__(925); +const pEvent = __webpack_require__(914); +const CpFileError = __webpack_require__(917); +const fs = __webpack_require__(921); +const ProgressEmitter = __webpack_require__(924); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -108082,12 +106780,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 915 */ +/* 914 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(916); +const pTimeout = __webpack_require__(915); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -108378,12 +107076,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 916 */ +/* 915 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(917); +const pFinally = __webpack_require__(916); class TimeoutError extends Error { constructor(message) { @@ -108429,7 +107127,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 917 */ +/* 916 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -108451,12 +107149,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 918 */ +/* 917 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(919); +const NestedError = __webpack_require__(918); class CpFileError extends NestedError { constructor(message, nested) { @@ -108470,10 +107168,10 @@ module.exports = CpFileError; /***/ }), -/* 919 */ +/* 918 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(920); +var inherits = __webpack_require__(919); var NestedError = function (message, nested) { this.nested = nested; @@ -108524,7 +107222,7 @@ module.exports = NestedError; /***/ }), -/* 920 */ +/* 919 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -108532,12 +107230,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(921); + module.exports = __webpack_require__(920); } /***/ }), -/* 921 */ +/* 920 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -108566,16 +107264,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 922 */ +/* 921 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(29); const fs = __webpack_require__(22); -const makeDir = __webpack_require__(923); -const pEvent = __webpack_require__(915); -const CpFileError = __webpack_require__(918); +const makeDir = __webpack_require__(922); +const pEvent = __webpack_require__(914); +const CpFileError = __webpack_require__(917); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -108672,7 +107370,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 923 */ +/* 922 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -108680,7 +107378,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const {promisify} = __webpack_require__(29); -const semver = __webpack_require__(924); +const semver = __webpack_require__(923); const defaults = { mode: 0o777 & (~process.umask()), @@ -108829,7 +107527,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 924 */ +/* 923 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -110431,7 +109129,7 @@ function coerce (version, options) { /***/ }), -/* 925 */ +/* 924 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -110472,7 +109170,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 926 */ +/* 925 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -110518,12 +109216,12 @@ exports.default = module.exports; /***/ }), -/* 927 */ +/* 926 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(928); +const NestedError = __webpack_require__(927); class CpyError extends NestedError { constructor(message, nested) { @@ -110537,7 +109235,7 @@ module.exports = CpyError; /***/ }), -/* 928 */ +/* 927 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -110593,7 +109291,7 @@ module.exports = NestedError; /***/ }), -/* 929 */ +/* 928 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts b/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts index dbc0894ab8071..89c0d3d2403c5 100644 --- a/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts +++ b/x-pack/legacy/plugins/beats_management/public/utils/typed_react.ts @@ -4,58 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { omit } from 'lodash'; import React from 'react'; -import { InferableComponentEnhancerWithProps } from 'react-redux'; export type RendererResult = React.ReactElement | null; export type RendererFunction = (args: RenderArgs) => Result; -export type ChildFunctionRendererProps = { - children: RendererFunction; - initializeOnMount?: boolean; - resetOnUnmount?: boolean; -} & RenderArgs; - -interface ChildFunctionRendererOptions { - onInitialize?: (props: RenderArgs) => void; - onCleanup?: (props: RenderArgs) => void; -} - -export const asChildFunctionRenderer = ( - hoc: InferableComponentEnhancerWithProps, - { onInitialize, onCleanup }: ChildFunctionRendererOptions = {} -) => - hoc( - class ChildFunctionRenderer extends React.Component> { - public displayName = 'ChildFunctionRenderer'; - - public componentDidMount() { - if (this.props.initializeOnMount && onInitialize) { - onInitialize(this.getRendererArgs()); - } - } - - public componentWillUnmount() { - if (this.props.resetOnUnmount && onCleanup) { - onCleanup(this.getRendererArgs()); - } - } - - public render() { - return (this.props.children as ChildFunctionRendererProps['children'])( - this.getRendererArgs() - ); - } - - private getRendererArgs = () => - omit(['children', 'initializeOnMount', 'resetOnUnmount'], this.props) as Pick< - ChildFunctionRendererProps, - keyof InjectedProps - >; - } - ); - export type StateUpdater = ( prevState: Readonly, prevProps: Readonly diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx index e855a381413eb..e8b772e0f2fbd 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx +++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.test.tsx @@ -12,6 +12,7 @@ import { WorkpadLoadedWithErrorsMetric, } from './workpad_telemetry'; import { METRIC_TYPE } from '../../../lib/ui_metric'; +import { ResolvedArgType } from '../../../../types'; jest.mock('ui/new_platform'); const trackMetric = jest.fn(); @@ -30,15 +31,15 @@ const mockWorkpad = { }; const resolvedArgsMatchWorkpad = { - '1': {}, - '2': {}, - '3': {}, - '4': {}, - '5': {}, + '1': {} as ResolvedArgType, + '2': {} as ResolvedArgType, + '3': {} as ResolvedArgType, + '4': {} as ResolvedArgType, + '5': {} as ResolvedArgType, }; const resolvedArgsNotMatchWorkpad = { - 'non-matching-id': {}, + 'non-matching-id': {} as ResolvedArgType, }; const pendingCounts = { diff --git a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.tsx b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.tsx index 39ff3f45d602a..5f3f114092e61 100644 --- a/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.tsx +++ b/x-pack/legacy/plugins/canvas/public/apps/workpad/workpad_app/workpad_telemetry.tsx @@ -5,7 +5,7 @@ */ import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; // @ts-ignore: Local Untyped import { trackCanvasUiMetric, METRIC_TYPE } from '../../../lib/ui_metric'; // @ts-ignore: Local Untyped @@ -23,18 +23,6 @@ const mapStateToProps = (state: any) => ({ telemetryResolvedArgs: getArgs(state), }); -/** - Counts of the loading states of workpad elements -*/ -interface ElementCounts { - /** Count of elements in error state */ - error: number; - /** Count of elements in pending state */ - pending: number; - /** Count of elements in ready state */ - ready: number; -} - // TODO: Build out full workpad types /** Individual Page of a Workpad @@ -61,10 +49,8 @@ interface ResolvedArgs { [keys: string]: any; } -interface ElementsLoadedTelemetryProps { - telemetryElementCounts: ElementCounts; +interface ElementsLoadedTelemetryProps extends PropsFromRedux { workpad: Workpad; - telemetryResolvedArgs: {}; } function areAllElementsInResolvedArgs(workpad: Workpad, resolvedArgs: ResolvedArgs): boolean { @@ -77,13 +63,11 @@ function areAllElementsInResolvedArgs(workpad: Workpad, resolvedArgs: ResolvedAr return workpadElements.every(element => resolvedArgsElements.includes(element)); } -export const withUnconnectedElementsLoadedTelemetry = function

( +export const withUnconnectedElementsLoadedTelemetry =

( Component: React.ComponentType

, trackMetric = trackCanvasUiMetric -): React.FC

{ - return function ElementsLoadedTelemetry( - props: P & ElementsLoadedTelemetryProps - ): React.FunctionComponentElement

{ +) => + function ElementsLoadedTelemetry(props: ElementsLoadedTelemetryProps) { const { telemetryElementCounts, workpad, telemetryResolvedArgs, ...other } = props; const [currentWorkpadId, setWorkpadId] = useState(undefined); @@ -127,11 +111,12 @@ export const withUnconnectedElementsLoadedTelemetry = function

return ; }; -}; -export const withElementsLoadedTelemetry =

( - Component: React.ComponentType

-) => { +const connector = connect(mapStateToProps, {}); + +type PropsFromRedux = ConnectedProps; + +export const withElementsLoadedTelemetry =

(Component: React.ComponentType

) => { const telemetry = withUnconnectedElementsLoadedTelemetry(Component); - return connect(mapStateToProps)(telemetry); + return connector(telemetry); }; diff --git a/x-pack/legacy/plugins/canvas/public/components/router/router.js b/x-pack/legacy/plugins/canvas/public/components/router/router.js index b2e4d540b89d8..40c9c469e5505 100644 --- a/x-pack/legacy/plugins/canvas/public/components/router/router.js +++ b/x-pack/legacy/plugins/canvas/public/components/router/router.js @@ -5,7 +5,6 @@ */ import React from 'react'; -import { isClassComponent } from 'recompose'; import PropTypes from 'prop-types'; import { routerProvider } from '../../lib/router_provider'; import { getAppState } from '../../lib/app_state'; @@ -98,9 +97,6 @@ export class Router extends React.PureComponent { return React.createElement(CanvasLoading, { msg: this.props.loadingMessage }); } - // show the activeComponent - return isClassComponent(this.state.activeComponent) - ? React.createElement(this.state.activeComponent, {}) - : this.state.activeComponent({}); + return ; } } diff --git a/x-pack/legacy/plugins/canvas/public/components/sidebar/element_settings/index.tsx b/x-pack/legacy/plugins/canvas/public/components/sidebar/element_settings/index.tsx index 7532b43a01054..b5e44b4996537 100644 --- a/x-pack/legacy/plugins/canvas/public/components/sidebar/element_settings/index.tsx +++ b/x-pack/legacy/plugins/canvas/public/components/sidebar/element_settings/index.tsx @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; -import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { getElementById, getSelectedPage } from '../../../state/selectors/workpad'; import { ElementSettings as Component } from './element_settings'; @@ -33,7 +32,3 @@ const renderIfElement: React.FunctionComponent = props => { export const ElementSettings = connect(mapStateToProps)( renderIfElement ); - -ElementSettings.propTypes = { - selectedElementId: PropTypes.string.isRequired, -}; diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx index d7cbcf59213be..948f89705a5d5 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.test.tsx @@ -70,8 +70,9 @@ describe('field_manager', () => { store.dispatch = dispatchSpy; instance = shallow( + // https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526 - {}} /> + {}} store={store} /> ); @@ -79,6 +80,7 @@ describe('field_manager', () => { instance .find(FieldManager) .dive() + .dive() .dive(); }); diff --git a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx index 89b325d737bd5..9bca5b82e58aa 100644 --- a/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx +++ b/x-pack/legacy/plugins/graph/public/components/field_manager/field_manager.tsx @@ -18,6 +18,7 @@ import { selectField, deselectField, GraphState, + GraphStore, } from '../../state_management'; import { WorkspaceField } from '../../types'; @@ -35,6 +36,7 @@ export function FieldManagerComponent(props: { deselectField: (fieldName: string) => void; pickerOpen: boolean; setPickerOpen: (open: boolean) => void; + store?: GraphStore; // only for testing purpose }) { return ( diff --git a/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx b/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx index 5095edd4c715c..3653a6d6bbeae 100644 --- a/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx +++ b/x-pack/legacy/plugins/infra/public/components/logging/log_time_controls.tsx @@ -16,7 +16,7 @@ const noop = () => undefined; interface LogTimeControlsProps { currentTime: number | null; startLiveStreaming: () => any; - stopLiveStreaming: () => any; + stopLiveStreaming: () => void; isLiveStreaming: boolean; jumpToTime: (time: number) => any; } diff --git a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_time_controls.tsx b/x-pack/legacy/plugins/infra/public/components/waffle/waffle_time_controls.tsx index 373cfdce5e2a9..4f840336de8c3 100644 --- a/x-pack/legacy/plugins/infra/public/components/waffle/waffle_time_controls.tsx +++ b/x-pack/legacy/plugins/infra/public/components/waffle/waffle_time_controls.tsx @@ -8,13 +8,14 @@ import { EuiButtonEmpty, EuiDatePicker, EuiFormControlLayout } from '@elastic/eu import { FormattedMessage } from '@kbn/i18n/react'; import moment, { Moment } from 'moment'; import React from 'react'; +import { Action } from 'typescript-fsa'; interface WaffleTimeControlsProps { currentTime: number; isLiveStreaming?: boolean; onChangeTime?: (time: number) => void; - startLiveStreaming?: () => void; - stopLiveStreaming?: () => void; + startLiveStreaming?: (payload: void) => Action; + stopLiveStreaming?: (payload: void) => Action; } export class WaffleTimeControls extends React.Component { diff --git a/x-pack/legacy/plugins/infra/public/utils/redux_context.tsx b/x-pack/legacy/plugins/infra/public/utils/redux_context.tsx index 3bd3d31c745a9..f249d72a6b56f 100644 --- a/x-pack/legacy/plugins/infra/public/utils/redux_context.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/redux_context.tsx @@ -4,13 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { connect } from 'react-redux'; +import { useSelector } from 'react-redux'; import React, { createContext } from 'react'; import { State, initialState } from '../store'; export const ReduxStateContext = createContext(initialState); -const withRedux = connect((state: State) => state); -export const ReduxStateContextProvider = withRedux(({ children, ...state }) => { - return {children}; -}); +export const ReduxStateContextProvider = ({ children }: { children: JSX.Element }) => { + const state = useSelector((store: State) => store); + return {children}; +}; diff --git a/x-pack/legacy/plugins/infra/public/utils/typed_react.tsx b/x-pack/legacy/plugins/infra/public/utils/typed_react.tsx index eadf3eb9686b5..d057899ba18be 100644 --- a/x-pack/legacy/plugins/infra/public/utils/typed_react.tsx +++ b/x-pack/legacy/plugins/infra/public/utils/typed_react.tsx @@ -6,7 +6,7 @@ import omit from 'lodash/fp/omit'; import React from 'react'; -import { InferableComponentEnhancerWithProps } from 'react-redux'; +import { InferableComponentEnhancerWithProps, ConnectedComponent } from 'react-redux'; export type RendererResult = React.ReactElement | null; export type RendererFunction = (args: RenderArgs) => Result; @@ -25,7 +25,14 @@ interface ChildFunctionRendererOptions { export const asChildFunctionRenderer = ( hoc: InferableComponentEnhancerWithProps, { onInitialize, onCleanup }: ChildFunctionRendererOptions = {} -) => +): ConnectedComponent< + React.ComponentClass<{}>, + { + children: RendererFunction; + initializeOnMount?: boolean; + resetOnUnmount?: boolean; + } & OwnProps +> => hoc( class ChildFunctionRenderer extends React.Component> { public displayName = 'ChildFunctionRenderer'; @@ -53,7 +60,7 @@ export const asChildFunctionRenderer = ( ChildFunctionRendererProps, keyof InjectedProps >; - } + } as any ); export type StateUpdater = ( diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js index b3f8ee727dff5..d2b43775c5a49 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/index.js @@ -52,7 +52,7 @@ function mapDispatchToProps(dispatch) { }; } -const connectedFlyOut = connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })( +const connectedFlyOut = connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })( AddLayerPanel ); export { connectedFlyOut as AddLayerPanel }; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js index 9148fbdfd2d1e..a2f121a9377fe 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/index.js @@ -69,6 +69,6 @@ function mapDispatchToProps(dispatch) { } const connectedMBMapContainer = connect(mapStateToProps, mapDispatchToProps, null, { - withRef: true, + forwardRef: true, })(MBMapContainer); export { connectedMBMapContainer as MBMapContainer }; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js index 0ae497b480776..1b5bf654822b2 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/index.js @@ -21,7 +21,7 @@ function mapStateToProps(state = {}) { }; } -const connectedLayerTOC = connect(mapStateToProps, mapDispatchToProps, null, { withRef: true })( +const connectedLayerTOC = connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })( LayerTOC ); export { connectedLayerTOC as LayerTOC }; diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx index fe5d3ff0c29fb..70a3d1c7d616c 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/population_view/metric_selection.tsx @@ -48,7 +48,10 @@ export const PopulationDetectors: FC = ({ setIsValid }) => { const [chartSettings, setChartSettings] = useState(defaultChartSettings); const [splitField, setSplitField] = useState(jobCreator.splitField); const [fieldValuesPerDetector, setFieldValuesPerDetector] = useState({}); - const [byFieldsUpdated, setByFieldsUpdated] = useReducer<(s: number) => number>(s => s + 1, 0); + const [byFieldsUpdated, setByFieldsUpdated] = useReducer<(s: number, action: any) => number>( + s => s + 1, + 0 + ); const [pageReady, setPageReady] = useState(false); const updateByFields = () => setByFieldsUpdated(0); diff --git a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard.tsx b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard.tsx index b63ada4bb535c..6fe27457428b1 100644 --- a/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard.tsx +++ b/x-pack/legacy/plugins/ml/public/application/jobs/new_job/pages/new_job/wizard.tsx @@ -41,13 +41,15 @@ export const Wizard: FC = ({ existingJobsAndGroups, firstWizardStep = WIZARD_STEPS.TIME_RANGE, }) => { - const [jobCreatorUpdated, setJobCreatorUpdate] = useReducer<(s: number) => number>(s => s + 1, 0); - const jobCreatorUpdate = () => setJobCreatorUpdate(jobCreatorUpdated); - - const [jobValidatorUpdated, setJobValidatorUpdate] = useReducer<(s: number) => number>( + const [jobCreatorUpdated, setJobCreatorUpdate] = useReducer<(s: number, action: any) => number>( s => s + 1, 0 ); + const jobCreatorUpdate = () => setJobCreatorUpdate(jobCreatorUpdated); + + const [jobValidatorUpdated, setJobValidatorUpdate] = useReducer< + (s: number, action: any) => number + >(s => s + 1, 0); const jobCreatorContext: JobCreatorContextValue = { jobCreatorUpdated, diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx index f9e6bfcf7c236..72f5a62d0af97 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -7,7 +7,7 @@ import { defaultTo, noop } from 'lodash/fp'; import React, { useCallback } from 'react'; import { DropResult, DragDropContext } from 'react-beautiful-dnd'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; import { BeforeCapture } from './drag_drop_context'; @@ -30,7 +30,6 @@ import { interface Props { browserFields: BrowserFields; children: React.ReactNode; - dataProviders?: dragAndDropModel.IdToDataProvider; dispatch: Dispatch; } @@ -59,7 +58,7 @@ const onDragEndHandler = ({ /** * DragDropContextWrapperComponent handles all drag end events */ -export const DragDropContextWrapperComponent = React.memo( +export const DragDropContextWrapperComponent = React.memo( ({ browserFields, children, dataProviders, dispatch }) => { const onDragEnd = useCallback( (result: DropResult) => { @@ -112,7 +111,11 @@ const mapStateToProps = (state: State) => { return { dataProviders }; }; -export const DragDropContextWrapper = connect(mapStateToProps)(DragDropContextWrapperComponent); +const connector = connect(mapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const DragDropContextWrapper = connector(DragDropContextWrapperComponent); DragDropContextWrapper.displayName = 'DragDropContextWrapper'; diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx index 9672097713a9b..cf958bfd75d3b 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx @@ -12,9 +12,8 @@ import { DraggableStateSnapshot, Droppable, } from 'react-beautiful-dnd'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import styled from 'styled-components'; -import { ActionCreator } from 'typescript-fsa'; import { EuiPortal } from '@elastic/eui'; import { dragAndDropActions } from '../../store/drag_and_drop'; @@ -59,16 +58,7 @@ interface OwnProps { truncate?: boolean; } -interface DispatchProps { - registerProvider?: ActionCreator<{ - provider: DataProvider; - }>; - unRegisterProvider?: ActionCreator<{ - id: string; - }>; -} - -type Props = OwnProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; /** * Wraps a draggable component to handle registration / unregistration of the @@ -141,10 +131,16 @@ const DraggableWrapperComponent = React.memo( DraggableWrapperComponent.displayName = 'DraggableWrapperComponent'; -export const DraggableWrapper = connect(null, { +const mapDispatchToProps = { registerProvider: dragAndDropActions.registerProvider, unRegisterProvider: dragAndDropActions.unRegisterProvider, -})(DraggableWrapperComponent); +}; + +const connector = connect(null, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const DraggableWrapper = connector(DraggableWrapperComponent); DraggableWrapper.displayName = 'DraggableWrapper'; diff --git a/x-pack/legacy/plugins/siem/public/components/error_toast_dispatcher/index.tsx b/x-pack/legacy/plugins/siem/public/components/error_toast_dispatcher/index.tsx index 3628330fbd459..ab9170ce1dd6a 100644 --- a/x-pack/legacy/plugins/siem/public/components/error_toast_dispatcher/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/error_toast_dispatcher/index.tsx @@ -5,10 +5,9 @@ */ import { useEffect } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; -import { appModel, appSelectors, State } from '../../store'; +import { appSelectors, State } from '../../store'; import { appActions } from '../../store/app'; import { useStateToaster } from '../toasters'; @@ -16,15 +15,7 @@ interface OwnProps { toastLifeTimeMs?: number; } -interface ReduxProps { - errors?: appModel.Error[]; -} - -interface DispatchProps { - removeError: ActionCreator<{ id: string }>; -} - -type Props = OwnProps & ReduxProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; const ErrorToastDispatcherComponent = ({ toastLifeTimeMs = 5000, @@ -58,6 +49,12 @@ const makeMapStateToProps = () => { return (state: State) => getErrorSelector(state); }; -export const ErrorToastDispatcher = connect(makeMapStateToProps, { +const mapDispatchToProps = { removeError: appActions.removeError, -})(ErrorToastDispatcherComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const ErrorToastDispatcher = connector(ErrorToastDispatcherComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx index c7ccff5bdfcff..1e7c7fc2411bb 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx @@ -6,20 +6,16 @@ import { isEqual } from 'lodash/fp'; import React, { useCallback, useMemo, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store'; import { inputsActions, timelineActions } from '../../store/actions'; -import { KqlMode, SubsetTimelineModel, TimelineModel } from '../../store/timeline/model'; +import { SubsetTimelineModel, TimelineModel } from '../../store/timeline/model'; import { ColumnHeader } from '../timeline/body/column_headers/column_header'; -import { DataProvider } from '../timeline/data_providers/data_provider'; -import { Sort } from '../timeline/body/sort'; import { OnChangeItemsPerPage } from '../timeline/events'; -import { Filter, Query } from '../../../../../../../src/plugins/data/public'; +import { Filter } from '../../../../../../../src/plugins/data/public'; import { useUiSetting } from '../../lib/kibana'; import { EventsViewer } from './events_viewer'; -import { InputsModelId } from '../../store/inputs/constants'; import { useFetchIndexPatterns } from '../../containers/detection_engine/rules/fetch_index_patterns'; import { TimelineTypeContextProps } from '../timeline/timeline_context'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; @@ -38,51 +34,7 @@ export interface OwnProps { utilityBar?: (refetch: inputsModel.Refetch, totalCount: number) => React.ReactNode; } -interface StateReduxProps { - columns: ColumnHeader[]; - dataProviders?: DataProvider[]; - filters: Filter[]; - isLive: boolean; - itemsPerPage?: number; - itemsPerPageOptions?: number[]; - kqlMode: KqlMode; - deletedEventIds: Readonly; - query: Query; - pageCount?: number; - sort?: Sort; - showCheckboxes: boolean; - showRowRenderers: boolean; -} - -interface DispatchProps { - createTimeline: ActionCreator<{ - id: string; - columns: ColumnHeader[]; - itemsPerPage?: number; - sort?: Sort; - showCheckboxes?: boolean; - showRowRenderers?: boolean; - }>; - deleteEventQuery: ActionCreator<{ - id: string; - inputId: InputsModelId; - }>; - removeColumn: ActionCreator<{ - id: string; - columnId: string; - }>; - updateItemsPerPage: ActionCreator<{ - id: string; - itemsPerPage: number; - }>; - upsertColumn: ActionCreator<{ - column: ColumnHeader; - id: string; - index: number; - }>; -} - -type Props = OwnProps & StateReduxProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; const defaultTimelineTypeContext = { loadingText: i18n.LOADING_EVENTS, @@ -224,13 +176,19 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const StatefulEventsViewer = connect(makeMapStateToProps, { +const mapDispatchToProps = { createTimeline: timelineActions.createTimeline, deleteEventQuery: inputsActions.deleteOneQuery, updateItemsPerPage: timelineActions.updateItemsPerPage, removeColumn: timelineActions.removeColumn, upsertColumn: timelineActions.upsertColumn, -})( +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulEventsViewer = connector( React.memo( StatefulEventsViewerComponent, (prevProps, nextProps) => @@ -245,7 +203,6 @@ export const StatefulEventsViewer = connect(makeMapStateToProps, { isEqual(prevProps.itemsPerPageOptions, nextProps.itemsPerPageOptions) && prevProps.kqlMode === nextProps.kqlMode && isEqual(prevProps.query, nextProps.query) && - prevProps.pageCount === nextProps.pageCount && isEqual(prevProps.sort, nextProps.sort) && prevProps.start === nextProps.start && isEqual(prevProps.pageFilters, nextProps.pageFilters) && diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx index 24e4cd77b20d3..cf9e4f57d67b7 100644 --- a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.test.tsx @@ -6,6 +6,7 @@ import { mount } from 'enzyme'; import React from 'react'; +import { ActionCreator } from 'typescript-fsa'; import { mockBrowserFields } from '../../containers/source/mock'; import { TestProviders } from '../../mock'; @@ -13,6 +14,7 @@ import { TestProviders } from '../../mock'; import { FIELD_BROWSER_HEIGHT, FIELD_BROWSER_WIDTH } from './helpers'; import { StatefulFieldsBrowserComponent } from '.'; +import { ColumnHeader } from '../timeline/body/column_headers/column_header'; // Suppress warnings about "react-beautiful-dnd" until we migrate to @testing-library/react /* eslint-disable no-console */ @@ -27,6 +29,17 @@ afterAll(() => { console.warn = originalWarn; }); +const removeColumnMock = (jest.fn() as unknown) as ActionCreator<{ + id: string; + columnId: string; +}>; + +const upsertColumnMock = (jest.fn() as unknown) as ActionCreator<{ + column: ColumnHeader; + id: string; + index: number; +}>; + describe('StatefulFieldsBrowser', () => { const timelineId = 'test'; @@ -41,6 +54,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -65,6 +80,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -83,6 +100,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -111,6 +130,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -142,6 +163,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -180,6 +203,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -206,6 +231,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); @@ -232,6 +259,8 @@ describe('StatefulFieldsBrowser', () => { timelineId={timelineId} toggleColumn={jest.fn()} width={FIELD_BROWSER_WIDTH} + removeColumn={removeColumnMock} + upsertColumn={upsertColumnMock} /> ); diff --git a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx index c8cde5fa02a51..b545c6890bd41 100644 --- a/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/fields_browser/index.tsx @@ -7,9 +7,8 @@ import { EuiButtonEmpty, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; import { noop } from 'lodash/fp'; import React, { useEffect, useRef, useState, useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import styled from 'styled-components'; -import { ActionCreator } from 'typescript-fsa'; import { BrowserFields } from '../../containers/source'; import { timelineActions } from '../../store/actions'; @@ -31,22 +30,10 @@ const FieldsBrowserButtonContainer = styled.div` FieldsBrowserButtonContainer.displayName = 'FieldsBrowserButtonContainer'; -interface DispatchProps { - removeColumn?: ActionCreator<{ - id: string; - columnId: string; - }>; - upsertColumn?: ActionCreator<{ - column: ColumnHeader; - id: string; - index: number; - }>; -} - /** * Manages the state of the field browser */ -export const StatefulFieldsBrowserComponent = React.memo( +export const StatefulFieldsBrowserComponent = React.memo( ({ columnHeaders, browserFields, @@ -212,7 +199,13 @@ export const StatefulFieldsBrowserComponent = React.memo; + +export const StatefulFieldsBrowser = connector(React.memo(StatefulFieldsBrowserComponent)); diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx index e1075c89ca350..901f7afd1ed48 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/header/index.tsx @@ -5,9 +5,8 @@ */ import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; import { isEmpty, get } from 'lodash/fp'; import { History } from '../../../lib/history'; @@ -19,10 +18,9 @@ import { State, timelineSelectors, } from '../../../store'; -import { UpdateNote } from '../../notes/helpers'; import { defaultHeaders } from '../../timeline/body/column_headers/default_headers'; import { Properties } from '../../timeline/properties'; -import { appActions, appModel } from '../../../store/app'; +import { appActions } from '../../../store/app'; import { inputsActions } from '../../../store/inputs'; import { timelineActions } from '../../../store/actions'; import { timelineDefaults, TimelineModel } from '../../../store/timeline/model'; @@ -34,41 +32,7 @@ interface OwnProps { usersViewing: string[]; } -interface StateReduxProps { - description: string; - notesById: appModel.NotesById; - isDataInTimeline: boolean; - isDatepickerLocked: boolean; - isFavorite: boolean; - noteIds: string[]; - title: string; - width: number; -} - -interface DispatchProps { - associateNote: (noteId: string) => void; - applyDeltaToWidth?: ({ - id, - delta, - bodyClientWidthPixels, - maxWidthPercent, - minWidthPixels, - }: { - id: string; - delta: number; - bodyClientWidthPixels: number; - maxWidthPercent: number; - minWidthPixels: number; - }) => void; - createTimeline: ActionCreator<{ id: string; show?: boolean }>; - toggleLock: ActionCreator<{ linkToId: InputsModelId }>; - updateDescription: ActionCreator<{ id: string; description: string }>; - updateIsFavorite: ActionCreator<{ id: string; isFavorite: boolean }>; - updateNote: UpdateNote; - updateTitle: ActionCreator<{ id: string; title: string }>; -} - -type Props = OwnProps & StateReduxProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; const StatefulFlyoutHeader = React.memo( ({ @@ -160,9 +124,7 @@ const makeMapStateToProps = () => { }; const mapDispatchToProps = (dispatch: Dispatch, { timelineId }: OwnProps) => ({ - associateNote: (noteId: string) => { - dispatch(timelineActions.addNote({ id: timelineId, noteId })); - }, + associateNote: (noteId: string) => dispatch(timelineActions.addNote({ id: timelineId, noteId })), applyDeltaToWidth: ({ id, delta, @@ -175,7 +137,7 @@ const mapDispatchToProps = (dispatch: Dispatch, { timelineId }: OwnProps) => ({ bodyClientWidthPixels: number; maxWidthPercent: number; minWidthPixels: number; - }) => { + }) => dispatch( timelineActions.applyDeltaToWidth({ id, @@ -184,35 +146,30 @@ const mapDispatchToProps = (dispatch: Dispatch, { timelineId }: OwnProps) => ({ maxWidthPercent, minWidthPixels, }) - ); - }, - createTimeline: ({ id, show }: { id: string; show?: boolean }) => { + ), + createTimeline: ({ id, show }: { id: string; show?: boolean }) => dispatch( timelineActions.createTimeline({ id, columns: defaultHeaders, show, }) - ); - }, - updateDescription: ({ id, description }: { id: string; description: string }) => { - dispatch(timelineActions.updateDescription({ id, description })); - }, - updateIsFavorite: ({ id, isFavorite }: { id: string; isFavorite: boolean }) => { - dispatch(timelineActions.updateIsFavorite({ id, isFavorite })); - }, - updateIsLive: ({ id, isLive }: { id: string; isLive: boolean }) => { - dispatch(timelineActions.updateIsLive({ id, isLive })); - }, - updateNote: (note: Note) => { - dispatch(appActions.updateNote({ note })); - }, - updateTitle: ({ id, title }: { id: string; title: string }) => { - dispatch(timelineActions.updateTitle({ id, title })); - }, - toggleLock: ({ linkToId }: { linkToId: InputsModelId }) => { - dispatch(inputsActions.toggleTimelineLinkTo({ linkToId })); - }, + ), + updateDescription: ({ id, description }: { id: string; description: string }) => + dispatch(timelineActions.updateDescription({ id, description })), + updateIsFavorite: ({ id, isFavorite }: { id: string; isFavorite: boolean }) => + dispatch(timelineActions.updateIsFavorite({ id, isFavorite })), + updateIsLive: ({ id, isLive }: { id: string; isLive: boolean }) => + dispatch(timelineActions.updateIsLive({ id, isLive })), + updateNote: (note: Note) => dispatch(appActions.updateNote({ note })), + updateTitle: ({ id, title }: { id: string; title: string }) => + dispatch(timelineActions.updateTitle({ id, title })), + toggleLock: ({ linkToId }: { linkToId: InputsModelId }) => + dispatch(inputsActions.toggleTimelineLinkTo({ linkToId })), }); -export const FlyoutHeader = connect(makeMapStateToProps, mapDispatchToProps)(StatefulFlyoutHeader); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const FlyoutHeader = connector(StatefulFlyoutHeader); diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx index 9fb59e2034b08..d18c22e44ce94 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/index.tsx @@ -7,9 +7,8 @@ import { EuiBadge } from '@elastic/eui'; import { defaultTo, getOr } from 'lodash/fp'; import React from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import styled from 'styled-components'; -import { ActionCreator } from 'typescript-fsa'; import { State, timelineSelectors } from '../../store'; import { DataProvider } from '../timeline/data_providers/data_provider'; @@ -46,30 +45,7 @@ interface OwnProps { usersViewing: string[]; } -interface DispatchProps { - showTimeline: ActionCreator<{ id: string; show: boolean }>; - applyDeltaToWidth?: ({ - id, - delta, - bodyClientWidthPixels, - maxWidthPercent, - minWidthPixels, - }: { - id: string; - delta: number; - bodyClientWidthPixels: number; - maxWidthPercent: number; - minWidthPixels: number; - }) => void; -} - -interface StateReduxProps { - dataProviders?: DataProvider[]; - show: boolean; - width: number; -} - -type Props = OwnProps & DispatchProps & StateReduxProps; +type Props = OwnProps & ProsFromRedux; export const FlyoutComponent = React.memo( ({ @@ -110,15 +86,21 @@ FlyoutComponent.displayName = 'FlyoutComponent'; const mapStateToProps = (state: State, { timelineId }: OwnProps) => { const timelineById = defaultTo({}, timelineSelectors.timelineByIdSelector(state)); - const dataProviders = getOr([], `${timelineId}.dataProviders`, timelineById); - const show = getOr('false', `${timelineId}.show`, timelineById); - const width = getOr(DEFAULT_TIMELINE_WIDTH, `${timelineId}.width`, timelineById); + const dataProviders = getOr([], `${timelineId}.dataProviders`, timelineById) as DataProvider[]; + const show = getOr(false, `${timelineId}.show`, timelineById) as boolean; + const width = getOr(DEFAULT_TIMELINE_WIDTH, `${timelineId}.width`, timelineById) as number; return { dataProviders, show, width }; }; -export const Flyout = connect(mapStateToProps, { +const mapDispatchToProps = { showTimeline: timelineActions.showTimeline, -})(FlyoutComponent); +}; + +const connector = connect(mapStateToProps, mapDispatchToProps); + +type ProsFromRedux = ConnectedProps; + +export const Flyout = connector(FlyoutComponent); Flyout.displayName = 'Flyout'; diff --git a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx index 00ac15092a6ec..fb977417ffbbf 100644 --- a/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/flyout/pane/index.tsx @@ -6,9 +6,8 @@ import { EuiButtonIcon, EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiToolTip } from '@elastic/eui'; import React, { useCallback, useState } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import styled from 'styled-components'; -import { ActionCreator } from 'typescript-fsa'; import { Resizable, ResizeCallback } from 're-resizable'; import { throttle } from 'lodash/fp'; @@ -30,17 +29,7 @@ interface OwnProps { width: number; } -interface DispatchProps { - applyDeltaToWidth: ActionCreator<{ - id: string; - delta: number; - bodyClientWidthPixels: number; - maxWidthPercent: number; - minWidthPixels: number; - }>; -} - -type Props = OwnProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; const EuiFlyoutContainer = styled.div<{ headerHeight: number }>` .timeline-flyout { @@ -183,8 +172,14 @@ const FlyoutPaneComponent: React.FC = ({ ); }; -export const Pane = connect(null, { +const mapDispatchToProps = { applyDeltaToWidth: timelineActions.applyDeltaToWidth, -})(React.memo(FlyoutPaneComponent)); +}; + +const connector = connect(null, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const Pane = connector(React.memo(FlyoutPaneComponent)); Pane.displayName = 'Pane'; diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts index 432939b0729d7..61c3d9f73e0d0 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/helpers.ts @@ -6,7 +6,7 @@ import ApolloClient from 'apollo-client'; import { getOr, set } from 'lodash/fp'; -import { ActionCreator } from 'typescript-fsa'; +import { Action } from 'typescript-fsa'; import { Dispatch } from 'redux'; import { oneTimelineQuery } from '../../containers/timeline/one/index.gql_query'; @@ -183,7 +183,13 @@ export interface QueryTimelineById { timelineId: string; onOpenTimeline?: (timeline: TimelineModel) => void; openTimeline?: boolean; - updateIsLoading: ActionCreator<{ id: string; isLoading: boolean }>; + updateIsLoading: ({ + id, + isLoading, + }: { + id: string; + isLoading: boolean; + }) => Action<{ id: string; isLoading: boolean }>; updateTimeline: DispatchUpdateTimeline; } diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx index a97cfefaf0393..daa64f9f16c83 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.tsx @@ -6,7 +6,7 @@ import ApolloClient from 'apollo-client'; import React, { useEffect, useState, useCallback } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers'; @@ -39,8 +39,6 @@ import { OpenTimelineResult, OnToggleShowNotes, OnDeleteOneTimeline, - OpenTimelineDispatchProps, - OpenTimelineReduxProps, } from './types'; import { DEFAULT_SORT_FIELD, DEFAULT_SORT_DIRECTION } from './constants'; @@ -55,8 +53,7 @@ interface OwnProps { export type OpenTimelineOwnProps = OwnProps & Pick & - OpenTimelineDispatchProps & - OpenTimelineReduxProps; + PropsFromRedux; /** Returns a collection of selected timeline ids */ export const getSelectedTimelineIds = (selectedItems: OpenTimelineResult[]): string[] => @@ -346,7 +343,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ updateTimeline: dispatchUpdateTimeline(dispatch), }); -export const StatefulOpenTimeline = connect( - makeMapStateToProps, - mapDispatchToProps -)(StatefulOpenTimelineComponent); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulOpenTimeline = connector(StatefulOpenTimelineComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts b/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts index e5e85ccf0954a..b14bb1cf86d31 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/types.ts @@ -4,11 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ActionCreator } from 'typescript-fsa'; - import { AllTimelinesVariables } from '../../containers/timeline/all'; import { TimelineModel } from '../../store/timeline/model'; -import { ColumnHeader } from '../timeline/body/column_headers/column_header'; import { NoteResult } from '../../graphql/types'; /** The users who added a timeline to favorites */ @@ -163,17 +160,3 @@ export type DispatchUpdateTimeline = ({ timeline, to, }: UpdateTimeline) => () => void; - -export interface OpenTimelineDispatchProps { - updateTimeline: DispatchUpdateTimeline; - createNewTimeline: ActionCreator<{ - id: string; - columns: ColumnHeader[]; - show?: boolean; - }>; - updateIsLoading: ActionCreator<{ id: string; isLoading: boolean }>; -} - -export interface OpenTimelineReduxProps { - timeline: TimelineModel; -} diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx index f5485922647ca..853ba7ae23414 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/authentications_table/index.tsx @@ -8,8 +8,7 @@ import { has } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { hostsActions } from '../../../../store/hosts'; import { AuthenticationsEdges } from '../../../../graphql/types'; @@ -40,24 +39,6 @@ interface OwnProps { type: hostsModel.HostsType; } -interface AuthenticationTableReduxProps { - activePage: number; - limit: number; -} - -interface AuthenticationTableDispatchProps { - updateTableActivePage: ActionCreator<{ - activePage: number; - hostsType: hostsModel.HostsType; - tableType: hostsModel.HostsTableType; - }>; - updateTableLimit: ActionCreator<{ - limit: number; - hostsType: hostsModel.HostsType; - tableType: hostsModel.HostsTableType; - }>; -} - export type AuthTableColumns = [ Columns, Columns, @@ -70,9 +51,7 @@ export type AuthTableColumns = [ Columns ]; -type AuthenticationTableProps = OwnProps & - AuthenticationTableReduxProps & - AuthenticationTableDispatchProps; +type AuthenticationTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -154,10 +133,16 @@ const makeMapStateToProps = () => { }; }; -export const AuthenticationTable = connect(makeMapStateToProps, { +const mapDispatchToProps = { updateTableActivePage: hostsActions.updateTableActivePage, updateTableLimit: hostsActions.updateTableLimit, -})(AuthenticationTableComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const AuthenticationTable = connector(AuthenticationTableComponent); const getAuthenticationColumns = (): AuthTableColumns => [ { diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx index 79bdf5c4b8315..0f56ef53ac081 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/hosts_table/index.tsx @@ -5,8 +5,7 @@ */ import React, { useMemo, useCallback } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { IIndexPattern } from 'src/plugins/data/public'; import { hostsActions } from '../../../../store/actions'; import { @@ -46,30 +45,6 @@ interface OwnProps { type: hostsModel.HostsType; } -interface HostsTableReduxProps { - activePage: number; - direction: Direction; - limit: number; - sortField: HostsFields; -} - -interface HostsTableDispatchProps { - updateHostsSort: ActionCreator<{ - hostsType: hostsModel.HostsType; - sort: HostsSortField; - }>; - updateTableActivePage: ActionCreator<{ - activePage: number; - hostsType: hostsModel.HostsType; - tableType: hostsModel.HostsTableType; - }>; - updateTableLimit: ActionCreator<{ - hostsType: hostsModel.HostsType; - limit: number; - tableType: hostsModel.HostsTableType; - }>; -} - export type HostsTableColumns = [ Columns, Columns, @@ -77,7 +52,7 @@ export type HostsTableColumns = [ Columns ]; -type HostsTableProps = OwnProps & HostsTableReduxProps & HostsTableDispatchProps; +type HostsTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -217,10 +192,16 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const HostsTable = connect(makeMapStateToProps, { +const mapDispatchToProps = { updateHostsSort: hostsActions.updateHostsSort, updateTableActivePage: hostsActions.updateTableActivePage, updateTableLimit: hostsActions.updateTableLimit, -})(HostsTableComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const HostsTable = connector(HostsTableComponent); HostsTable.displayName = 'HostsTable'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx index b4c01053f0e9c..12744ddafa396 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/hosts/uncommon_process_table/index.tsx @@ -7,8 +7,7 @@ /* eslint-disable react/display-name */ import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { hostsActions } from '../../../../store/actions'; import { UncommonProcessesEdges, UncommonProcessItem } from '../../../../graphql/types'; @@ -33,24 +32,6 @@ interface OwnProps { type: hostsModel.HostsType; } -interface UncommonProcessTableReduxProps { - activePage: number; - limit: number; -} - -interface UncommonProcessTableDispatchProps { - updateTableActivePage: ActionCreator<{ - activePage: number; - hostsType: hostsModel.HostsType; - tableType: hostsModel.HostsTableType; - }>; - updateTableLimit: ActionCreator<{ - limit: number; - hostsType: hostsModel.HostsType; - tableType: hostsModel.HostsTableType; - }>; -} - export type UncommonProcessTableColumns = [ Columns, Columns, @@ -60,9 +41,7 @@ export type UncommonProcessTableColumns = [ Columns ]; -type UncommonProcessTableProps = OwnProps & - UncommonProcessTableReduxProps & - UncommonProcessTableDispatchProps; +type UncommonProcessTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -150,10 +129,16 @@ const makeMapStateToProps = () => { return (state: State, { type }: OwnProps) => getUncommonProcessesSelector(state, type); }; -export const UncommonProcessTable = connect(makeMapStateToProps, { +const mapDispatchToProps = { updateTableActivePage: hostsActions.updateTableActivePage, updateTableLimit: hostsActions.updateTableLimit, -})(UncommonProcessTableComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const UncommonProcessTable = connector(UncommonProcessTableComponent); UncommonProcessTable.displayName = 'UncommonProcessTable'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx index e316f951a0363..f3fe98936a55d 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_dns_table/index.tsx @@ -6,8 +6,7 @@ import { isEqual } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { networkActions } from '../../../../store/actions'; import { @@ -37,22 +36,7 @@ interface OwnProps { type: networkModel.NetworkType; } -interface NetworkDnsTableReduxProps { - activePage: number; - limit: number; - sort: NetworkDnsSortField; - isPtrIncluded: boolean; -} - -interface NetworkDnsTableDispatchProps { - updateNetworkTable: ActionCreator<{ - networkType: networkModel.NetworkType; - tableType: networkModel.AllNetworkTables; - updates: networkModel.TableUpdates; - }>; -} - -type NetworkDnsTableProps = OwnProps & NetworkDnsTableReduxProps & NetworkDnsTableDispatchProps; +type NetworkDnsTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -172,6 +156,12 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const NetworkDnsTable = connect(makeMapStateToProps, { +const mapDispatchToProps = { updateNetworkTable: networkActions.updateNetworkTable, -})(NetworkDnsTableComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const NetworkDnsTable = connector(NetworkDnsTableComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap index dfc1b2cf64e38..875a490d86be3 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/__snapshots__/index.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`NetworkHttp Table Component rendering it renders the default NetworkHttp table 1`] = ` - { ); - expect(wrapper.find('Connect(NetworkHttpTableComponent)')).toMatchSnapshot(); + expect(wrapper.find('Connect(Component)')).toMatchSnapshot(); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx index 58adc971b0101..87038eb6aaeeb 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_http_table/index.tsx @@ -5,17 +5,10 @@ */ import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { networkActions } from '../../../../store/actions'; -import { - Direction, - NetworkHttpEdges, - NetworkHttpFields, - NetworkHttpSortField, -} from '../../../../graphql/types'; +import { Direction, NetworkHttpEdges, NetworkHttpFields } from '../../../../graphql/types'; import { networkModel, networkSelectors, State } from '../../../../store'; import { Criteria, ItemsPerRow, PaginatedTable } from '../../../paginated_table'; @@ -34,21 +27,7 @@ interface OwnProps { type: networkModel.NetworkType; } -interface NetworkHttpTableReduxProps { - activePage: number; - limit: number; - sort: NetworkHttpSortField; -} - -interface NetworkHttpTableDispatchProps { - updateNetworkTable: ActionCreator<{ - networkType: networkModel.NetworkType; - tableType: networkModel.AllNetworkTables; - updates: networkModel.TableUpdates; - }>; -} - -type NetworkHttpTableProps = OwnProps & NetworkHttpTableReduxProps & NetworkHttpTableDispatchProps; +type NetworkHttpTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -61,91 +40,89 @@ const rowItems: ItemsPerRow[] = [ }, ]; -const NetworkHttpTableComponent = React.memo( - ({ - activePage, - data, - fakeTotalCount, - id, - isInspect, - limit, - loading, - loadPage, - showMorePagesIndicator, - sort, - totalCount, - type, - updateNetworkTable, - }) => { - const tableType = - type === networkModel.NetworkType.page - ? networkModel.NetworkTableType.http - : networkModel.IpDetailsTableType.http; - - const updateLimitPagination = useCallback( - newLimit => +const NetworkHttpTableComponent: React.FC = ({ + activePage, + data, + fakeTotalCount, + id, + isInspect, + limit, + loading, + loadPage, + showMorePagesIndicator, + sort, + totalCount, + type, + updateNetworkTable, +}) => { + const tableType = + type === networkModel.NetworkType.page + ? networkModel.NetworkTableType.http + : networkModel.IpDetailsTableType.http; + + const updateLimitPagination = useCallback( + newLimit => + updateNetworkTable({ + networkType: type, + tableType, + updates: { limit: newLimit }, + }), + [type, updateNetworkTable, tableType] + ); + + const updateActivePage = useCallback( + newPage => + updateNetworkTable({ + networkType: type, + tableType, + updates: { activePage: newPage }, + }), + [type, updateNetworkTable, tableType] + ); + + const onChange = useCallback( + (criteria: Criteria) => { + if (criteria.sort != null && criteria.sort.direction !== sort.direction) { updateNetworkTable({ networkType: type, tableType, - updates: { limit: newLimit }, - }), - [type, updateNetworkTable, tableType] - ); - - const updateActivePage = useCallback( - newPage => - updateNetworkTable({ - networkType: type, - tableType, - updates: { activePage: newPage }, - }), - [type, updateNetworkTable, tableType] - ); - - const onChange = useCallback( - (criteria: Criteria) => { - if (criteria.sort != null && criteria.sort.direction !== sort.direction) { - updateNetworkTable({ - networkType: type, - tableType, - updates: { - sort: { - direction: criteria.sort.direction as Direction, - }, + updates: { + sort: { + direction: criteria.sort.direction as Direction, }, - }); - } - }, - [tableType, sort.direction, type, updateNetworkTable] - ); - - const sorting = { field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }; - - return ( - - ); - } -); + }, + }); + } + }, + [tableType, sort.direction, type, updateNetworkTable] + ); + + const sorting = { field: `node.${NetworkHttpFields.requestCount}`, direction: sort.direction }; + + return ( + + ); +}; NetworkHttpTableComponent.displayName = 'NetworkHttpTableComponent'; @@ -155,8 +132,12 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const NetworkHttpTable = compose>( - connect(makeMapStateToProps, { - updateNetworkTable: networkActions.updateNetworkTable, - }) -)(NetworkHttpTableComponent); +const mapDispatchToProps = { + updateNetworkTable: networkActions.updateNetworkTable, +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const NetworkHttpTable = connector(React.memo(NetworkHttpTableComponent)); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx index 6d14b52d3586d..37355a37fa6d7 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_countries_table/index.tsx @@ -6,9 +6,7 @@ import { isEqual, last } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { IIndexPattern } from 'src/plugins/data/public'; import { networkActions } from '../../../../store/actions'; @@ -39,23 +37,7 @@ interface OwnProps { type: networkModel.NetworkType; } -interface NetworkTopCountriesTableReduxProps { - activePage: number; - limit: number; - sort: NetworkTopTablesSortField; -} - -interface NetworkTopCountriesTableDispatchProps { - updateNetworkTable: ActionCreator<{ - networkType: networkModel.NetworkType; - tableType: networkModel.AllNetworkTables; - updates: networkModel.TableUpdates; - }>; -} - -type NetworkTopCountriesTableProps = OwnProps & - NetworkTopCountriesTableReduxProps & - NetworkTopCountriesTableDispatchProps; +type NetworkTopCountriesTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -197,8 +179,12 @@ const makeMapStateToProps = () => { getTopCountriesSelector(state, type, flowTargeted); }; -export const NetworkTopCountriesTable = compose>( - connect(makeMapStateToProps, { - updateNetworkTable: networkActions.updateNetworkTable, - }) -)(NetworkTopCountriesTableComponent); +const mapDispatchToProps = { + updateNetworkTable: networkActions.updateNetworkTable, +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const NetworkTopCountriesTable = connector(NetworkTopCountriesTableComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx index 7c1fcb2681a8c..c4f6dad0a47ed 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/network_top_n_flow_table/index.tsx @@ -5,9 +5,7 @@ */ import { isEqual, last } from 'lodash/fp'; import React, { useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { networkActions } from '../../../../store/actions'; import { @@ -36,23 +34,7 @@ interface OwnProps { type: networkModel.NetworkType; } -interface NetworkTopNFlowTableReduxProps { - activePage: number; - limit: number; - sort: NetworkTopTablesSortField; -} - -interface NetworkTopNFlowTableDispatchProps { - updateNetworkTable: ActionCreator<{ - networkType: networkModel.NetworkType; - tableType: networkModel.TopNTableType; - updates: networkModel.TableUpdates; - }>; -} - -type NetworkTopNFlowTableProps = OwnProps & - NetworkTopNFlowTableReduxProps & - NetworkTopNFlowTableDispatchProps; +type NetworkTopNFlowTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -180,8 +162,12 @@ const makeMapStateToProps = () => { getTopNFlowSelector(state, type, flowTargeted); }; -export const NetworkTopNFlowTable = compose>( - connect(makeMapStateToProps, { - updateNetworkTable: networkActions.updateNetworkTable, - }) -)(React.memo(NetworkTopNFlowTableComponent)); +const mapDispatchToProps = { + updateNetworkTable: networkActions.updateNetworkTable, +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const NetworkTopNFlowTable = connector(React.memo(NetworkTopNFlowTableComponent)); diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx index 95c0fff054440..77abae68b76bf 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/tls_table/index.tsx @@ -6,9 +6,7 @@ import { isEqual } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { networkActions } from '../../../../store/network'; import { TlsEdges, TlsSortField, TlsFields, Direction } from '../../../../graphql/types'; @@ -29,21 +27,7 @@ interface OwnProps { type: networkModel.NetworkType; } -interface TlsTableReduxProps { - activePage: number; - limit: number; - sort: TlsSortField; -} - -interface TlsTableDispatchProps { - updateNetworkTable: ActionCreator<{ - networkType: networkModel.NetworkType; - tableType: networkModel.AllNetworkTables; - updates: networkModel.TableUpdates; - }>; -} - -type TlsTableProps = OwnProps & TlsTableReduxProps & TlsTableDispatchProps; +type TlsTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -152,11 +136,15 @@ const makeMapStateToProps = () => { return (state: State, { type }: OwnProps) => getTlsSelector(state, type); }; -export const TlsTable = compose>( - connect(makeMapStateToProps, { - updateNetworkTable: networkActions.updateNetworkTable, - }) -)(TlsTableComponent); +const mapDispatchToProps = { + updateNetworkTable: networkActions.updateNetworkTable, +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const TlsTable = connector(TlsTableComponent); const getSortField = (sortField: TlsSortField): SortingBasicTable => ({ field: `node.${sortField.field}`, diff --git a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx index f4f14c7c009dc..bc7ef2314add4 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/network/users_table/index.tsx @@ -6,8 +6,7 @@ import { isEqual } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { networkActions } from '../../../../store/network'; import { @@ -38,21 +37,7 @@ interface OwnProps { type: networkModel.NetworkType; } -interface UsersTableReduxProps { - activePage: number; - limit: number; - sort: UsersSortField; -} - -interface UsersTableDispatchProps { - updateNetworkTable: ActionCreator<{ - networkType: networkModel.NetworkType; - tableType: networkModel.AllNetworkTables; - updates: networkModel.TableUpdates; - }>; -} - -type UsersTableProps = OwnProps & UsersTableReduxProps & UsersTableDispatchProps; +type UsersTableProps = OwnProps & PropsFromRedux; const rowItems: ItemsPerRow[] = [ { @@ -159,9 +144,15 @@ const makeMapStateToProps = () => { }); }; -export const UsersTable = connect(makeMapStateToProps, { +const mapDispatchToProps = { updateNetworkTable: networkActions.updateNetworkTable, -})(UsersTableComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const UsersTable = connector(UsersTableComponent); const getSortField = (sortField: UsersSortField): SortingBasicTable => { switch (sortField.field) { diff --git a/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx b/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx index d5157e81b0fc8..41eb137742963 100644 --- a/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/recent_timelines/index.tsx @@ -7,14 +7,14 @@ import ApolloClient from 'apollo-client'; import { EuiHorizontalRule, EuiLink, EuiText } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; import { ActionCreator } from 'typescript-fsa'; import { AllTimelinesQuery } from '../../containers/timeline/all'; import { SortFieldTimeline, Direction } from '../../graphql/types'; import { queryTimelineById, dispatchUpdateTimeline } from '../open_timeline/helpers'; -import { DispatchUpdateTimeline, OnOpenTimeline } from '../open_timeline/types'; +import { OnOpenTimeline } from '../open_timeline/types'; import { LoadingPlaceholders } from '../page/overview/loading_placeholders'; import { updateIsLoading as dispatchUpdateIsLoading } from '../../store/timeline/actions'; @@ -31,12 +31,7 @@ interface OwnProps { filterBy: FilterMode; } -interface DispatchProps { - updateIsLoading: ({ id, isLoading }: { id: string; isLoading: boolean }) => void; - updateTimeline: DispatchUpdateTimeline; -} - -export type Props = OwnProps & DispatchProps; +export type Props = OwnProps & PropsFromRedux; const StatefulRecentTimelinesComponent = React.memo( ({ apolloClient, filterBy, updateIsLoading, updateTimeline }) => { @@ -100,7 +95,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ updateTimeline: dispatchUpdateTimeline(dispatch), }); -export const StatefulRecentTimelines = connect( - null, - mapDispatchToProps -)(StatefulRecentTimelinesComponent); +const connector = connect(null, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulRecentTimelines = connector(StatefulRecentTimelinesComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx index 2c3f677cc585d..cb5729ad8e26e 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -6,7 +6,7 @@ import { getOr, isEqual, set } from 'lodash/fp'; import React, { memo, useEffect, useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; import { Subscription } from 'rxjs'; import styled from 'styled-components'; @@ -34,29 +34,6 @@ import { import { timelineActions, hostsActions, networkActions } from '../../store/actions'; import { useKibana } from '../../lib/kibana'; -interface SiemSearchBarRedux { - end: number; - fromStr: string; - isLoading: boolean; - queries: inputsModel.GlobalGraphqlQuery[]; - filterQuery: Query; - savedQuery?: SavedQuery; - start: number; - toStr: string; -} - -interface SiemSearchBarDispatch { - updateSearch: DispatchUpdateSearch; - setSavedQuery: ({ - id, - savedQuery, - }: { - id: InputsModelId; - savedQuery: SavedQuery | undefined; - }) => void; - setSearchBarFilter: ({ id, filters }: { id: InputsModelId; filters: Filter[] }) => void; -} - interface SiemSearchBarProps { id: InputsModelId; indexPattern: IIndexPattern; @@ -70,7 +47,7 @@ const SearchBarContainer = styled.div` } `; -const SearchBarComponent = memo( +const SearchBarComponent = memo( ({ end, filterQuery, @@ -322,15 +299,7 @@ interface UpdateReduxSearchBar extends OnTimeChangeProps { updateTime: boolean; } -type DispatchUpdateSearch = ({ - end, - id, - isQuickSelection, - start, - timelineId, -}: UpdateReduxSearchBar) => void; - -const dispatchUpdateSearch = (dispatch: Dispatch) => ({ +export const dispatchUpdateSearch = (dispatch: Dispatch) => ({ end, filters, id, @@ -403,4 +372,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ dispatch(inputsActions.setSearchBarFilter({ id, filters })), }); -export const SiemSearchBar = connect(makeMapStateToProps, mapDispatchToProps)(SearchBarComponent); +export const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const SiemSearchBar = connector(SearchBarComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx index 0877906c721ce..ad38a7d61bcba 100644 --- a/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/super_date_picker/index.tsx @@ -14,7 +14,7 @@ import { } from '@elastic/eui'; import { getOr, take, isEmpty } from 'lodash/fp'; import React, { useState, useCallback } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; import { DEFAULT_TIMEPICKER_QUICK_RANGES } from '../../../common/constants'; @@ -34,7 +34,7 @@ import { queriesSelector, kqlQuerySelector, } from './selectors'; -import { InputsRange, Policy } from '../../store/inputs/model'; +import { InputsRange } from '../../store/inputs/model'; const MAX_RECENTLY_USED_RANGES = 9; @@ -44,19 +44,6 @@ interface Range { display: string; } -interface SuperDatePickerStateRedux { - duration: number; - end: number; - fromStr: string; - isLoading: boolean; - kind: string; - kqlQuery: inputsModel.GlobalKqlQuery; - policy: Policy['kind']; - queries: inputsModel.GlobalGraphqlQuery[]; - start: number; - toStr: string; -} - interface UpdateReduxTime extends OnTimeChangeProps { id: InputsModelId; kql?: inputsModel.GlobalKqlQuery | undefined; @@ -76,22 +63,13 @@ export type DispatchUpdateReduxTime = ({ timelineId, }: UpdateReduxTime) => ReturnUpdateReduxTime; -interface SuperDatePickerDispatchProps { - setDuration: ({ id, duration }: { id: InputsModelId; duration: number }) => void; - startAutoReload: ({ id }: { id: InputsModelId }) => void; - stopAutoReload: ({ id }: { id: InputsModelId }) => void; - updateReduxTime: DispatchUpdateReduxTime; -} - interface OwnProps { disabled?: boolean; id: InputsModelId; timelineId?: string; } -export type SuperDatePickerProps = OwnProps & - SuperDatePickerDispatchProps & - SuperDatePickerStateRedux; +export type SuperDatePickerProps = OwnProps & PropsFromRedux; export const SuperDatePickerComponent = React.memo( ({ @@ -308,9 +286,9 @@ export const makeMapStateToProps = () => { fromStr: getFromStrSelector(inputsRange), isLoading: getIsLoadingSelector(inputsRange), kind: getKindSelector(inputsRange), - kqlQuery: getKqlQuerySelector(inputsRange), + kqlQuery: getKqlQuerySelector(inputsRange) as inputsModel.GlobalKqlQuery, policy: getPolicySelector(inputsRange), - queries: getQueriesSelector(inputsRange), + queries: getQueriesSelector(inputsRange) as inputsModel.GlobalGraphqlQuery[], start: getStartSelector(inputsRange), toStr: getToStrSelector(inputsRange), }; @@ -328,7 +306,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ updateReduxTime: dispatchUpdateReduxTime(dispatch), }); -export const SuperDatePicker = connect( - makeMapStateToProps, - mapDispatchToProps -)(SuperDatePickerComponent); +export const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const SuperDatePicker = connector(SuperDatePickerComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/auto_save_warning/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/auto_save_warning/index.tsx index c2dfda6a81ce4..90d0738aba72f 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/auto_save_warning/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/auto_save_warning/index.tsx @@ -12,41 +12,17 @@ import { } from '@elastic/eui'; import { getOr } from 'lodash/fp'; import React from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { State, timelineSelectors } from '../../../store'; import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../../store/inputs/actions'; -import { TimelineModel } from '../../../store/timeline/model'; import * as i18n from './translations'; import { timelineActions } from '../../../store/timeline'; import { AutoSavedWarningMsg } from '../../../store/timeline/types'; import { useStateToaster } from '../../toasters'; -interface ReduxProps { - timelineId: string | null; - newTimelineModel: TimelineModel | null; -} - -interface DispatchProps { - setTimelineRangeDatePicker: ActionCreator<{ - from: number; - to: number; - }>; - updateAutoSaveMsg: ActionCreator<{ - timelineId: string | null; - newTimelineModel: TimelineModel | null; - }>; - updateTimeline: ActionCreator<{ - id: string; - timeline: TimelineModel; - }>; -} - -type OwnProps = ReduxProps & DispatchProps; - -const AutoSaveWarningMsgComponent = React.memo( +const AutoSaveWarningMsgComponent = React.memo( ({ newTimelineModel, setTimelineRangeDatePicker, @@ -106,8 +82,14 @@ const mapStateToProps = (state: State) => { }; }; -export const AutoSaveWarningMsg = connect(mapStateToProps, { +const mapDispatchToProps = { setTimelineRangeDatePicker: dispatchSetTimelineRangeDatePicker, updateAutoSaveMsg: timelineActions.updateAutoSaveMsg, updateTimeline: timelineActions.updateTimeline, -})(AutoSaveWarningMsgComponent); +}; + +const connector = connect(mapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const AutoSaveWarningMsg = connector(AutoSaveWarningMsgComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.test.tsx index 239d8a9d77916..cf35c8e565bbc 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.test.tsx @@ -66,7 +66,6 @@ describe('Body', () => { onUnPinEvent={jest.fn()} onUpdateColumns={jest.fn()} pinnedEventIds={{}} - range={'1 Day'} rowRenderers={rowRenderers} selectedEventIds={{}} sort={mockSort} @@ -110,7 +109,6 @@ describe('Body', () => { onUnPinEvent={jest.fn()} onUpdateColumns={jest.fn()} pinnedEventIds={{}} - range={'1 Day'} rowRenderers={rowRenderers} selectedEventIds={{}} sort={mockSort} @@ -154,7 +152,6 @@ describe('Body', () => { onUnPinEvent={jest.fn()} onUpdateColumns={jest.fn()} pinnedEventIds={{}} - range={'1 Day'} rowRenderers={rowRenderers} selectedEventIds={{}} sort={mockSort} @@ -200,7 +197,6 @@ describe('Body', () => { onUnPinEvent={jest.fn()} onUpdateColumns={jest.fn()} pinnedEventIds={{}} - range={'1 Day'} rowRenderers={rowRenderers} selectedEventIds={{}} sort={mockSort} @@ -293,7 +289,6 @@ describe('Body', () => { onUnPinEvent={jest.fn()} onUpdateColumns={jest.fn()} pinnedEventIds={{}} - range={'1 Day'} rowRenderers={rowRenderers} selectedEventIds={{}} sort={mockSort} @@ -339,7 +334,6 @@ describe('Body', () => { onUnPinEvent={jest.fn()} onUpdateColumns={jest.fn()} pinnedEventIds={{}} - range={'1 Day'} rowRenderers={rowRenderers} selectedEventIds={{}} sort={mockSort} diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx index f00da48266927..7f689a877c6b7 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/index.tsx @@ -54,7 +54,6 @@ export interface BodyProps { onUpdateColumns: OnUpdateColumns; onUnPinEvent: OnUnPinEvent; pinnedEventIds: Readonly>; - range: string; rowRenderers: RowRenderer[]; selectedEventIds: Readonly>; showCheckboxes: boolean; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx index edf0613ac2693..ffb5f2a206f4d 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/body/stateful_body.tsx @@ -7,13 +7,12 @@ import { noop } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import React, { useCallback, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { BrowserFields } from '../../../containers/source'; -import { TimelineItem, TimelineNonEcsData } from '../../../graphql/types'; +import { TimelineItem } from '../../../graphql/types'; import { Note } from '../../../lib/note'; -import { appModel, appSelectors, State, timelineSelectors } from '../../../store'; +import { appSelectors, State, timelineSelectors } from '../../../store'; import { AddNoteToEvent, UpdateNote } from '../../notes/helpers'; import { OnColumnRemoved, @@ -46,59 +45,7 @@ interface OwnProps { toggleColumn: (column: ColumnHeader) => void; } -interface ReduxProps { - columnHeaders: ColumnHeader[]; - eventIdToNoteIds: Readonly>; - isSelectAllChecked: boolean; - loadingEventIds: Readonly; - notesById: appModel.NotesById; - pinnedEventIds: Readonly>; - range?: string; - selectedEventIds: Readonly>; - showCheckboxes: boolean; - showRowRenderers: boolean; -} - -interface DispatchProps { - addNoteToEvent?: ActionCreator<{ id: string; noteId: string; eventId: string }>; - applyDeltaToColumnWidth?: ActionCreator<{ - id: string; - columnId: string; - delta: number; - }>; - clearSelected?: ActionCreator<{ - id: string; - }>; - pinEvent?: ActionCreator<{ - id: string; - eventId: string; - }>; - removeColumn?: ActionCreator<{ - id: string; - columnId: string; - }>; - setSelected?: ActionCreator<{ - id: string; - eventIds: Record; - isSelected: boolean; - isSelectAllChecked: boolean; - }>; - unPinEvent?: ActionCreator<{ - id: string; - eventId: string; - }>; - updateColumns?: ActionCreator<{ - id: string; - columns: ColumnHeader[]; - }>; - updateSort?: ActionCreator<{ - id: string; - sort: Sort; - }>; - updateNote?: ActionCreator<{ note: Note }>; -} - -type StatefulBodyComponentProps = OwnProps & ReduxProps & DispatchProps; +type StatefulBodyComponentProps = OwnProps & PropsFromRedux; export const emptyColumnHeaders: ColumnHeader[] = []; @@ -118,7 +65,6 @@ const StatefulBodyComponent = React.memo( notesById, pinEvent, pinnedEventIds, - range, removeColumn, selectedEventIds, setSelected, @@ -234,7 +180,6 @@ const StatefulBodyComponent = React.memo( onUnPinEvent={onUnPinEvent} onUpdateColumns={onUpdateColumns} pinnedEventIds={pinnedEventIds} - range={range!} rowRenderers={showRowRenderers ? rowRenderers : [plainRowRenderer]} selectedEventIds={selectedEventIds} showCheckboxes={showCheckboxes} @@ -260,7 +205,6 @@ const StatefulBodyComponent = React.memo( prevProps.selectedEventIds === nextProps.selectedEventIds && prevProps.showCheckboxes === nextProps.showCheckboxes && prevProps.showRowRenderers === nextProps.showRowRenderers && - prevProps.range === nextProps.range && prevProps.sort === nextProps.sort ); } @@ -307,7 +251,7 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const StatefulBody = connect(makeMapStateToProps, { +const mapDispatchToProps = { addNoteToEvent: timelineActions.addNoteToEvent, applyDeltaToColumnWidth: timelineActions.applyDeltaToColumnWidth, clearSelected: timelineActions.clearSelected, @@ -319,4 +263,10 @@ export const StatefulBody = connect(makeMapStateToProps, { updateColumns: timelineActions.updateColumns, updateNote: appActions.updateNote, updateSort: timelineActions.updateSort, -})(StatefulBodyComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulBody = connector(StatefulBodyComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx index 4b1e723f1bb3a..65c539d77a16b 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/fetch_kql_timeline.tsx @@ -5,37 +5,21 @@ */ import { memo, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { IIndexPattern } from 'src/plugins/data/public'; -import { inputsModel, KueryFilterQuery, timelineSelectors, State } from '../../store'; +import { timelineSelectors, State } from '../../store'; import { inputsActions } from '../../store/actions'; import { InputsModelId } from '../../store/inputs/constants'; import { useUpdateKql } from '../../utils/kql/use_update_kql'; -interface TimelineKqlFetchRedux { - kueryFilterQuery: KueryFilterQuery | null; - kueryFilterQueryDraft: KueryFilterQuery | null; -} - -interface TimelineKqlFetchDispatch { - setTimelineQuery: ActionCreator<{ - id: string; - inputId: InputsModelId; - inspect: inputsModel.InspectQuery | null; - loading: boolean; - refetch: inputsModel.Refetch | inputsModel.RefetchKql | null; - }>; -} - export interface TimelineKqlFetchProps { id: string; indexPattern: IIndexPattern; inputId: InputsModelId; } -type OwnProps = TimelineKqlFetchProps & TimelineKqlFetchRedux & TimelineKqlFetchDispatch; +type OwnProps = TimelineKqlFetchProps & PropsFromRedux; const TimelineKqlFetchComponent = memo( ({ id, indexPattern, inputId, kueryFilterQuery, kueryFilterQueryDraft, setTimelineQuery }) => { @@ -70,6 +54,12 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const TimelineKqlFetch = connect(makeMapStateToProps, { +const mapDispatchToProps = { setTimelineQuery: inputsActions.setQuery, -})(TimelineKqlFetchComponent); +}; + +export const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const TimelineKqlFetch = connector(TimelineKqlFetchComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx index b6a57ebacb11c..34f760e411ed4 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/index.tsx @@ -6,21 +6,16 @@ import { isEqual } from 'lodash/fp'; import React, { useEffect, useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; - -import { Filter } from '../../../../../../../src/plugins/data/public'; +import { connect, ConnectedProps } from 'react-redux'; import { WithSource } from '../../containers/source'; import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store'; import { timelineActions } from '../../store/actions'; -import { EventType, KqlMode, timelineDefaults, TimelineModel } from '../../store/timeline/model'; +import { timelineDefaults, TimelineModel } from '../../store/timeline/model'; import { useSignalIndex } from '../../containers/detection_engine/signals/use_signal_index'; import { ColumnHeader } from './body/column_headers/column_header'; -import { DataProvider, QueryOperator } from './data_providers/data_provider'; import { defaultHeaders } from './body/column_headers/default_headers'; -import { Sort } from './body/sort'; import { OnChangeDataProviderKqlQuery, OnChangeDroppableAndProvider, @@ -38,103 +33,7 @@ export interface OwnProps { flyoutHeight: number; } -interface StateReduxProps { - activePage?: number; - columns: ColumnHeader[]; - dataProviders?: DataProvider[]; - eventType: EventType; - end: number; - filters: Filter[]; - isLive: boolean; - itemsPerPage?: number; - itemsPerPageOptions?: number[]; - kqlMode: KqlMode; - kqlQueryExpression: string; - pageCount?: number; - sort?: Sort; - start: number; - show?: boolean; - showCallOutUnauthorizedMsg: boolean; -} - -interface DispatchProps { - createTimeline?: ActionCreator<{ - id: string; - columns: ColumnHeader[]; - show?: boolean; - }>; - addProvider?: ActionCreator<{ - id: string; - provider: DataProvider; - }>; - onDataProviderEdited?: ActionCreator<{ - andProviderId?: string; - excluded: boolean; - field: string; - id: string; - operator: QueryOperator; - providerId: string; - value: string | number; - }>; - updateColumns?: ActionCreator<{ - id: string; - category: string; - columns: ColumnHeader[]; - }>; - updateProviders?: ActionCreator<{ - id: string; - providers: DataProvider[]; - }>; - removeColumn?: ActionCreator<{ - id: string; - columnId: string; - }>; - removeProvider?: ActionCreator<{ - id: string; - providerId: string; - andProviderId?: string; - }>; - updateDataProviderEnabled?: ActionCreator<{ - id: string; - providerId: string; - enabled: boolean; - andProviderId?: string; - }>; - updateDataProviderExcluded?: ActionCreator<{ - id: string; - excluded: boolean; - providerId: string; - andProviderId?: string; - }>; - updateDataProviderKqlQuery?: ActionCreator<{ - id: string; - kqlQuery: string; - providerId: string; - }>; - updateItemsPerPage?: ActionCreator<{ - id: string; - itemsPerPage: number; - }>; - updateItemsPerPageOptions?: ActionCreator<{ - id: string; - itemsPerPageOptions: number[]; - }>; - updatePageIndex?: ActionCreator<{ - id: string; - activePage: number; - }>; - updateHighlightedDropAndProviderId?: ActionCreator<{ - id: string; - providerId: string; - }>; - upsertColumn?: ActionCreator<{ - column: ColumnHeader; - id: string; - index: number; - }>; -} - -type Props = OwnProps & StateReduxProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; const StatefulTimelineComponent = React.memo( ({ @@ -169,7 +68,12 @@ const StatefulTimelineComponent = React.memo( const { loading, signalIndexExists, signalIndexName } = useSignalIndex(); const indexToAdd = useMemo(() => { - if (signalIndexExists && signalIndexName != null && ['signal', 'all'].includes(eventType)) { + if ( + eventType && + signalIndexExists && + signalIndexName != null && + ['signal', 'all'].includes(eventType) + ) { return [signalIndexName]; } return []; @@ -300,7 +204,6 @@ const StatefulTimelineComponent = React.memo( }, (prevProps, nextProps) => { return ( - prevProps.activePage === nextProps.activePage && prevProps.eventType === nextProps.eventType && prevProps.end === nextProps.end && prevProps.flyoutHeaderHeight === nextProps.flyoutHeaderHeight && @@ -310,7 +213,6 @@ const StatefulTimelineComponent = React.memo( prevProps.itemsPerPage === nextProps.itemsPerPage && prevProps.kqlMode === nextProps.kqlMode && prevProps.kqlQueryExpression === nextProps.kqlQueryExpression && - prevProps.pageCount === nextProps.pageCount && prevProps.show === nextProps.show && prevProps.showCallOutUnauthorizedMsg === nextProps.showCallOutUnauthorizedMsg && prevProps.start === nextProps.start && @@ -344,7 +246,7 @@ const makeMapStateToProps = () => { show, sort, } = timeline; - const kqlQueryExpression = getKqlQueryTimeline(state, id); + const kqlQueryExpression = getKqlQueryTimeline(state, id)!; const timelineFilter = kqlMode === 'filter' ? filters || [] : []; @@ -369,7 +271,7 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const StatefulTimeline = connect(makeMapStateToProps, { +const mapDispatchToProps = { addProvider: timelineActions.addProvider, createTimeline: timelineActions.createTimeline, onDataProviderEdited: timelineActions.dataProviderEdited, @@ -384,4 +286,10 @@ export const StatefulTimeline = connect(makeMapStateToProps, { updateItemsPerPageOptions: timelineActions.updateItemsPerPageOptions, updateSort: timelineActions.updateSort, upsertColumn: timelineActions.upsertColumn, -})(StatefulTimelineComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulTimeline = connector(StatefulTimelineComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx index c804ccf658296..3d2ec0683f091 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/refetch_timeline.tsx @@ -5,33 +5,21 @@ */ import React, { useEffect } from 'react'; -import { connect } from 'react-redux'; -import { compose } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { inputsModel } from '../../store'; import { inputsActions } from '../../store/actions'; import { InputsModelId } from '../../store/inputs/constants'; -interface TimelineRefetchDispatch { - setTimelineQuery: ActionCreator<{ - id: string; - inputId: InputsModelId; - inspect: inputsModel.InspectQuery | null; - loading: boolean; - refetch: inputsModel.Refetch | inputsModel.RefetchKql | null; - }>; -} - export interface TimelineRefetchProps { id: string; inputId: InputsModelId; inspect: inputsModel.InspectQuery | null; loading: boolean; - refetch: inputsModel.Refetch | null; + refetch: inputsModel.Refetch; } -type OwnProps = TimelineRefetchProps & TimelineRefetchDispatch; +type OwnProps = TimelineRefetchProps & PropsFromRedux; const TimelineRefetchComponent: React.FC = ({ id, @@ -48,8 +36,12 @@ const TimelineRefetchComponent: React.FC = ({ return null; }; -export const TimelineRefetch = compose>( - connect(null, { - setTimelineQuery: inputsActions.setQuery, - }) -)(React.memo(TimelineRefetchComponent)); +const mapDispatchToProps = { + setTimelineQuery: inputsActions.setQuery, +}; + +const connector = connect(null, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const TimelineRefetch = connector(React.memo(TimelineRefetchComponent)); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx index 3c47823fbbc3b..2b139d3948fe1 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/search_or_filter/index.tsx @@ -6,7 +6,7 @@ import { getOr, isEqual } from 'lodash/fp'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; import { Filter, IIndexPattern } from '../../../../../../../../src/plugins/data/public'; @@ -22,8 +22,7 @@ import { } from '../../../store'; import { timelineActions } from '../../../store/actions'; import { KqlMode, timelineDefaults, TimelineModel, EventType } from '../../../store/timeline/model'; -import { DispatchUpdateReduxTime, dispatchUpdateReduxTime } from '../../super_date_picker'; -import { DataProvider } from '../data_providers/data_provider'; +import { dispatchUpdateReduxTime } from '../../super_date_picker'; import { SearchOrFilter } from './search_or_filter'; interface OwnProps { @@ -32,45 +31,7 @@ interface OwnProps { timelineId: string; } -interface StateReduxProps { - dataProviders: DataProvider[]; - eventType: EventType; - filters: Filter[]; - filterQuery: KueryFilterQuery; - filterQueryDraft: KueryFilterQuery; - from: number; - fromStr: string; - isRefreshPaused: boolean; - kqlMode: KqlMode; - refreshInterval: number; - savedQueryId: string | null; - to: number; - toStr: string; -} - -interface DispatchProps { - applyKqlFilterQuery: ({ - id, - filterQuery, - }: { - id: string; - filterQuery: SerializedFilterQuery; - }) => void; - updateEventType: ({ id, eventType }: { id: string; eventType: EventType }) => void; - updateKqlMode: ({ id, kqlMode }: { id: string; kqlMode: KqlMode }) => void; - setKqlFilterQueryDraft: ({ - id, - filterQueryDraft, - }: { - id: string; - filterQueryDraft: KueryFilterQuery; - }) => void; - setSavedQueryId: ({ id, savedQueryId }: { id: string; savedQueryId: string | null }) => void; - setFilters: ({ id, filters }: { id: string; filters: Filter[] }) => void; - updateReduxTime: DispatchUpdateReduxTime; -} - -type Props = OwnProps & StateReduxProps & DispatchProps; +type Props = OwnProps & PropsFromRedux; const StatefulSearchOrFilterComponent = React.memo( ({ @@ -217,17 +178,17 @@ const makeMapStateToProps = () => { return { dataProviders: timeline.dataProviders, eventType: timeline.eventType ?? 'raw', - filterQuery: getKqlFilterQuery(state, timelineId), - filterQueryDraft: getKqlFilterQueryDraft(state, timelineId), - filters: timeline.filters, + filterQuery: getKqlFilterQuery(state, timelineId)!, + filterQueryDraft: getKqlFilterQueryDraft(state, timelineId)!, + filters: timeline.filters!, from: input.timerange.from, - fromStr: input.timerange.fromStr, + fromStr: input.timerange.fromStr!, isRefreshPaused: policy.kind === 'manual', kqlMode: getOr('filter', 'kqlMode', timeline), refreshInterval: policy.duration, savedQueryId: getOr(null, 'savedQueryId', timeline), to: input.timerange.to, - toStr: input.timerange.toStr, + toStr: input.timerange.toStr!, }; }; return mapStateToProps; @@ -265,7 +226,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ updateReduxTime: dispatchUpdateReduxTime(dispatch), }); -export const StatefulSearchOrFilter = connect( - makeMapStateToProps, - mapDispatchToProps -)(StatefulSearchOrFilterComponent); +export const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulSearchOrFilter = connector(StatefulSearchOrFilterComponent); diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx index 9d70b69124f30..09457c8f0285a 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx @@ -60,7 +60,7 @@ interface Props { columns: ColumnHeader[]; dataProviders: DataProvider[]; end: number; - eventType: EventType; + eventType?: EventType; filters: Filter[]; flyoutHeaderHeight: number; flyoutHeight: number; diff --git a/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx b/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx index d77e0215f8353..caf597d02c835 100644 --- a/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/global_time/index.tsx @@ -5,12 +5,10 @@ */ import React, { useState, useEffect } from 'react'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; +import { connect, ConnectedProps } from 'react-redux'; import { inputsModel, inputsSelectors, State } from '../../store'; import { inputsActions } from '../../store/actions'; -import { InputsModelId } from '../../store/inputs/constants'; interface SetQuery { id: string; @@ -19,10 +17,6 @@ interface SetQuery { refetch: inputsModel.Refetch | inputsModel.RefetchKql; } -interface GlobalQuery extends SetQuery { - inputId: InputsModelId; -} - export interface GlobalTimeArgs { from: number; to: number; @@ -31,21 +25,11 @@ export interface GlobalTimeArgs { isInitializing: boolean; } -interface GlobalTimeDispatch { - setGlobalQuery: ActionCreator; - deleteAllQuery: ActionCreator<{ id: InputsModelId }>; - deleteOneQuery: ActionCreator<{ inputId: InputsModelId; id: string }>; -} - -interface GlobalTimeReduxState { - from: number; - to: number; -} interface OwnProps { children: (args: GlobalTimeArgs) => React.ReactNode; } -type GlobalTimeProps = OwnProps & GlobalTimeReduxState & GlobalTimeDispatch; +type GlobalTimeProps = OwnProps & PropsFromRedux; export const GlobalTimeComponent: React.FC = ({ children, @@ -88,8 +72,14 @@ const mapStateToProps = (state: State) => { }; }; -export const GlobalTime = connect(mapStateToProps, { +const mapDispatchToProps = { deleteAllQuery: inputsActions.deleteAllQuery, deleteOneQuery: inputsActions.deleteOneQuery, setGlobalQuery: inputsActions.setQuery, -})(React.memo(GlobalTimeComponent)); +}; + +export const connector = connect(mapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const GlobalTime = connector(React.memo(GlobalTimeComponent)); diff --git a/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx b/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx index 9576c66c4c9a5..ade94c430c6ef 100644 --- a/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/ip_overview/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { GetIpOverviewQuery, IpOverviewData } from '../../graphql/types'; @@ -28,17 +28,13 @@ export interface IpOverviewArgs { refetch: inputsModel.Refetch; } -export interface IpOverviewReduxProps { - isInspected: boolean; -} - export interface IpOverviewProps extends QueryTemplateProps { children: (args: IpOverviewArgs) => React.ReactNode; type: networkModel.NetworkType; ip: string; } -const IpOverviewComponentQuery = React.memo( +const IpOverviewComponentQuery = React.memo( ({ id = ID, isInspected, children, filterQuery, skip, sourceId, ip }) => ( query={ipOverviewQuery} @@ -81,4 +77,8 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const IpOverviewQuery = connect(makeMapStateToProps)(IpOverviewComponentQuery); +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const IpOverviewQuery = connector(IpOverviewComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/kpi_host_details/index.tsx b/x-pack/legacy/plugins/siem/public/containers/kpi_host_details/index.tsx index 501bc8472b5e2..de9d54b1a185c 100644 --- a/x-pack/legacy/plugins/siem/public/containers/kpi_host_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/kpi_host_details/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { KpiHostDetailsData, GetKpiHostDetailsQuery } from '../../graphql/types'; @@ -32,11 +32,7 @@ export interface QueryKpiHostDetailsProps extends QueryTemplateProps { children: (args: KpiHostDetailsArgs) => React.ReactNode; } -export interface KpiHostDetailsReducer { - isInspected: boolean; -} - -const KpiHostDetailsComponentQuery = React.memo( +const KpiHostDetailsComponentQuery = React.memo( ({ id = ID, children, endDate, filterQuery, isInspected, skip, sourceId, startDate }) => ( query={kpiHostDetailsQuery} @@ -82,4 +78,8 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const KpiHostDetailsQuery = connect(makeMapStateToProps)(KpiHostDetailsComponentQuery); +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const KpiHostDetailsQuery = connector(KpiHostDetailsComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx b/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx index 32472ba6deedf..5be2423e8a162 100644 --- a/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/kpi_hosts/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { GetKpiHostsQuery, KpiHostsData } from '../../graphql/types'; @@ -28,15 +28,11 @@ export interface KpiHostsArgs { refetch: inputsModel.Refetch; } -export interface KpiHostsReducer { - isInspected: boolean; -} - export interface KpiHostsProps extends QueryTemplateProps { children: (args: KpiHostsArgs) => React.ReactNode; } -const KpiHostsComponentQuery = React.memo( +const KpiHostsComponentQuery = React.memo( ({ id = ID, children, endDate, filterQuery, isInspected, skip, sourceId, startDate }) => ( query={kpiHostsQuery} @@ -82,4 +78,8 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const KpiHostsQuery = connect(makeMapStateToProps)(KpiHostsComponentQuery); +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const KpiHostsQuery = connector(KpiHostsComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx b/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx index 52b8814958ba0..338cdc39b178c 100644 --- a/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/kpi_network/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; import { GetKpiNetworkQuery, KpiNetworkData } from '../../graphql/types'; @@ -28,15 +28,11 @@ export interface KpiNetworkArgs { refetch: inputsModel.Refetch; } -export interface KpiNetworkReducer { - isInspected: boolean; -} - export interface KpiNetworkProps extends QueryTemplateProps { children: (args: KpiNetworkArgs) => React.ReactNode; } -const KpiNetworkComponentQuery = React.memo( +const KpiNetworkComponentQuery = React.memo( ({ id = ID, children, filterQuery, isInspected, skip, sourceId, startDate, endDate }) => ( query={kpiNetworkQuery} @@ -82,4 +78,8 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const KpiNetworkQuery = connect(makeMapStateToProps)(KpiNetworkComponentQuery); +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const KpiNetworkQuery = connector(KpiNetworkComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/containers/overview/overview_host/index.tsx index 8c40c4044a746..2dd9ccf24d802 100644 --- a/x-pack/legacy/plugins/siem/public/containers/overview/overview_host/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/overview/overview_host/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { GetOverviewHostQuery, OverviewHostData } from '../../../graphql/types'; @@ -29,10 +29,6 @@ export interface OverviewHostArgs { refetch: inputsModel.Refetch; } -export interface OverviewHostReducer { - isInspected: boolean; -} - export interface OverviewHostProps extends QueryTemplateProps { children: (args: OverviewHostArgs) => React.ReactNode; sourceId: string; @@ -40,7 +36,7 @@ export interface OverviewHostProps extends QueryTemplateProps { startDate: number; } -const OverviewHostComponentQuery = React.memo( +const OverviewHostComponentQuery = React.memo( ({ id = ID, children, filterQuery, isInspected, sourceId, startDate, endDate }) => { return ( @@ -86,4 +82,8 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const OverviewHostQuery = connect(makeMapStateToProps)(OverviewHostComponentQuery); +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const OverviewHostQuery = connector(OverviewHostComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/overview/overview_network/index.tsx b/x-pack/legacy/plugins/siem/public/containers/overview/overview_network/index.tsx index 9e7d59de0e546..d0acd41c224a5 100644 --- a/x-pack/legacy/plugins/siem/public/containers/overview/overview_network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/overview/overview_network/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { DEFAULT_INDEX_KEY } from '../../../../common/constants'; import { GetOverviewNetworkQuery, OverviewNetworkData } from '../../../graphql/types'; @@ -29,10 +29,6 @@ export interface OverviewNetworkArgs { refetch: inputsModel.Refetch; } -export interface OverviewNetworkReducer { - isInspected: boolean; -} - export interface OverviewNetworkProps extends QueryTemplateProps { children: (args: OverviewNetworkArgs) => React.ReactNode; sourceId: string; @@ -40,37 +36,37 @@ export interface OverviewNetworkProps extends QueryTemplateProps { startDate: number; } -export const OverviewNetworkComponentQuery = React.memo< - OverviewNetworkProps & OverviewNetworkReducer ->(({ id = ID, children, filterQuery, isInspected, sourceId, startDate, endDate }) => ( - - query={overviewNetworkQuery} - fetchPolicy={getDefaultFetchPolicy()} - notifyOnNetworkStatusChange - variables={{ - sourceId, - timerange: { - interval: '12h', - from: startDate, - to: endDate, - }, - filterQuery: createFilter(filterQuery), - defaultIndex: useUiSetting(DEFAULT_INDEX_KEY), - inspect: isInspected, - }} - > - {({ data, loading, refetch }) => { - const overviewNetwork = getOr({}, `source.OverviewNetwork`, data); - return children({ - id, - inspect: getOr(null, 'source.OverviewNetwork.inspect', data), - overviewNetwork, - loading, - refetch, - }); - }} - -)); +export const OverviewNetworkComponentQuery = React.memo( + ({ id = ID, children, filterQuery, isInspected, sourceId, startDate, endDate }) => ( + + query={overviewNetworkQuery} + fetchPolicy={getDefaultFetchPolicy()} + notifyOnNetworkStatusChange + variables={{ + sourceId, + timerange: { + interval: '12h', + from: startDate, + to: endDate, + }, + filterQuery: createFilter(filterQuery), + defaultIndex: useUiSetting(DEFAULT_INDEX_KEY), + inspect: isInspected, + }} + > + {({ data, loading, refetch }) => { + const overviewNetwork = getOr({}, `source.OverviewNetwork`, data); + return children({ + id, + inspect: getOr(null, 'source.OverviewNetwork.inspect', data), + overviewNetwork, + loading, + refetch, + }); + }} + + ) +); OverviewNetworkComponentQuery.displayName = 'OverviewNetworkComponentQuery'; @@ -85,4 +81,8 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const OverviewNetworkQuery = connect(makeMapStateToProps)(OverviewNetworkComponentQuery); +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + +export const OverviewNetworkQuery = connector(OverviewNetworkComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx index f4eb088b6ad94..68d87ef565fb7 100644 --- a/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/timeline/index.tsx @@ -9,7 +9,7 @@ import memoizeOne from 'memoize-one'; import React from 'react'; import { Query } from 'react-apollo'; import { compose } from 'redux'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common/index_patterns'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; @@ -39,10 +39,6 @@ export interface TimelineArgs { getUpdatedAt: () => number; } -export interface TimelineQueryReduxProps { - isInspected: boolean; -} - export interface OwnProps extends QueryTemplateProps { children?: (args: TimelineArgs) => React.ReactNode; eventType?: EventType; @@ -53,7 +49,8 @@ export interface OwnProps extends QueryTemplateProps { sortField: SortField; fields: string[]; } -type TimelineQueryProps = OwnProps & TimelineQueryReduxProps & WithKibanaProps; + +type TimelineQueryProps = OwnProps & PropsFromRedux & WithKibanaProps; class TimelineQueryComponent extends QueryTemplate< TimelineQueryProps, @@ -171,7 +168,11 @@ const makeMapStateToProps = () => { return mapStateToProps; }; +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + export const TimelineQuery = compose>( - connect(makeMapStateToProps), + connector, withKibana )(TimelineQueryComponent); diff --git a/x-pack/legacy/plugins/siem/public/containers/uncommon_processes/index.tsx b/x-pack/legacy/plugins/siem/public/containers/uncommon_processes/index.tsx index 520ade954eb5c..0a2ce67d9be80 100644 --- a/x-pack/legacy/plugins/siem/public/containers/uncommon_processes/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/uncommon_processes/index.tsx @@ -7,7 +7,7 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { compose } from 'redux'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; @@ -43,13 +43,7 @@ export interface OwnProps extends QueryTemplatePaginatedProps { type: hostsModel.HostsType; } -export interface UncommonProcessesComponentReduxProps { - activePage: number; - isInspected: boolean; - limit: number; -} - -type UncommonProcessesProps = OwnProps & UncommonProcessesComponentReduxProps & WithKibanaProps; +type UncommonProcessesProps = OwnProps & PropsFromRedux & WithKibanaProps; class UncommonProcessesComponentQuery extends QueryTemplatePaginated< UncommonProcessesProps, @@ -144,7 +138,11 @@ const makeMapStateToProps = () => { return mapStateToProps; }; +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + export const UncommonProcessesQuery = compose>( - connect(makeMapStateToProps), + connector, withKibana )(UncommonProcessesComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/containers/users/index.tsx b/x-pack/legacy/plugins/siem/public/containers/users/index.tsx index ece73b7b10ff0..5f71449c52460 100644 --- a/x-pack/legacy/plugins/siem/public/containers/users/index.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/users/index.tsx @@ -7,17 +7,11 @@ import { getOr } from 'lodash/fp'; import React from 'react'; import { Query } from 'react-apollo'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { compose } from 'redux'; import { DEFAULT_INDEX_KEY } from '../../../common/constants'; -import { - GetUsersQuery, - FlowTarget, - PageInfoPaginated, - UsersEdges, - UsersSortField, -} from '../../graphql/types'; +import { GetUsersQuery, FlowTarget, PageInfoPaginated, UsersEdges } from '../../graphql/types'; import { inputsModel, networkModel, networkSelectors, State, inputsSelectors } from '../../store'; import { withKibana, WithKibanaProps } from '../../lib/kibana'; import { createFilter, getDefaultFetchPolicy } from '../helpers'; @@ -47,14 +41,7 @@ export interface OwnProps extends QueryTemplatePaginatedProps { type: networkModel.NetworkType; } -export interface UsersComponentReduxProps { - activePage: number; - isInspected: boolean; - limit: number; - sort: UsersSortField; -} - -type UsersProps = OwnProps & UsersComponentReduxProps & WithKibanaProps; +type UsersProps = OwnProps & PropsFromRedux & WithKibanaProps; class UsersComponentQuery extends QueryTemplatePaginated< UsersProps, @@ -156,7 +143,11 @@ const makeMapStateToProps = () => { return mapStateToProps; }; +const connector = connect(makeMapStateToProps); + +type PropsFromRedux = ConnectedProps; + export const UsersQuery = compose>( - connect(makeMapStateToProps), + connector, withKibana )(UsersComponentQuery); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx index 8108e24cfa2c3..aacc6d951f4c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/components/signals/index.tsx @@ -7,17 +7,14 @@ import { EuiPanel, EuiLoadingContent } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; -import { ActionCreator } from 'typescript-fsa'; -import { Filter, esQuery, Query } from '../../../../../../../../../src/plugins/data/public'; +import { Filter, esQuery } from '../../../../../../../../../src/plugins/data/public'; import { useFetchIndexPatterns } from '../../../../containers/detection_engine/rules/fetch_index_patterns'; import { StatefulEventsViewer } from '../../../../components/events_viewer'; import { HeaderSection } from '../../../../components/header_section'; -import { DispatchUpdateTimeline } from '../../../../components/open_timeline/types'; import { combineQueries } from '../../../../components/timeline/helpers'; -import { TimelineNonEcsData } from '../../../../graphql/types'; import { useKibana } from '../../../../lib/kibana'; import { inputsSelectors, State, inputsModel } from '../../../../store'; import { timelineActions, timelineSelectors } from '../../../../store/timeline'; @@ -51,33 +48,6 @@ import { dispatchUpdateTimeline } from '../../../../components/open_timeline/hel const SIGNALS_PAGE_TIMELINE_ID = 'signals-page'; -interface ReduxProps { - globalQuery: Query; - globalFilters: Filter[]; - deletedEventIds: string[]; - isSelectAllChecked: boolean; - loadingEventIds: string[]; - selectedEventIds: Readonly>; -} - -interface DispatchProps { - clearEventsDeleted?: ActionCreator<{ id: string }>; - clearEventsLoading?: ActionCreator<{ id: string }>; - clearSelected?: ActionCreator<{ id: string }>; - setEventsDeleted?: ActionCreator<{ - id: string; - eventIds: string[]; - isDeleted: boolean; - }>; - setEventsLoading?: ActionCreator<{ - id: string; - eventIds: string[]; - isLoading: boolean; - }>; - updateTimelineIsLoading: ActionCreator<{ id: string; isLoading: boolean }>; - updateTimeline: DispatchUpdateTimeline; -} - interface OwnProps { canUserCRUD: boolean; defaultFilters?: Filter[]; @@ -88,7 +58,7 @@ interface OwnProps { to: number; } -type SignalsTableComponentProps = OwnProps & ReduxProps & DispatchProps; +type SignalsTableComponentProps = OwnProps & PropsFromRedux; const SignalsTableComponent: React.FC = ({ canUserCRUD, @@ -390,7 +360,8 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ updateTimeline: dispatchUpdateTimeline(dispatch), }); -export const SignalsTable = connect( - makeMapStateToProps, - mapDispatchToProps -)(React.memo(SignalsTableComponent)); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const SignalsTable = connector(React.memo(SignalsTableComponent)); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx index 8cfcac8fc862b..c3fb907ae83e1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -8,11 +8,7 @@ import { EuiButton, EuiSpacer } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; -import { connect } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; - -import { Query } from '../../../../../../../src/plugins/data/common/query'; -import { Filter } from '../../../../../../../src/plugins/data/common/es_query'; +import { connect, ConnectedProps } from 'react-redux'; import { GlobalTime } from '../../containers/global_time'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; @@ -30,7 +26,6 @@ import { State } from '../../store'; import { inputsSelectors } from '../../store/inputs'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { SpyRoute } from '../../utils/route/spy_routes'; -import { InputsModelId } from '../../store/inputs/constants'; import { InputsRange } from '../../store/inputs/model'; import { AlertsByCategory } from '../overview/alerts_by_category'; import { useSignalInfo } from './components/signals_info'; @@ -47,21 +42,6 @@ import { DetectionEngineUserUnauthenticated } from './detection_engine_user_unau import * as i18n from './translations'; import { DetectionEngineTab } from './types'; -interface ReduxProps { - filters: Filter[]; - query: Query; -} - -export interface DispatchProps { - setAbsoluteRangeDatePicker: ActionCreator<{ - id: InputsModelId; - from: number; - to: number; - }>; -} - -type DetectionEnginePageComponentProps = ReduxProps & DispatchProps; - const detectionsTabs: Record = { [DetectionEngineTab.signals]: { id: DetectionEngineTab.signals, @@ -79,7 +59,7 @@ const detectionsTabs: Record = { }, }; -const DetectionEnginePageComponent: React.FC = ({ +const DetectionEnginePageComponent: React.FC = ({ filters, query, setAbsoluteRangeDatePicker, @@ -234,7 +214,8 @@ const mapDispatchToProps = { setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker, }; -export const DetectionEnginePage = connect( - makeMapStateToProps, - mapDispatchToProps -)(React.memo(DetectionEnginePageComponent)); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const DetectionEnginePage = connector(React.memo(DetectionEnginePageComponent)); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index dc1ebd6052538..83dd18f0f14b7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -19,9 +19,8 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC, memo, useCallback, useMemo, useState } from 'react'; import { Redirect, useParams } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; +import { connect, ConnectedProps } from 'react-redux'; -import { ActionCreator } from 'typescript-fsa'; -import { connect } from 'react-redux'; import { FiltersGlobal } from '../../../../components/filters_global'; import { FormattedDate } from '../../../../components/formatted_date'; import { @@ -59,9 +58,6 @@ import * as ruleI18n from '../translations'; import * as i18n from './translations'; import { GlobalTime } from '../../../../containers/global_time'; import { signalsHistogramOptions } from '../../components/signals_histogram_panel/config'; -import { InputsModelId } from '../../../../store/inputs/constants'; -import { Filter } from '../../../../../../../../../src/plugins/data/common/es_query'; -import { Query } from '../../../../../../../../../src/plugins/data/common/query'; import { inputsSelectors } from '../../../../store/inputs'; import { State } from '../../../../store'; import { InputsRange } from '../../../../store/inputs/model'; @@ -71,19 +67,6 @@ import { RuleStatusFailedCallOut } from './status_failed_callout'; import { FailureHistory } from './failure_history'; import { RuleStatus } from '../components/rule_status'; -interface ReduxProps { - filters: Filter[]; - query: Query; -} - -export interface DispatchProps { - setAbsoluteRangeDatePicker: ActionCreator<{ - id: InputsModelId; - from: number; - to: number; - }>; -} - enum RuleDetailTabs { signals = 'signals', failures = 'failures', @@ -102,9 +85,7 @@ const ruleDetailTabs = [ }, ]; -type RuleDetailsComponentProps = ReduxProps & DispatchProps; - -const RuleDetailsPageComponent: FC = ({ +const RuleDetailsPageComponent: FC = ({ filters, query, setAbsoluteRangeDatePicker, @@ -417,7 +398,8 @@ const mapDispatchToProps = { setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker, }; -export const RuleDetailsPage = connect( - makeMapStateToProps, - mapDispatchToProps -)(memo(RuleDetailsPageComponent)); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const RuleDetailsPage = connector(memo(RuleDetailsPageComponent)); diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index 9ee103f88793d..1dce26b7c5d3a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -104,9 +104,7 @@ export const HomePage: React.FC = () => ( /> ( - - )} + render={({ match }) => } /> ( +export const HostDetailsTabs = React.memo( ({ pageFilters, deleteQuery, @@ -105,5 +105,3 @@ const HostDetailsTabs = React.memo( ); HostDetailsTabs.displayName = 'HostDetailsTabs'; - -export { HostDetailsTabs }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index a02e2b4aed22e..8af4731e4dda4 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -6,9 +6,8 @@ import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; import React, { useContext, useEffect, useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; -import { compose } from 'redux'; import { FiltersGlobal } from '../../../components/filters_global'; import { HeaderPage } from '../../../components/header_page'; @@ -39,14 +38,14 @@ import { esQuery, Filter } from '../../../../../../../../src/plugins/data/public import { HostsEmptyPage } from '../hosts_empty_page'; import { HostDetailsTabs } from './details_tabs'; import { navTabsHostDetails } from './nav_tabs'; -import { HostDetailsComponentProps, HostDetailsProps } from './types'; +import { HostDetailsProps } from './types'; import { type } from './utils'; import { getHostDetailsPageFilters } from './helpers'; const HostOverviewManage = manageQuery(HostOverview); const KpiHostDetailsManage = manageQuery(KpiHostsComponent); -const HostDetailsComponent = React.memo( +const HostDetailsComponent = React.memo( ({ filters, from, @@ -61,7 +60,7 @@ const HostDetailsComponent = React.memo( hostDetailsPagePath, }) => { useEffect(() => { - setHostDetailsTablesActivePageToZero(null); + setHostDetailsTablesActivePageToZero(); }, [setHostDetailsTablesActivePageToZero, detailName]); const capabilities = useContext(MlCapabilitiesContext); const kibana = useKibana(); @@ -218,9 +217,13 @@ export const makeMapStateToProps = () => { }); }; -export const HostDetails = compose>( - connect(makeMapStateToProps, { - setAbsoluteRangeDatePicker: dispatchAbsoluteRangeDatePicker, - setHostDetailsTablesActivePageToZero: dispatchHostDetailsTablesActivePageToZero, - }) -)(HostDetailsComponent); +const mapDispatchToProps = { + setAbsoluteRangeDatePicker: dispatchAbsoluteRangeDatePicker, + setHostDetailsTablesActivePageToZero: dispatchHostDetailsTablesActivePageToZero, +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const HostDetails = connector(HostDetailsComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx index 71dc3aac756ba..99cf767c65e08 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.test.tsx @@ -9,14 +9,12 @@ import { cloneDeep } from 'lodash/fp'; import React from 'react'; import { Router } from 'react-router-dom'; import { MockedProvider } from 'react-apollo/test-utils'; -import { ActionCreator } from 'typescript-fsa'; import { Filter } from '../../../../../../../src/plugins/data/common/es_query'; import '../../mock/match_media'; import { mocksSource } from '../../containers/source/mock'; import { wait } from '../../lib/helpers'; import { apolloClientObservable, TestProviders, mockGlobalState } from '../../mock'; -import { InputsModelId } from '../../store/inputs/constants'; import { SiemNavigation } from '../../components/navigation'; import { inputsActions } from '../../store/inputs'; import { State, createStore } from '../../store'; @@ -77,13 +75,6 @@ describe('Hosts - rendering', () => { to, setQuery: jest.fn(), isInitializing: false, - setAbsoluteRangeDatePicker: (jest.fn() as unknown) as ActionCreator<{ - from: number; - id: InputsModelId; - to: number; - }>, - query: { query: '', language: 'kuery' }, - filters: [], hostsPagePath: '', }; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index 06dffcdb220a9..a7aa9920b7d08 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -6,9 +6,8 @@ import { EuiSpacer } from '@elastic/eui'; import React, { useCallback } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; -import { compose } from 'redux'; import { useParams } from 'react-router-dom'; import { FiltersGlobal } from '../../components/filters_global'; @@ -21,7 +20,6 @@ import { KpiHostsComponent } from '../../components/page/hosts'; import { manageQuery } from '../../components/page/manage_query'; import { SiemSearchBar } from '../../components/search_bar'; import { WrapperPage } from '../../components/wrapper_page'; -import { GlobalTimeArgs } from '../../containers/global_time'; import { KpiHostsQuery } from '../../containers/kpi_hosts'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { LastEventIndexKey } from '../../graphql/types'; @@ -36,13 +34,13 @@ import { HostsEmptyPage } from './hosts_empty_page'; import { HostsTabs } from './hosts_tabs'; import { navTabsHosts } from './nav_tabs'; import * as i18n from './translations'; -import { HostsComponentProps, HostsComponentReduxProps } from './types'; +import { HostsComponentProps } from './types'; import { filterHostData } from './navigation'; import { HostsTableType } from '../../store/hosts/model'; const KpiHostsComponentManage = manageQuery(KpiHostsComponent); -export const HostsComponent = React.memo( +export const HostsComponent = React.memo( ({ deleteQuery, isInitializing, @@ -161,7 +159,7 @@ HostsComponent.displayName = 'HostsComponent'; const makeMapStateToProps = () => { const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); - const mapStateToProps = (state: State): HostsComponentReduxProps => ({ + const mapStateToProps = (state: State) => ({ query: getGlobalQuerySelector(state), filters: getGlobalFiltersQuerySelector(state), }); @@ -169,13 +167,12 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -interface HostsProps extends GlobalTimeArgs { - hostsPagePath: string; -} +const mapDispatchToProps = { + setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker, +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const Hosts = compose>( - connect(makeMapStateToProps, { - setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker, - }) -)(HostsComponent); +export const Hosts = connector(HostsComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx index fff5c5218c003..699b1441905c3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/index.tsx @@ -56,10 +56,14 @@ export const HostsContainer = React.memo(({ url }) => ( ( + render={({ + match: { + params: { detailName }, + }, + }) => ( ; - hostsPagePath: string; -} - -export type HostsTabsProps = HostsComponentDispatchProps & - HostsQueryProps & { - filterQuery: string; - type: hostsModel.HostsType; - indexPattern: IIndexPattern; - }; +}; export type HostsQueryProps = GlobalTimeArgs; -export type HostsComponentProps = HostsComponentReduxProps & - HostsComponentDispatchProps & - HostsQueryProps; +export type HostsComponentProps = HostsQueryProps & { hostsPagePath: string }; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx index 9a9d1cf085eb9..02132d790796c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx @@ -92,7 +92,7 @@ const getMockProps = (ip: string) => ({ from: number; to: number; }>, - setIpDetailsTablesActivePageToZero: (jest.fn() as unknown) as ActionCreator, + setIpDetailsTablesActivePageToZero: (jest.fn() as unknown) as ActionCreator, }); describe('Ip Details', () => { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx index 2e8044d2c2fe8..e796eaca0cd28 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx @@ -6,7 +6,7 @@ import { EuiHorizontalRule, EuiSpacer, EuiFlexItem } from '@elastic/eui'; import React, { useCallback, useEffect } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; import { FiltersGlobal } from '../../../components/filters_global'; @@ -46,7 +46,7 @@ export { getBreadcrumbs } from './utils'; const IpOverviewManage = manageQuery(IpOverview); -export const IPDetailsComponent = ({ +export const IPDetailsComponent: React.FC = ({ detailName, filters, flowTarget, @@ -57,7 +57,7 @@ export const IPDetailsComponent = ({ setIpDetailsTablesActivePageToZero, setQuery, to, -}: IPDetailsComponentProps) => { +}) => { const type = networkModel.NetworkType.details; const narrowDateRange = useCallback( (score, interval) => { @@ -73,7 +73,7 @@ export const IPDetailsComponent = ({ const kibana = useKibana(); useEffect(() => { - setIpDetailsTablesActivePageToZero(null); + setIpDetailsTablesActivePageToZero(); }, [detailName, setIpDetailsTablesActivePageToZero]); return ( @@ -279,13 +279,20 @@ IPDetailsComponent.displayName = 'IPDetailsComponent'; const makeMapStateToProps = () => { const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); + return (state: State) => ({ query: getGlobalQuerySelector(state), filters: getGlobalFiltersQuerySelector(state), }); }; -export const IPDetails = connect(makeMapStateToProps, { +const mapDispatchToProps = { setAbsoluteRangeDatePicker: dispatchAbsoluteRangeDatePicker, setIpDetailsTablesActivePageToZero: dispatchIpDetailsTablesActivePageToZero, -})(React.memo(IPDetailsComponent)); +}; + +export const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const IPDetails = connector(React.memo(IPDetailsComponent)); diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts index b53d58e6664af..ef989fb64eabe 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/types.ts @@ -4,38 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ActionCreator } from 'typescript-fsa'; -import { IIndexPattern, Query, Filter } from 'src/plugins/data/public'; +import { IIndexPattern } from 'src/plugins/data/public'; import { NetworkType } from '../../../store/network/model'; import { ESTermQuery } from '../../../../common/typed_json'; import { InspectQuery, Refetch } from '../../../store/inputs/model'; import { FlowTarget, FlowTargetSourceDest } from '../../../graphql/types'; -import { InputsModelId } from '../../../store/inputs/constants'; import { GlobalTimeArgs } from '../../../containers/global_time'; export const type = NetworkType.details; -type SetAbsoluteRangeDatePicker = ActionCreator<{ - id: InputsModelId; - from: number; - to: number; -}>; - -interface IPDetailsComponentReduxProps { - filters: Filter[]; +export type IPDetailsComponentProps = GlobalTimeArgs & { + detailName: string; flowTarget: FlowTarget; - query: Query; -} - -interface IPDetailsComponentDispatchProps { - setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker; - setIpDetailsTablesActivePageToZero: ActionCreator; -} - -export type IPDetailsComponentProps = IPDetailsComponentReduxProps & - IPDetailsComponentDispatchProps & - GlobalTimeArgs & { detailName: string }; +}; export interface OwnProps { type: NetworkType; diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index 0f9eaaef48aa7..9b1ee76e1d376 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -6,7 +6,7 @@ import { EuiSpacer } from '@elastic/eui'; import React, { useCallback, useMemo } from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { useParams } from 'react-router-dom'; import { StickyContainer } from 'react-sticky'; @@ -38,7 +38,7 @@ import { NetworkRouteType } from './navigation/types'; const KpiNetworkComponentManage = manageQuery(KpiNetworkComponent); const sourceId = 'default'; -const NetworkComponent = React.memo( +const NetworkComponent = React.memo( ({ filters, query, @@ -183,6 +183,12 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export const Network = connect(makeMapStateToProps, { +const mapDispatchToProps = { setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker, -})(NetworkComponent); +}; + +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const Network = connector(NetworkComponent); diff --git a/x-pack/legacy/plugins/siem/public/pages/network/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/types.ts index 8a9914133c9af..01d3fb6b48c63 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/types.ts @@ -6,9 +6,8 @@ import { RouteComponentProps } from 'react-router-dom'; import { ActionCreator } from 'typescript-fsa'; -import { GlobalTimeArgs } from '../../containers/global_time'; import { InputsModelId } from '../../store/inputs/constants'; -import { Query, Filter } from '../../../../../../../src/plugins/data/public'; +import { GlobalTimeArgs } from '../../containers/global_time'; export type SetAbsoluteRangeDatePicker = ActionCreator<{ id: InputsModelId; @@ -16,15 +15,8 @@ export type SetAbsoluteRangeDatePicker = ActionCreator<{ to: number; }>; -interface NetworkComponentReduxProps { - filters: Filter[]; - query: Query; - setAbsoluteRangeDatePicker: SetAbsoluteRangeDatePicker; -} - -export type NetworkComponentProps = NetworkComponentReduxProps & - GlobalTimeArgs & - Partial> & { +export type NetworkComponentProps = Partial> & + GlobalTimeArgs & { networkPagePath: string; hasMlUserPermissions: boolean; capabilitiesFetched: boolean; diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx index 8505b91fe1ff5..2db49e60193fc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx @@ -6,9 +6,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import React from 'react'; -import { connect } from 'react-redux'; +import { connect, ConnectedProps } from 'react-redux'; import { StickyContainer } from 'react-sticky'; -import { compose } from 'redux'; import { Query, Filter } from 'src/plugins/data/public'; import styled from 'styled-components'; @@ -20,7 +19,6 @@ import { GlobalTime } from '../../containers/global_time'; import { WithSource, indicesExistOrDataTemporarilyUnavailable } from '../../containers/source'; import { EventsByDataset } from './events_by_dataset'; import { EventCounts } from './event_counts'; -import { SetAbsoluteRangeDatePicker } from '../network/types'; import { OverviewEmpty } from './overview_empty'; import { StatefulSidebar } from './sidebar'; import { SignalsByCategory } from './signals_by_category'; @@ -35,13 +33,7 @@ const SidebarFlexItem = styled(EuiFlexItem)` margin-right: 24px; `; -interface OverviewComponentReduxProps { - query?: Query; - filters?: Filter[]; - setAbsoluteRangeDatePicker?: SetAbsoluteRangeDatePicker; -} - -const OverviewComponent: React.FC = ({ +const OverviewComponent: React.FC = ({ filters = NO_FILTERS, query = DEFAULT_QUERY, setAbsoluteRangeDatePicker, @@ -133,7 +125,7 @@ const makeMapStateToProps = () => { const getGlobalFiltersQuerySelector = inputsSelectors.globalFiltersQuerySelector(); const getGlobalQuerySelector = inputsSelectors.globalQuerySelector(); - const mapStateToProps = (state: State): OverviewComponentReduxProps => ({ + const mapStateToProps = (state: State) => ({ query: getGlobalQuerySelector(state), filters: getGlobalFiltersQuerySelector(state), }); @@ -143,6 +135,8 @@ const makeMapStateToProps = () => { const mapDispatchToProps = { setAbsoluteRangeDatePicker: dispatchSetAbsoluteRangeDatePicker }; -export const StatefulOverview = compose>( - connect(makeMapStateToProps, mapDispatchToProps) -)(React.memo(OverviewComponent)); +const connector = connect(makeMapStateToProps, mapDispatchToProps); + +type PropsFromRedux = ConnectedProps; + +export const StatefulOverview = connector(React.memo(OverviewComponent)); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts b/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts index 8255ba41d2bb1..f9da0e558c655 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/actions.ts @@ -7,7 +7,7 @@ import actionCreatorFactory from 'typescript-fsa'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; -import { InspectQuery, Refetch } from './model'; +import { InspectQuery, Refetch, RefetchKql } from './model'; import { InputsModelId } from './constants'; import { Filter } from '../../../../../../../src/plugins/data/public'; @@ -42,7 +42,7 @@ export const setQuery = actionCreator<{ inputId: InputsModelId; id: string; loading: boolean; - refetch: Refetch; + refetch: Refetch | RefetchKql; inspect: InspectQuery | null; }>('SET_QUERY'); diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts b/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts index 194d2e8cd35c3..4a3c17a6234e7 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/helpers.ts @@ -6,7 +6,7 @@ import { get } from 'lodash/fp'; -import { InputsModel, TimeRange, Refetch, InspectQuery } from './model'; +import { InputsModel, TimeRange, Refetch, RefetchKql, InspectQuery } from './model'; import { InputsModelId } from './constants'; export const updateInputTimerange = ( @@ -59,7 +59,7 @@ export interface UpdateQueryParams { inputId: InputsModelId; inspect: InspectQuery | null; loading: boolean; - refetch: Refetch; + refetch: Refetch | RefetchKql; state: InputsModel; } diff --git a/x-pack/legacy/plugins/siem/public/store/network/selectors.ts b/x-pack/legacy/plugins/siem/public/store/network/selectors.ts index 64a325b6e1ca4..273eaf7c0ee7f 100644 --- a/x-pack/legacy/plugins/siem/public/store/network/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/store/network/selectors.ts @@ -16,6 +16,9 @@ import { NetworkPageModel, NetworkTableType, NetworkType, + TopCountriesQuery, + TlsQuery, + HttpQuery, } from './model'; const selectNetworkPage = (state: State): NetworkPageModel => state.network.page; @@ -41,7 +44,7 @@ const selectTopNFlowByType = ( export const topNFlowSelector = () => createSelector(selectTopNFlowByType, topNFlowQueries => topNFlowQueries); -const selectTlsByType = (state: State, networkType: NetworkType) => { +const selectTlsByType = (state: State, networkType: NetworkType): TlsQuery => { const tlsType = networkType === NetworkType.page ? NetworkTableType.tls : IpDetailsTableType.tls; return ( get([networkType, 'queries', tlsType], state.network) || @@ -55,7 +58,7 @@ const selectTopCountriesByType = ( state: State, networkType: NetworkType, flowTarget: FlowTargetSourceDest -) => { +): TopCountriesQuery => { const ft = flowTarget === FlowTargetSourceDest.source ? 'topCountriesSource' : 'topCountriesDestination'; const nFlowType = @@ -70,7 +73,7 @@ const selectTopCountriesByType = ( export const topCountriesSelector = () => createSelector(selectTopCountriesByType, topCountriesQueries => topCountriesQueries); -const selectHttpByType = (state: State, networkType: NetworkType) => { +const selectHttpByType = (state: State, networkType: NetworkType): HttpQuery => { const httpType = networkType === NetworkType.page ? NetworkTableType.http : IpDetailsTableType.http; return ( diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/charts/ping_histogram.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/charts/ping_histogram.tsx index 744c82a9983c8..b4989282f854c 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/charts/ping_histogram.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/charts/ping_histogram.tsx @@ -31,7 +31,7 @@ export interface PingHistogramComponentProps { */ height?: string; - data?: HistogramResult; + data: HistogramResult | null; loading?: boolean; } diff --git a/x-pack/legacy/plugins/uptime/public/contexts/uptime_refresh_context.tsx b/x-pack/legacy/plugins/uptime/public/contexts/uptime_refresh_context.tsx index 4516289bd51d6..8e406f621f648 100644 --- a/x-pack/legacy/plugins/uptime/public/contexts/uptime_refresh_context.tsx +++ b/x-pack/legacy/plugins/uptime/public/contexts/uptime_refresh_context.tsx @@ -28,6 +28,7 @@ export const UptimeRefreshContextProvider: React.FC = ({ children }) => { const refreshApp = () => { const refreshTime = Date.now(); setLastRefresh(refreshTime); + // @ts-ignore store.dispatch(triggerAppRefresh(refreshTime)); }; diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index db34566d6c148..66ff5ba7a58ee 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -85,6 +85,7 @@ const Application = (props: UptimeAppProps) => { ); }, [canSave, renderGlobalHelpControls, setBadge]); + // @ts-ignore store.dispatch(setBasePath(basePath)); return ( diff --git a/x-pack/package.json b/x-pack/package.json index e3bc8aa36373d..37791c255aee9 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -65,7 +65,7 @@ "@types/graphql": "^0.13.2", "@types/gulp": "^4.0.6", "@types/hapi__wreck": "^15.0.1", - "@types/hoist-non-react-statics": "^3.3.0", + "@types/hoist-non-react-statics": "^3.3.1", "@types/history": "^4.7.3", "@types/jest": "24.0.19", "@types/joi": "^13.4.2", @@ -88,14 +88,14 @@ "@types/prop-types": "^15.5.3", "@types/proper-lockfile": "^3.0.1", "@types/puppeteer": "^1.20.1", - "@types/react": "^16.9.11", - "@types/react-dom": "^16.9.4", - "@types/react-redux": "^6.0.6", + "@types/react": "^16.9.19", + "@types/react-dom": "^16.9.5", + "@types/react-redux": "^7.1.7", "@types/react-router-dom": "^5.1.3", "@types/react-sticky": "^6.0.3", "@types/react-test-renderer": "^16.9.1", "@types/recompose": "^0.30.6", - "@types/reduce-reducers": "^0.3.0", + "@types/reduce-reducers": "^1.0.0", "@types/redux-actions": "^2.6.1", "@types/sinon": "^7.0.13", "@types/styled-components": "^4.4.2", @@ -118,10 +118,10 @@ "copy-webpack-plugin": "^5.0.4", "cypress": "^3.6.1", "cypress-multi-reporters": "^1.2.3", - "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.15.1", - "enzyme-adapter-utils": "^1.12.1", - "enzyme-to-json": "^3.4.3", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.2", + "enzyme-adapter-utils": "^1.13.0", + "enzyme-to-json": "^3.4.4", "execa": "^3.2.0", "fancy-log": "^1.3.2", "fetch-mock": "^7.3.9", @@ -134,6 +134,7 @@ "graphql-codegen-typescript-server": "^0.18.2", "gulp": "4.0.2", "hapi": "^17.5.3", + "hoist-non-react-statics": "^3.3.2", "jest": "^24.9.0", "jest-cli": "^24.9.0", "jest-styled-components": "^7.0.0", @@ -305,7 +306,7 @@ "react-markdown": "^3.4.1", "react-moment-proptypes": "^1.7.0", "react-portal": "^3.2.0", - "react-redux": "^5.1.2", + "react-redux": "^7.1.3", "react-reverse-portal": "^1.0.4", "react-router-dom": "^5.1.2", "react-shortcuts": "^2.0.0", @@ -316,15 +317,15 @@ "react-vis": "^1.8.1", "react-visibility-sensor": "^5.1.1", "recompose": "^0.26.0", - "reduce-reducers": "^0.4.3", - "redux": "4.0.0", - "redux-actions": "2.6.5", - "redux-observable": "^1.0.0", + "reduce-reducers": "^1.0.4", + "redux": "^4.0.5", + "redux-actions": "^2.6.5", + "redux-observable": "^1.2.0", "redux-saga": "^0.16.0", - "redux-thunk": "2.3.0", + "redux-thunk": "^2.3.0", "redux-thunks": "^1.0.0", "request": "^2.88.0", - "reselect": "3.0.1", + "reselect": "^4.0.0", "resize-observer-polyfill": "^1.5.0", "rison-node": "0.3.1", "rxjs": "^6.5.3", @@ -339,8 +340,8 @@ "topojson-client": "3.0.0", "tslib": "^1.9.3", "turf": "3.0.14", - "typescript-fsa": "^2.5.0", - "typescript-fsa-reducers": "^0.4.5", + "typescript-fsa": "^3.0.0", + "typescript-fsa-reducers": "^1.2.1", "ui-select": "0.19.8", "unstated": "^2.1.1", "uuid": "3.3.2", diff --git a/yarn.lock b/yarn.lock index 33cb366e72f00..27967aefa20c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4391,7 +4391,7 @@ resolved "https://registry.yarnpkg.com/@types/elasticsearch/-/elasticsearch-5.0.33.tgz#b0fd37dc674f498223b6d68c313bdfd71f4d812b" integrity sha512-n/g9pqJEpE4fyUE8VvHNGtl7E2Wv8TCroNwfgAeJKRV4ghDENahtrAo1KMsFNIejBD2gDAlEUa4CM4oEEd8p9Q== -"@types/enzyme@^3.1.13", "@types/enzyme@^3.9.0": +"@types/enzyme@^3.1.13", "@types/enzyme@^3.10.5": version "3.10.5" resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0" integrity sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA== @@ -4580,7 +4580,7 @@ resolved "https://registry.yarnpkg.com/@types/hoek/-/hoek-4.1.3.tgz#d1982d48fb0d2a0e5d7e9d91838264d8e428d337" integrity sha1-0ZgtSPsNKg5dfp2Rg4Jk2OQo0zc= -"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0": +"@types/hoist-non-react-statics@*", "@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== @@ -4998,13 +4998,20 @@ dependencies: "@types/react" "*" -"@types/react-dom@*", "@types/react-dom@^16.9.4": +"@types/react-dom@*": version "16.9.4" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.4.tgz#0b58df09a60961dcb77f62d4f1832427513420df" integrity sha512-fya9xteU/n90tda0s+FtN5Ym4tbgxpq/hb/Af24dvs6uYnYn+fspaxw5USlw0R8apDNwxsqumdRoCoKitckQqw== dependencies: "@types/react" "*" +"@types/react-dom@^16.9.5": + version "16.9.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.5.tgz#5de610b04a35d07ffd8f44edad93a71032d9aaa7" + integrity sha512-BX6RQ8s9D+2/gDhxrj8OW+YD4R+8hj7FEM/OJHGNR0KipE1h1mSsf39YeyC81qafkq+N3rU3h3RFbLSwE5VqUg== + dependencies: + "@types/react" "*" + "@types/react-grid-layout@^0.16.7": version "0.16.7" resolved "https://registry.yarnpkg.com/@types/react-grid-layout/-/react-grid-layout-0.16.7.tgz#53d5f5034deb0c60e25a0fa578141e9a0982011f" @@ -5025,14 +5032,6 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react-redux@^6.0.6": - version "6.0.6" - resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-6.0.6.tgz#87f1d0a6ea901b93fcaf95fa57641ff64079d277" - integrity sha512-sD/QEn45h+CH0OAhCn6/9COlihZ94bzpP58QzYYCL3tOFta/WBhuvMoyLP8khJLfwQBx1PT70HP/1GnDws9YXQ== - dependencies: - "@types/react" "*" - redux "^4.0.0" - "@types/react-redux@^7.1.0": version "7.1.5" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.5.tgz#c7a528d538969250347aa53c52241051cf886bd3" @@ -5043,6 +5042,16 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" +"@types/react-redux@^7.1.7": + version "7.1.7" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.7.tgz#12a0c529aba660696947384a059c5c6e08185c7a" + integrity sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-resize-detector@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/react-resize-detector/-/react-resize-detector-4.0.1.tgz#cc8f012f5957e4826e69b8d2afd59baadcac556c" @@ -5103,10 +5112,10 @@ "@types/prop-types" "*" "@types/react" "*" -"@types/react@*", "@types/react@^16.8.23", "@types/react@^16.9.11", "@types/react@^16.9.13": - version "16.9.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.15.tgz#aeabb7a50f96c9e31a16079ada20ede9ed602977" - integrity sha512-WsmM1b6xQn1tG3X2Hx4F3bZwc2E82pJXt5OPs2YJgg71IzvUoKOSSSYOvLXYCg1ttipM+UuA4Lj3sfvqjVxyZw== +"@types/react@*", "@types/react@^16.8.23", "@types/react@^16.9.19": + version "16.9.19" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.19.tgz#c842aa83ea490007d29938146ff2e4d9e4360c40" + integrity sha512-LJV97//H+zqKWMms0kvxaKYJDG05U2TtQB3chRLF8MPNs+MQh/H1aGlyDUxjaHvu08EAGerdX2z4LTBc7ns77A== dependencies: "@types/prop-types" "*" csstype "^2.2.0" @@ -5125,23 +5134,18 @@ dependencies: "@types/react" "*" -"@types/reduce-reducers@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@types/reduce-reducers/-/reduce-reducers-0.3.0.tgz#d86d88049c38bebbafa5baf121282d765fabbebf" - integrity sha512-S9Vi74p0UOlqf9dqiurBwNBp+ABAGZ7nJAZteFS6LS8y9u2fkGfvNk1gtauKoDG+BuB4vjp1+P/R0/r5rFxYDQ== +"@types/reduce-reducers@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/reduce-reducers/-/reduce-reducers-1.0.0.tgz#033120b109159f4b344210ee75310d1ec3fcc8ef" + integrity sha512-MWKF9QDH/HaMQZvGyQlrut1ttyrFVIrGZqhCXPBcl5LelhnfC7dIE2jK2qZjxFHUCeXgjKCk5hN+/GzA7GgqjQ== dependencies: - redux "^4.0.0" + reduce-reducers "*" "@types/redux-actions@^2.6.1": version "2.6.1" resolved "https://registry.yarnpkg.com/@types/redux-actions/-/redux-actions-2.6.1.tgz#0940e97fa35ad3004316bddb391d8e01d2efa605" integrity sha512-zKgK+ATp3sswXs6sOYo1tk8xdXTy4CTaeeYrVQlClCjeOpag5vzPo0ASWiiBJ7vsiQRAdb3VkuFLnDoBimF67g== -"@types/redux@^3.6.31": - version "3.6.31" - resolved "https://registry.yarnpkg.com/@types/redux/-/redux-3.6.31.tgz#40eafa7575db36b912ce0059b85de98c205b0708" - integrity sha1-QOr6dXXbNrkSzgBZuF3pjCBbBwg= - "@types/request@^2.48.2": version "2.48.2" resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.2.tgz#936374cbe1179d7ed529fc02543deb4597450fed" @@ -6807,14 +6811,13 @@ array.prototype.find@^2.1.0: define-properties "^1.1.3" es-abstract "^1.13.0" -array.prototype.flat@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4" - integrity sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw== +array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== dependencies: - define-properties "^1.1.2" - es-abstract "^1.10.0" - function-bind "^1.1.1" + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" array.prototype.flatmap@^1.2.1: version "1.2.1" @@ -8967,7 +8970,7 @@ cheerio@0.22.0: lodash.reject "^4.4.0" lodash.some "^4.4.0" -cheerio@^1.0.0-rc.2: +cheerio@^1.0.0-rc.3: version "1.0.0-rc.3" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== @@ -12218,74 +12221,76 @@ env-variable@0.0.x: resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88" integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA== -enzyme-adapter-react-16@^1.15.1: - version "1.15.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.1.tgz#8ad55332be7091dc53a25d7d38b3485fc2ba50d5" - integrity sha512-yMPxrP3vjJP+4wL/qqfkT6JAIctcwKF+zXO6utlGPgUJT2l4tzrdjMDWGd/Pp1BjHBcljhN24OzNEGRteibJhA== +enzyme-adapter-react-16@^1.15.2: + version "1.15.2" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.2.tgz#b16db2f0ea424d58a808f9df86ab6212895a4501" + integrity sha512-SkvDrb8xU3lSxID8Qic9rB8pvevDbLybxPK6D/vW7PrT0s2Cl/zJYuXvsd1EBTz0q4o3iqG3FJhpYz3nUNpM2Q== dependencies: - enzyme-adapter-utils "^1.12.1" - enzyme-shallow-equal "^1.0.0" + enzyme-adapter-utils "^1.13.0" + enzyme-shallow-equal "^1.0.1" has "^1.0.3" object.assign "^4.1.0" - object.values "^1.1.0" + object.values "^1.1.1" prop-types "^15.7.2" - react-is "^16.10.2" + react-is "^16.12.0" react-test-renderer "^16.0.0-0" semver "^5.7.0" -enzyme-adapter-utils@^1.12.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.1.tgz#e828e0d038e2b1efa4b9619ce896226f85c9dd88" - integrity sha512-KWiHzSjZaLEoDCOxY8Z1RAbUResbqKN5bZvenPbfKtWorJFVETUw754ebkuCQ3JKm0adx1kF8JaiR+PHPiP47g== +enzyme-adapter-utils@^1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.0.tgz#01c885dde2114b4690bf741f8dc94cee3060eb78" + integrity sha512-YuEtfQp76Lj5TG1NvtP2eGJnFKogk/zT70fyYHXK2j3v6CtuHqc8YmgH/vaiBfL8K1SgVVbQXtTcgQZFwzTVyQ== dependencies: airbnb-prop-types "^2.15.0" - function.prototype.name "^1.1.1" + function.prototype.name "^1.1.2" object.assign "^4.1.0" - object.fromentries "^2.0.1" + object.fromentries "^2.0.2" prop-types "^15.7.2" - semver "^5.7.0" + semver "^5.7.1" -enzyme-shallow-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.0.tgz#d8e4603495e6ea279038eef05a4bf4887b55dc69" - integrity sha512-VUf+q5o1EIv2ZaloNQQtWCJM9gpeux6vudGVH6vLmfPXFLRuxl5+Aq3U260wof9nn0b0i+P5OEUXm1vnxkRpXQ== +enzyme-shallow-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.1.tgz#7afe03db3801c9b76de8440694096412a8d9d49e" + integrity sha512-hGA3i1so8OrYOZSM9whlkNmVHOicJpsjgTzC+wn2JMJXhq1oO4kA4bJ5MsfzSIcC71aLDKzJ6gZpIxrqt3QTAQ== dependencies: has "^1.0.3" - object-is "^1.0.1" + object-is "^1.0.2" -enzyme-to-json@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.4.3.tgz#ed4386f48768ed29e2d1a2910893542c34e7e0af" - integrity sha512-jqNEZlHqLdz7OTpXSzzghArSS3vigj67IU/fWkPyl1c0TCj9P5s6Ze0kRkYZWNEoCqCR79xlQbigYlMx5erh8A== +enzyme-to-json@^3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.4.4.tgz#b30726c59091d273521b6568c859e8831e94d00e" + integrity sha512-50LELP/SCPJJGic5rAARvU7pgE3m1YaNj7JLM+Qkhl5t7PAs6fiyc8xzc50RnkKPFQCv0EeFVjEWdIFRGPWMsA== dependencies: lodash "^4.17.15" + react-is "^16.12.0" -enzyme@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.10.0.tgz#7218e347c4a7746e133f8e964aada4a3523452f6" - integrity sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg== +enzyme@^3.11.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28" + integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw== dependencies: - array.prototype.flat "^1.2.1" - cheerio "^1.0.0-rc.2" - function.prototype.name "^1.1.0" + array.prototype.flat "^1.2.3" + cheerio "^1.0.0-rc.3" + enzyme-shallow-equal "^1.0.1" + function.prototype.name "^1.1.2" has "^1.0.3" - html-element-map "^1.0.0" - is-boolean-object "^1.0.0" - is-callable "^1.1.4" - is-number-object "^1.0.3" - is-regex "^1.0.4" - is-string "^1.0.4" + html-element-map "^1.2.0" + is-boolean-object "^1.0.1" + is-callable "^1.1.5" + is-number-object "^1.0.4" + is-regex "^1.0.5" + is-string "^1.0.5" is-subset "^0.1.1" lodash.escape "^4.0.1" lodash.isequal "^4.5.0" - object-inspect "^1.6.0" - object-is "^1.0.1" + object-inspect "^1.7.0" + object-is "^1.0.2" object.assign "^4.1.0" - object.entries "^1.0.4" - object.values "^1.0.4" - raf "^3.4.0" + object.entries "^1.1.1" + object.values "^1.1.1" + raf "^3.4.1" rst-selector-parser "^2.2.3" - string.prototype.trim "^1.1.2" + string.prototype.trim "^1.2.1" errlop@^1.1.2: version "1.1.2" @@ -12335,26 +12340,44 @@ error@^7.0.0, error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" -es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.14.2, es-abstract@^1.15.0, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" - integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg== +es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.13.0, es-abstract@^1.14.2, es-abstract@^1.17.0-next.1, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" + integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== dependencies: - es-to-primitive "^1.2.0" + es-to-primitive "^1.2.1" function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.0" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-inspect "^1.6.0" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" object-keys "^1.1.1" - string.prototype.trimleft "^2.1.0" - string.prototype.trimright "^2.1.0" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== +es-abstract@^1.15.0: + version "1.17.4" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" + integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== dependencies: is-callable "^1.1.4" is-date-object "^1.0.1" @@ -14417,31 +14440,21 @@ function-bind@^1.0.2, function-bind@^1.1.1, function-bind@~1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327" - integrity sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - is-callable "^1.1.3" - -function.prototype.name@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.1.tgz#6d252350803085abc2ad423d4fe3be2f9cbda392" - integrity sha512-e1NzkiJuw6xqVH7YSdiW/qDHebcmMhPNe6w+4ZYYEg0VA+LaLzx37RimbPLuonHhYGFGPx1ME2nSi74JiaCr/Q== +function.prototype.name@^1.1.0, function.prototype.name@^1.1.1, function.prototype.name@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45" + integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg== dependencies: define-properties "^1.1.3" - function-bind "^1.1.1" - functions-have-names "^1.1.1" - is-callable "^1.1.4" + es-abstract "^1.17.0-next.1" + functions-have-names "^1.2.0" functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -functions-have-names@^1.1.1: +functions-have-names@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.0.tgz#83da7583e4ea0c9ac5ff530f73394b033e0bf77d" integrity sha512-zKXyzksTeaCSw5wIX79iCA40YAa6CJMJgNg9wdkU/ERBrIdPSimPICYiLp65lRbSBqtiHql/HZfS2DyI/AH6tQ== @@ -15876,10 +15889,10 @@ has-symbol-support-x@^1.4.1: resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q= +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== has-to-string-tag-x@^1.2.0: version "1.4.1" @@ -16110,22 +16123,10 @@ hoek@6.x.x: resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.0.3.tgz#7884360426d927865a0a1251fc9c59313af5b798" integrity sha512-TU6RyZ/XaQCTWRLrdqZZtZqwxUVr6PDMfi6MlWNURZ7A6czanQqX4pFE1mdOUQR9FdPCsZ0UzL8jI/izZ+eBSQ== -hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5: - version "2.5.5" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" - integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== - -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#101685d3aff3b23ea213163f6e8e12f4f111e19f" - integrity sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw== - dependencies: - react-is "^16.7.0" - -hoist-non-react-statics@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" - integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== +hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0, hoist-non-react-statics@^2.5.5, hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" @@ -16156,10 +16157,10 @@ hpack.js@^2.1.6: readable-stream "^2.0.1" wbuf "^1.1.0" -html-element-map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.1.tgz#3c4fcb4874ebddfe4283b51c8994e7713782b592" - integrity sha512-BZSfdEm6n706/lBfXKWa4frZRZcT5k1cOusw95ijZsHlI+GdgY0v95h6IzO3iIDf2ROwq570YTwqNPqHcNMozw== +html-element-map@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.2.0.tgz#dfbb09efe882806af63d990cf6db37993f099f22" + integrity sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw== dependencies: array-filter "^1.0.0" @@ -16967,7 +16968,7 @@ invariant@2.2.4, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.3, invariant dependencies: loose-envify "^1.0.0" -invariant@^2.0.0, invariant@^2.2.1, invariant@^2.2.2: +invariant@^2.2.1, invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" integrity sha1-nh9WrArNtr8wMwbzOL47IErmA2A= @@ -17099,10 +17100,10 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-boolean-object@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" - integrity sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M= +is-boolean-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" + integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== is-buffer@^1.0.2, is-buffer@^1.1.4, is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.6" @@ -17121,10 +17122,10 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" -is-callable@^1.1.3, is-callable@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" - integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== is-ci@1.2.1: version "1.2.1" @@ -17374,10 +17375,10 @@ is-npm@^1.0.0: resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= -is-number-object@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" - integrity sha1-8mWrian0RQNO9q/xWo8AsA9VF5k= +is-number-object@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" + integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== is-number@^0.1.1: version "0.1.1" @@ -17514,12 +17515,12 @@ is-redirect@^1.0.0: resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= -is-regex@^1.0.3, is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== dependencies: - has "^1.0.1" + has "^1.0.3" is-regexp@^1.0.0: version "1.0.0" @@ -17587,10 +17588,10 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64" - integrity sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ= +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== is-subset@^0.1.1: version "0.1.1" @@ -17655,12 +17656,7 @@ is-whitespace-character@^1.0.0: resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.1.tgz#9ae0176f3282b65457a1992cdb084f8a5f833e3b" integrity sha1-muAXbzKCtlRXoZks2whPil+DPjs= -is-windows@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.1.tgz#310db70f742d259a16a369202b51af84233310d9" - integrity sha1-MQ23D3QtJZoWo2kgK1GvhCMzENk= - -is-windows@^1.0.2: +is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -19478,7 +19474,7 @@ locutus@^2.0.5: resolved "https://registry.yarnpkg.com/locutus/-/locutus-2.0.10.tgz#f903619466a98a4ab76e8b87a5854b55a743b917" integrity sha512-AZg2kCqrquMJ5FehDsBidV0qHl98NrsYtseUClzjAQ3HFnsDBJTCwGVplSQ82t9/QfgahqvTjaKcZqZkHmS0wQ== -lodash-es@^4.17.11, lodash-es@^4.17.5, lodash-es@^4.2.1: +lodash-es@^4.17.11, lodash-es@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -21844,7 +21840,7 @@ object-identity-map@^1.0.2: dependencies: object.entries "^1.1.0" -object-inspect@^1.6.0: +object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== @@ -21859,10 +21855,10 @@ object-inspect@~1.6.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== -object-is@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" - integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= +object-is@^1.0.1, object-is@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" + integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6, object-keys@^1.0.9, object-keys@^1.1.1: version "1.1.1" @@ -21908,13 +21904,13 @@ object.defaults@^1.0.0, object.defaults@^1.1.0: for-own "^1.0.0" isobject "^3.0.0" -object.entries@^1.0.4, object.entries@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" - integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== +object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" + integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== dependencies: define-properties "^1.1.3" - es-abstract "^1.12.0" + es-abstract "^1.17.0-next.1" function-bind "^1.1.1" has "^1.0.3" @@ -21938,6 +21934,16 @@ object.fromentries@^2.0.1: function-bind "^1.1.1" has "^1.0.3" +object.fromentries@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9" + integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + object.getownpropertydescriptors@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" @@ -21969,13 +21975,13 @@ object.reduce@^1.0.0: for-own "^1.0.0" make-iterator "^1.0.0" -object.values@^1.0.4, object.values@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" - integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== +object.values@^1.0.4, object.values@^1.1.0, object.values@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== dependencies: define-properties "^1.1.3" - es-abstract "^1.12.0" + es-abstract "^1.17.0-next.1" function-bind "^1.1.1" has "^1.0.3" @@ -23935,14 +23941,7 @@ raf-schd@^4.0.2: resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== -raf@^3.1.0, raf@^3.3.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" - integrity sha512-pDP/NMRAXoTfrhCfyfSEwJAKLaxBU9eApMeBPB1TkDouZmvPerIClV8lTAd+uF8ZiTaVl69e1FCxQrAd/VTjGw== - dependencies: - performance-now "^2.1.0" - -raf@^3.4.0: +raf@^3.1.0, raf@^3.3.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -24442,11 +24441,6 @@ react-intl@^2.8.0: intl-relativeformat "^2.1.0" invariant "^2.1.1" -react-is@^16.10.2, react-is@^16.9.0: - version "16.11.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa" - integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw== - react-is@^16.12.0, react-is@^16.3.1: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" @@ -24467,6 +24461,11 @@ react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.5.tgz#c54ac229dd66b5afe0de5acbe47647c3da692ff8" integrity sha512-sudt2uq5P/2TznPV4Wtdi+Lnq3yaYW8LfvPKLM9BKD8jJNBkxMVyB0C9/GmVhLw7Jbdmndk/73n7XQGeN9A3QQ== +react-is@^16.9.0: + version "16.11.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa" + integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw== + react-is@~16.3.0: version "16.3.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22" @@ -24625,19 +24624,7 @@ react-reconciler@^0.22.1: prop-types "^15.6.2" scheduler "^0.16.2" -react-redux@^5.0.7: - version "5.0.7" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8" - integrity sha512-5VI8EV5hdgNgyjfmWzBbdrqUkrVRKlyTKk1sGH3jzM2M2Mhj/seQgPXaz6gVAj2lz/nz688AdTqMO18Lr24Zhg== - dependencies: - hoist-non-react-statics "^2.5.0" - invariant "^2.0.0" - lodash "^4.17.5" - lodash-es "^4.17.5" - loose-envify "^1.1.0" - prop-types "^15.6.0" - -react-redux@^5.1.2: +react-redux@^5.0.7, react-redux@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== @@ -24650,7 +24637,7 @@ react-redux@^5.1.2: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react-redux@^7.1.0, react-redux@^7.1.1: +react-redux@^7.1.0, react-redux@^7.1.1, react-redux@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.3.tgz#717a3d7bbe3a1b2d535c94885ce04cdc5a33fc79" integrity sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w== @@ -25281,12 +25268,17 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" +reduce-reducers@*, reduce-reducers@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reduce-reducers/-/reduce-reducers-1.0.4.tgz#fb77e751a9eb0201760ac5a605ca8c9c2d0537f8" + integrity sha512-Mb2WZ2bJF597exiqX7owBzrqJ74DHLK3yOQjCyPAaNifRncE8OD0wFIuoMhXxTnHK07+8zZ2SJEKy/qtiyR7vw== + reduce-reducers@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/reduce-reducers/-/reduce-reducers-0.4.3.tgz#8e052618801cd8fc2714b4915adaa8937eb6d66c" integrity sha512-+CNMnI8QhgVMtAt54uQs3kUxC3Sybpa7Y63HR14uGLgI9/QR5ggHvpxwhGGe3wmx5V91YwqQIblN9k5lspAmGw== -redux-actions@2.6.5: +redux-actions@^2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/redux-actions/-/redux-actions-2.6.5.tgz#bdca548768ee99832a63910c276def85e821a27e" integrity sha512-pFhEcWFTYNk7DhQgxMGnbsB1H2glqhQJRQrtPb96kD3hWiZRzXHwwmFPswg6V2MjraXRXWNmuP9P84tvdLAJmw== @@ -25302,10 +25294,10 @@ redux-devtools-extension@^2.13.8: resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1" integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg== -redux-observable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redux-observable/-/redux-observable-1.0.0.tgz#780ff2455493eedcef806616fe286b454fd15d91" - integrity sha512-6bXnpqWTBeLaLQjXHyN1giXq4nLxCmv+SUkdmiwBgvmVxvDbdmydvL1Z7DGo0WItyzI/kqXQKiucUuTxnrPRkA== +redux-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redux-observable/-/redux-observable-1.2.0.tgz#ff51b6c6be2598e9b5e89fc36639186bb0e669c7" + integrity sha512-yeR90RP2WzZzCxxnQPlh2uFzyfFLsfXu8ROh53jGDPXVqj71uNDMmvi/YKQkd9ofiVoO4OYb1snbowO49tCEMg== redux-saga@^0.16.0: version "0.16.2" @@ -25317,7 +25309,7 @@ redux-thunk@2.2.0: resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.2.0.tgz#e615a16e16b47a19a515766133d1e3e99b7852e5" integrity sha1-5hWhbha0ehmlFXZhM9Hj6Zt4UuU= -redux-thunk@2.3.0: +redux-thunk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622" integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw== @@ -25337,7 +25329,7 @@ redux@3.7.2: loose-envify "^1.1.0" symbol-observable "^1.0.3" -redux@4.0.0, redux@^4.0.0: +redux@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03" integrity sha512-NnnHF0h0WVE/hXyrB6OlX67LYRuaf/rJcbWvnHHEPCF/Xa/AZpwhs/20WyqzQae5x4SD2F9nPObgBh2rxAgLiA== @@ -25361,6 +25353,14 @@ redux@^4.0.4: loose-envify "^1.4.0" symbol-observable "^1.2.0" +redux@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + reflect.ownkeys@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" @@ -25916,11 +25916,6 @@ requires-regex@^0.3.3: resolved "https://registry.yarnpkg.com/requires-regex/-/requires-regex-0.3.3.tgz#60309eaabbfd5ca8259e090b8b5a94b2144eb0dd" integrity sha1-YDCeqrv9XKglngkLi1qUshROsN0= -reselect@3.0.1, reselect@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147" - integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc= - reselect@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" @@ -26671,17 +26666,17 @@ semver-truncate@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= -"semver@2 || 3 || 4 || 5", semver@^5.3.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" - integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@5.5.0, semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.5.0: +semver@5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== -semver@5.7.0, semver@^5.4.1, semver@^5.6.0, semver@^5.7.0: +semver@5.7.0: version "5.7.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== @@ -27907,13 +27902,13 @@ string.prototype.padstart@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" -string.prototype.trim@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz#75a729b10cfc1be439543dae442129459ce61e3d" - integrity sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg== +string.prototype.trim@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782" + integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== dependencies: define-properties "^1.1.3" - es-abstract "^1.13.0" + es-abstract "^1.17.0-next.1" function-bind "^1.1.1" string.prototype.trim@~1.1.2: @@ -27925,18 +27920,18 @@ string.prototype.trim@~1.1.2: es-abstract "^1.5.0" function-bind "^1.0.2" -string.prototype.trimleft@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634" - integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw== +string.prototype.trimleft@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" + integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== dependencies: define-properties "^1.1.3" function-bind "^1.1.1" -string.prototype.trimright@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58" - integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg== +string.prototype.trimright@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" + integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== dependencies: define-properties "^1.1.3" function-bind "^1.1.1" @@ -29863,17 +29858,15 @@ typedarray@^0.0.6, typedarray@~0.0.5: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript-fsa-reducers@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/typescript-fsa-reducers/-/typescript-fsa-reducers-0.4.5.tgz#58fffb2f6eeca6817c2f656b7e7df2cb1c9d1f84" - integrity sha512-mBIpU4je365qpqp2XWKtNW3rGO/hA4OI+l8vkkXdHLUYukrp3wNeL+e3roUq1F6wa6Kcr0WaMblEQDsIdWHTEQ== - dependencies: - typescript-fsa "^2.0.0" +typescript-fsa-reducers@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/typescript-fsa-reducers/-/typescript-fsa-reducers-1.2.1.tgz#2af1a85f7b88fb0dfb9fa59d5da51a5d7ac6543f" + integrity sha512-Qgn7zEnAU5n3YEWEL5ooEmIWZl9B4QyXD4Y/0DqpUzF0YuTrcsLa7Lht0gFXZ+xqLJXQwo3fEiTfQTDF1fBnMg== -typescript-fsa@^2.0.0, typescript-fsa@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/typescript-fsa/-/typescript-fsa-2.5.0.tgz#1baec01b5e8f5f34c322679d1327016e9e294faf" - integrity sha1-G67AG16PXzTDImedEycBbp4pT68= +typescript-fsa@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/typescript-fsa/-/typescript-fsa-3.0.0.tgz#3ad1cb915a67338e013fc21f67c9b3e0e110c912" + integrity sha512-xiXAib35i0QHl/+wMobzPibjAH5TJLDj+qGq5jwVLG9qR4FUswZURBw2qihBm0m06tHoyb3FzpnJs1GRhRwVag== typescript@3.5.3, typescript@3.7.2, typescript@^3.0.1, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: version "3.7.2" From 84be2620751dce74f6272d6783a3a0d85e78d6ae Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Mon, 17 Feb 2020 10:59:07 +0100 Subject: [PATCH 23/65] [ML] New Platform server shim: update indices routes (#57685) * [ML] NP indices routes * [ML] fix error function * [ML] fix createAndOpenUrl function --- .../components/anomalies_table/links_menu.js | 70 +++++++++---------- .../plugins/ml/server/routes/apidoc.json | 4 +- .../plugins/ml/server/routes/indices.js | 28 -------- .../plugins/ml/server/routes/indices.ts | 49 +++++++++++++ 4 files changed, 87 insertions(+), 64 deletions(-) delete mode 100644 x-pack/legacy/plugins/ml/server/routes/indices.js create mode 100644 x-pack/legacy/plugins/ml/server/routes/indices.ts diff --git a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js index c16dc37097b13..f161e37efa8d8 100644 --- a/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/legacy/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -236,26 +236,26 @@ class LinksMenuUI extends Component { let i = 0; findFieldType(datafeedIndices[i]); - function findFieldType(index) { - getFieldTypeFromMapping(index, categorizationFieldName) - .then(resp => { - if (resp !== '') { - createAndOpenUrl(index, resp); - } else { - i++; - if (i < datafeedIndices.length) { - findFieldType(datafeedIndices[i]); - } else { - error(); - } - } + const error = () => { + console.log( + `viewExamples(): error finding type of field ${categorizationFieldName} in indices:`, + datafeedIndices + ); + const { toasts } = this.props.kibana.services.notifications; + toasts.addDanger( + i18n.translate('xpack.ml.anomaliesTable.linksMenu.noMappingCouldBeFoundErrorMessage', { + defaultMessage: + 'Unable to view examples of documents with mlcategory {categoryId} ' + + 'as no mapping could be found for the categorization field {categorizationFieldName}', + values: { + categoryId, + categorizationFieldName, + }, }) - .catch(() => { - error(); - }); - } + ); + }; - function createAndOpenUrl(index, categorizationFieldType) { + const createAndOpenUrl = (index, categorizationFieldType) => { // Find the ID of the index pattern with a title attribute which matches the // index configured in the datafeed. If a Kibana index pattern has not been created // for this index, then the user will see a warning message on the Discover tab advising @@ -340,25 +340,25 @@ class LinksMenuUI extends Component { }) ); }); - } + }; - function error() { - console.log( - `viewExamples(): error finding type of field ${categorizationFieldName} in indices:`, - datafeedIndices - ); - const { toasts } = this.props.kibana.services.notifications; - toasts.addDanger( - i18n.translate('xpack.ml.anomaliesTable.linksMenu.noMappingCouldBeFoundErrorMessage', { - defaultMessage: - 'Unable to view examples of documents with mlcategory {categoryId} ' + - 'as no mapping could be found for the categorization field {categorizationFieldName}', - values: { - categoryId, - categorizationFieldName, - }, + function findFieldType(index) { + getFieldTypeFromMapping(index, categorizationFieldName) + .then(resp => { + if (resp !== '') { + createAndOpenUrl(index, resp); + } else { + i++; + if (i < datafeedIndices.length) { + findFieldType(datafeedIndices[i]); + } else { + error(); + } + } }) - ); + .catch(() => { + error(); + }); } }; diff --git a/x-pack/legacy/plugins/ml/server/routes/apidoc.json b/x-pack/legacy/plugins/ml/server/routes/apidoc.json index be1554bf55f78..35215f8008ec3 100644 --- a/x-pack/legacy/plugins/ml/server/routes/apidoc.json +++ b/x-pack/legacy/plugins/ml/server/routes/apidoc.json @@ -76,6 +76,8 @@ "CreateFilter", "UpdateFilter", "DeleteFilter", - "GetFiltersStats" + "GetFiltersStats", + "Indices", + "FieldCaps" ] } diff --git a/x-pack/legacy/plugins/ml/server/routes/indices.js b/x-pack/legacy/plugins/ml/server/routes/indices.js deleted file mode 100644 index 309b41e53eef5..0000000000000 --- a/x-pack/legacy/plugins/ml/server/routes/indices.js +++ /dev/null @@ -1,28 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { callWithRequestFactory } from '../client/call_with_request_factory'; -import { wrapError } from '../client/errors'; - -export function indicesRoutes({ commonRouteConfig, elasticsearchPlugin, route }) { - route({ - method: 'POST', - path: '/api/ml/indices/field_caps', - handler(request) { - const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request); - const index = request.payload.index; - let fields = '*'; - if (request.payload.fields !== undefined && Array.isArray(request.payload.fields)) { - fields = request.payload.fields.join(','); - } - - return callWithRequest('fieldCaps', { index, fields }).catch(resp => wrapError(resp)); - }, - config: { - ...commonRouteConfig, - }, - }); -} diff --git a/x-pack/legacy/plugins/ml/server/routes/indices.ts b/x-pack/legacy/plugins/ml/server/routes/indices.ts new file mode 100644 index 0000000000000..0ee15f1321e9c --- /dev/null +++ b/x-pack/legacy/plugins/ml/server/routes/indices.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { wrapError } from '../client/error_wrapper'; +import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; +import { RouteInitialization } from '../new_platform/plugin'; + +/** + * Indices routes. + */ +export function indicesRoutes({ xpackMainPlugin, router }: RouteInitialization) { + /** + * @apiGroup Indices + * + * @api {post} /api/ml/indices/field_caps + * @apiName FieldCaps + * @apiDescription Retrieves the capabilities of fields among multiple indices. + */ + router.post( + { + path: '/api/ml/indices/field_caps', + validate: { + body: schema.object({ + index: schema.maybe(schema.string()), + fields: schema.maybe(schema.arrayOf(schema.string())), + }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const { + body: { index, fields: requestFields }, + } = request; + const fields = + requestFields !== undefined && Array.isArray(requestFields) + ? requestFields.join(',') + : '*'; + const result = await context.ml!.mlClient.callAsCurrentUser('fieldCaps', { index, fields }); + return response.ok({ body: result }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); +} From 26fdc4a6b373dac5ec110a34ef0472046dc438b7 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 17 Feb 2020 11:03:57 +0100 Subject: [PATCH 24/65] [Upgrade Assistant] Fix filter deprecations search filter (#57541) * Made eui search field not a controlled component Added validateRegExpString util * Update error message display. Use EuiCallOut and i18n to replicate other search filter behaviour, e.g. index management. * Remove unused variable * Update Jest snapshot * Updated layout for callout The previous callout layout looked off-center next to the controls in the table. * Update copy and remove intl Update "Filter Invalid:" to sentence case Remove inject intl wrapper from CheckupControls component Remove unnecessary grow={true} * Updated Jest component snapshot Co-authored-by: Elastic Machine --- .../__snapshots__/checkup_tab.test.tsx.snap | 3 +- .../components/tabs/checkup/checkup_tab.tsx | 3 +- .../components/tabs/checkup/controls.tsx | 121 ++++++++++++------ .../public/np_ready/application/utils.test.ts | 20 +++ .../public/np_ready/application/utils.ts | 19 +++ 5 files changed, 121 insertions(+), 45 deletions(-) create mode 100644 x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.test.ts create mode 100644 x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.ts diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap index eb76498b55f4c..da1760d3773d4 100644 --- a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/components/tabs/checkup/__snapshots__/checkup_tab.test.tsx.snap @@ -57,7 +57,7 @@ exports[`CheckupTab render with deprecations 1`] = ` - @@ -143,7 +143,6 @@ export class CheckupTab extends UpgradeAssistantTabComponent void; currentFilter: LevelFilterOption; onFilterChange: (filter: LevelFilterOption) => void; - search: string; onSearchChange: (filter: string) => void; availableGroupByOptions: GroupByOption[]; currentGroupBy: GroupByOption; onGroupByChange: (groupBy: GroupByOption) => void; } -export const CheckupControlsUI: FunctionComponent = ({ +export const CheckupControls: FunctionComponent = ({ allDeprecations, loadingState, loadData, currentFilter, onFilterChange, - search, onSearchChange, availableGroupByOptions, currentGroupBy, onGroupByChange, - intl, -}) => ( - - - onSearchChange(e.target.value)} - /> - - - {/* These two components provide their own EuiFlexItem wrappers */} - - +}) => { + const [searchTermError, setSearchTermError] = useState(null); + const filterInvalid = Boolean(searchTermError); + return ( + + + + + { + const string = e.target.value; + const errorMessage = validateRegExpString(string); + if (errorMessage) { + // Emit an empty search term to listeners if search term is invalid. + onSearchChange(''); + setSearchTermError(errorMessage); + } else { + onSearchChange(e.target.value); + if (searchTermError) { + setSearchTermError(null); + } + } + }} + /> + - - - - - - -); + {/* These two components provide their own EuiFlexItem wrappers */} + + -export const CheckupControls = injectI18n(CheckupControlsUI); + + + + + + + + {filterInvalid && ( + + + + )} + + ); +}; diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.test.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.test.ts new file mode 100644 index 0000000000000..067f11798e151 --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.test.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { validateRegExpString } from './utils'; + +describe('validRegExpString', () => { + it('correctly returns false for invalid strings', () => { + expect(validateRegExpString('?asd')).toContain(`Invalid regular expression`); + expect(validateRegExpString('*asd')).toContain(`Invalid regular expression`); + expect(validateRegExpString('(')).toContain(`Invalid regular expression`); + }); + + it('correctly returns true for valid strings', () => { + expect(validateRegExpString('asd')).toBe(''); + expect(validateRegExpString('.*asd')).toBe(''); + expect(validateRegExpString('')).toBe(''); + }); +}); diff --git a/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.ts b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.ts new file mode 100644 index 0000000000000..6a1f32fe8f20b --- /dev/null +++ b/x-pack/legacy/plugins/upgrade_assistant/public/np_ready/application/utils.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import { pipe } from 'fp-ts/lib/pipeable'; +import { tryCatch, fold } from 'fp-ts/lib/Either'; + +export const validateRegExpString = (s: string) => + pipe( + tryCatch( + () => new RegExp(s), + e => (e as Error).message + ), + fold( + (errorMessage: string) => errorMessage, + () => '' + ) + ); From 3d7bae4ed24b8b5777ee217f304a1203e5753880 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Mon, 17 Feb 2020 11:06:20 +0100 Subject: [PATCH 25/65] Move Ace XJSON lexer-rules, worker and utils to es_ui_shared (#57563) * Move mode and lexer rules to shared space * Update searchprofiler mode rules * Moved x-json worker out of searchprofiler and into es_ui_shared (for use in Watcher) Renamed ace/mode -> ace/modes * Moved collapse and expand literal string functions to es_ui_shared * Fix some imports Enable Watcher editor to parse XJson using XJsonMode * Fix imports Import JSON highlight rules in XJSONHighlight rules * Move console_lang, fix Jest tests Exporting console_lang through the es_ui_shared/public barrel caused the XJsonMode to imported to a index_management too and any other plugin that may import something from that directory. console_lang was moved to it's own top level directory es_ui_shared/console_lang. We also included a mock for tests using XJsonMode to import from console_lang. * Fixed OSS Jest tests Console Jest tests were still failing because they did not mock out the raw-loader imported worker file * Expand triple quotes in editor Upon entering the advanced watcher creation view, the JSON should be scanned for triple quote expansion * Bring all editors themes in line Editors had github theme, which diverged from the textmate theme used in Console and SearchProfiler * Added XJSON mode to simulate alternative input editor Slight refactor to the logic for using XJSON mode. Created an adhoc hook for wrapping the logic of useState and moved lanugage util imports there too to reduce the number of imports in both the watcher form and simulate tabs in advanced creation. * Moved x-json worker to x-pack x-json worker is currently only used inside of x-pack. Also testing for if this will fix the CI/prod build Co-authored-by: Elastic Machine --- .../legacy/console_editor/editor_output.tsx | 4 +- .../send_request_to_es.ts | 9 +- .../models/legacy_core_editor/mode/input.js | 2 +- .../mode/input_highlight_rules.js | 4 +- .../mode/output_highlight_rules.js | 4 +- .../models/legacy_core_editor/mode/script.js | 19 +- .../legacy_core_editor/mode/worker/index.d.ts | 20 ++ .../__tests__/sense_editor.test.js | 4 +- .../models/sense_editor/sense_editor.ts | 6 +- .../ace_token_provider/token_provider.test.ts | 1 - .../public/lib/autocomplete/autocomplete.ts | 2 +- .../public/lib/utils/__tests__/utils.test.js | 179 +++++++----------- .../public/lib/utils/{utils.ts => index.ts} | 57 +----- .../console_lang/ace/modes/index.d.ts | 32 ++++ .../console_lang/ace/modes/index.js | 27 +++ .../elasticsearch_sql_highlight_rules.ts | 0 .../ace/modes/lexer_rules/index.js | 22 +++ .../lexer_rules}/script_highlight_rules.js | 0 .../lexer_rules}/x_json_highlight_rules.js | 31 ++- .../console_lang/ace/modes/x_json/index.ts | 20 ++ .../ace/modes/x_json/x_json_mode.ts | 40 ++++ .../es_ui_shared/console_lang/index.ts | 29 +++ .../es_ui_shared/console_lang/lib/index.ts | 20 ++ .../json_xjson_translation_tools.test.ts | 65 +++++++ .../__tests__/utils_string_collapsing.txt | 0 .../__tests__/utils_string_expanding.txt | 0 .../lib/json_xjson_translation_tools/index.ts | 71 +++++++ src/plugins/es_ui_shared/public/index.ts | 2 +- .../console_lang/ace/modes/index.ts | 7 + .../console_lang/ace/modes/x_json/index.ts | 7 + .../ace/modes/x_json}/worker/index.ts | 0 .../ace/modes/x_json}/worker/worker.d.ts | 0 .../ace/modes/x_json}/worker/worker.js | 0 .../console_lang/ace/modes/x_json/x_json.ts | 34 ++++ .../es_ui_shared/console_lang/index.ts | 7 + .../es_ui_shared/console_lang/mocks.ts | 9 + .../application/containers/main/main.tsx | 1 - .../public/application/editor/editor.test.tsx | 4 +- .../public/application/editor/init_editor.ts | 2 +- .../editor/x_json_highlight_rules.ts | 139 -------------- .../public/application/editor/x_json_mode.ts | 54 ------ .../utils/check_for_json_errors.ts | 7 +- .../watch_create_json.test.ts | 2 + .../watch_create_threshold.test.tsx | 3 + .../client_integration/watch_edit.test.ts | 2 + .../client_integration/watch_list.test.ts | 1 + .../client_integration/watch_status.test.ts | 2 + .../json_watch_edit/json_watch_edit_form.tsx | 16 +- .../json_watch_edit_simulate.tsx | 15 +- .../json_watch_edit/use_x_json_mode.ts | 24 +++ .../action_fields/webhook_action_fields.tsx | 2 +- 51 files changed, 591 insertions(+), 417 deletions(-) create mode 100644 src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts rename src/plugins/console/public/lib/utils/{utils.ts => index.ts} (59%) create mode 100644 src/plugins/es_ui_shared/console_lang/ace/modes/index.d.ts create mode 100644 src/plugins/es_ui_shared/console_lang/ace/modes/index.js rename src/plugins/{console/public/application/models/legacy_core_editor/mode => es_ui_shared/console_lang/ace/modes/lexer_rules}/elasticsearch_sql_highlight_rules.ts (100%) create mode 100644 src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/index.js rename src/plugins/{console/public/application/models/legacy_core_editor/mode => es_ui_shared/console_lang/ace/modes/lexer_rules}/script_highlight_rules.js (100%) rename src/plugins/{console/public/application/models/legacy_core_editor/mode => es_ui_shared/console_lang/ace/modes/lexer_rules}/x_json_highlight_rules.js (85%) create mode 100644 src/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts create mode 100644 src/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json_mode.ts create mode 100644 src/plugins/es_ui_shared/console_lang/index.ts create mode 100644 src/plugins/es_ui_shared/console_lang/lib/index.ts create mode 100644 src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts rename src/plugins/{console/public/lib/utils => es_ui_shared/console_lang/lib/json_xjson_translation_tools}/__tests__/utils_string_collapsing.txt (100%) rename src/plugins/{console/public/lib/utils => es_ui_shared/console_lang/lib/json_xjson_translation_tools}/__tests__/utils_string_expanding.txt (100%) create mode 100644 src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/index.ts create mode 100644 x-pack/plugins/es_ui_shared/console_lang/ace/modes/index.ts create mode 100644 x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts rename x-pack/plugins/{searchprofiler/public/application/editor => es_ui_shared/console_lang/ace/modes/x_json}/worker/index.ts (100%) rename x-pack/plugins/{searchprofiler/public/application/editor => es_ui_shared/console_lang/ace/modes/x_json}/worker/worker.d.ts (100%) rename x-pack/plugins/{searchprofiler/public/application/editor => es_ui_shared/console_lang/ace/modes/x_json}/worker/worker.js (100%) create mode 100644 x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json.ts create mode 100644 x-pack/plugins/es_ui_shared/console_lang/index.ts create mode 100644 x-pack/plugins/es_ui_shared/console_lang/mocks.ts delete mode 100644 x-pack/plugins/searchprofiler/public/application/editor/x_json_highlight_rules.ts delete mode 100644 x-pack/plugins/searchprofiler/public/application/editor/x_json_mode.ts create mode 100644 x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/use_x_json_mode.ts diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx index e232e41902cbb..ca468f85275a8 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor_output.tsx @@ -26,7 +26,7 @@ import { useRequestReadContext, } from '../../../../contexts'; -import * as utils from '../../../../../lib/utils/utils'; +import { expandLiteralStrings } from '../../../../../../../es_ui_shared/console_lang/lib'; import { subscribeResizeChecker } from '../subscribe_console_resize_checker'; import { applyCurrentSettings } from './apply_editor_settings'; @@ -67,7 +67,7 @@ function EditorOutputUI() { editor.update( data .map(d => d.response.value as string) - .map(readOnlySettings.tripleQuotes ? utils.expandLiteralStrings : a => a) + .map(readOnlySettings.tripleQuotes ? expandLiteralStrings : a => a) .join('\n') ); } else if (error) { diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts index 35d9865ee4ccb..102f90a9feb6f 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/send_request_to_es.ts @@ -17,10 +17,11 @@ * under the License. */ -import * as utils from '../../../lib/utils/utils'; +import { extractDeprecationMessages } from '../../../lib/utils'; +import { collapseLiteralStrings } from '../../../../../es_ui_shared/console_lang/lib'; // @ts-ignore import * as es from '../../../lib/es/es'; -import { BaseResponseType } from '../../../types/common'; +import { BaseResponseType } from '../../../types'; export interface EsRequestArgs { requests: any; @@ -73,7 +74,7 @@ export function sendRequestToES(args: EsRequestArgs): Promise const req = requests.shift(); const esPath = req.url; const esMethod = req.method; - let esData = utils.collapseLiteralStrings(req.data.join('\n')); + let esData = collapseLiteralStrings(req.data.join('\n')); if (esData) { esData += '\n'; } // append a new line for bulk requests. @@ -97,7 +98,7 @@ export function sendRequestToES(args: EsRequestArgs): Promise const warnings = xhr.getResponseHeader('warning'); if (warnings) { - const deprecationMessages = utils.extractDeprecationMessages(warnings); + const deprecationMessages = extractDeprecationMessages(warnings); value = deprecationMessages.join('\n') + '\n' + value; } diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js index da101b61f8035..d763db7ae5d79 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/input.js @@ -18,6 +18,7 @@ */ import ace from 'brace'; +import { workerModule } from './worker'; const oop = ace.acequire('ace/lib/oop'); const TextMode = ace.acequire('ace/mode/text').Mode; @@ -29,7 +30,6 @@ const WorkerClient = ace.acequire('ace/worker/worker_client').WorkerClient; const AceTokenizer = ace.acequire('ace/tokenizer').Tokenizer; const HighlightRules = require('./input_highlight_rules').InputHighlightRules; -import { workerModule } from './worker'; export function Mode() { this.$tokenizer = new AceTokenizer(new HighlightRules().getRules()); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js index 842736428e8bb..2c1b30f806f95 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/input_highlight_rules.js @@ -18,7 +18,7 @@ */ const ace = require('brace'); -import { addToRules } from './x_json_highlight_rules'; +import { addXJsonToRules } from '../../../../../../es_ui_shared/console_lang'; export function addEOL(tokens, reg, nextIfEOL, normalNext) { if (typeof reg === 'object') { @@ -101,7 +101,7 @@ export function InputHighlightRules() { ), }; - addToRules(this); + addXJsonToRules(this); if (this.constructor === InputHighlightRules) { this.normalizeRules(); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js index 920bdff1798db..e27222ebd65e9 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/output_highlight_rules.js @@ -19,7 +19,7 @@ const ace = require('brace'); import 'brace/mode/json'; -import { addToRules } from './x_json_highlight_rules'; +import { addXJsonToRules } from '../../../../../../es_ui_shared/console_lang'; const oop = ace.acequire('ace/lib/oop'); const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules').JsonHighlightRules; @@ -27,7 +27,7 @@ const JsonHighlightRules = ace.acequire('ace/mode/json_highlight_rules').JsonHig export function OutputJsonHighlightRules() { this.$rules = {}; - addToRules(this, 'start'); + addXJsonToRules(this, 'start'); this.$rules.start.unshift( { diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js b/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js index b6b74c6377233..13ae329380221 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/script.js @@ -18,17 +18,15 @@ */ import ace from 'brace'; +import { ScriptHighlightRules } from '../../../../../../es_ui_shared/console_lang'; const oop = ace.acequire('ace/lib/oop'); const TextMode = ace.acequire('ace/mode/text').Mode; const MatchingBraceOutdent = ace.acequire('ace/mode/matching_brace_outdent').MatchingBraceOutdent; const CstyleBehaviour = ace.acequire('ace/mode/behaviour/cstyle').CstyleBehaviour; const CStyleFoldMode = ace.acequire('ace/mode/folding/cstyle').FoldMode; -//const WorkerClient = ace.acequire('ace/worker/worker_client').WorkerClient; ace.acequire('ace/tokenizer'); -const ScriptHighlightRules = require('./script_highlight_rules').ScriptHighlightRules; - export function ScriptMode() { this.$outdent = new MatchingBraceOutdent(); this.$behaviour = new CstyleBehaviour(); @@ -57,19 +55,4 @@ oop.inherits(ScriptMode, TextMode); this.autoOutdent = function(state, doc, row) { this.$outdent.autoOutdent(doc, row); }; - - // this.createWorker = function (session) { - // const worker = new WorkerClient(['ace', 'sense_editor'], 'sense_editor/mode/worker', 'SenseWorker', 'sense_editor/mode/worker'); - // worker.attachToDocument(session.getDocument()); - - // worker.on('error', function (e) { - // session.setAnnotations([e.data]); - // }); - - // worker.on('ok', function (anno) { - // session.setAnnotations(anno.data); - // }); - - // return worker; - // }; }.call(ScriptMode.prototype)); diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts b/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts new file mode 100644 index 0000000000000..c7ceb6a95b896 --- /dev/null +++ b/src/plugins/console/public/application/models/legacy_core_editor/mode/worker/index.d.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export declare const workerModule: { id: string; src: string }; diff --git a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js index a66bc20685df7..63f97345bc9ff 100644 --- a/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js +++ b/src/plugins/console/public/application/models/sense_editor/__tests__/sense_editor.test.js @@ -22,8 +22,8 @@ import $ from 'jquery'; import _ from 'lodash'; import { create } from '../create'; +import { collapseLiteralStrings } from '../../../../../../es_ui_shared/console_lang/lib'; const editorInput1 = require('./editor_input1.txt'); -const utils = require('../../../../lib/utils/utils'); describe('Editor', () => { let input; @@ -331,7 +331,7 @@ describe('Editor', () => { const expected = { method: 'POST', url: '_search', - data: [utils.collapseLiteralStrings(simpleRequest.data)], + data: [collapseLiteralStrings(simpleRequest.data)], }; compareRequest(request, expected); diff --git a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts index 1271f167c6cc1..f559f5dfcd707 100644 --- a/src/plugins/console/public/application/models/sense_editor/sense_editor.ts +++ b/src/plugins/console/public/application/models/sense_editor/sense_editor.ts @@ -19,7 +19,9 @@ import _ from 'lodash'; import RowParser from '../../../lib/row_parser'; -import * as utils from '../../../lib/utils/utils'; +import { collapseLiteralStrings } from '../../../../../es_ui_shared/console_lang/lib'; +import * as utils from '../../../lib/utils'; + // @ts-ignore import * as es from '../../../lib/es/es'; @@ -480,7 +482,7 @@ export class SenseEditor { let ret = 'curl -X' + esMethod + ' "' + url + '"'; if (esData && esData.length) { ret += " -H 'Content-Type: application/json' -d'\n"; - const dataAsString = utils.collapseLiteralStrings(esData.join('\n')); + const dataAsString = collapseLiteralStrings(esData.join('\n')); // since Sense doesn't allow single quote json string any single qoute is within a string. ret += dataAsString.replace(/'/g, '\\"'); if (esData.length > 1) { diff --git a/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts b/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts index 00bfe32c85906..d5465ebe96514 100644 --- a/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts +++ b/src/plugins/console/public/lib/ace_token_provider/token_provider.test.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ - import '../../application/models/sense_editor/sense_editor.test.mocks'; import $ from 'jquery'; diff --git a/src/plugins/console/public/lib/autocomplete/autocomplete.ts b/src/plugins/console/public/lib/autocomplete/autocomplete.ts index ac8fa1ea48caa..e09024ccfc859 100644 --- a/src/plugins/console/public/lib/autocomplete/autocomplete.ts +++ b/src/plugins/console/public/lib/autocomplete/autocomplete.ts @@ -29,7 +29,7 @@ import { // @ts-ignore } from '../kb/kb'; -import * as utils from '../utils/utils'; +import * as utils from '../utils'; // @ts-ignore import { populateContext } from './engine'; diff --git a/src/plugins/console/public/lib/utils/__tests__/utils.test.js b/src/plugins/console/public/lib/utils/__tests__/utils.test.js index f6828f354a1bc..6115be3c84ed9 100644 --- a/src/plugins/console/public/lib/utils/__tests__/utils.test.js +++ b/src/plugins/console/public/lib/utils/__tests__/utils.test.js @@ -17,125 +17,80 @@ * under the License. */ -const _ = require('lodash'); -const utils = require('../utils'); -const collapsingTests = require('./utils_string_collapsing.txt'); -const expandingTests = require('./utils_string_expanding.txt'); +const utils = require('../'); describe('Utils class', () => { - describe('collapseLiteralStrings', () => { - it('will collapse multiline strings', () => { - const multiline = '{ "foo": """bar\nbaz""" }'; - expect(utils.collapseLiteralStrings(multiline)).toEqual('{ "foo": "bar\\nbaz" }'); - }); + test('extract deprecation messages', function() { + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT"' + ) + ).toEqual(['#! Deprecation: this is a warning']); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning"' + ) + ).toEqual(['#! Deprecation: this is a warning']); - it('will collapse multiline strings with CRLF endings', () => { - const multiline = '{ "foo": """bar\r\nbaz""" }'; - expect(utils.collapseLiteralStrings(multiline)).toEqual('{ "foo": "bar\\r\\nbaz" }'); - }); - }); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning" "Mon, 27 Feb 2017 14:52:14 GMT"' + ) + ).toEqual(['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning"' + ) + ).toEqual(['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']); - _.each(collapsingTests.split(/^=+$/m), function(fixture) { - if (fixture.trim() === '') { - return; - } - fixture = fixture.split(/^-+$/m); - const name = fixture[0].trim(); - const expanded = fixture[1].trim(); - const collapsed = fixture[2].trim(); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma" "Mon, 27 Feb 2017 14:52:14 GMT"' + ) + ).toEqual(['#! Deprecation: this is a warning, and it includes a comma']); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma"' + ) + ).toEqual(['#! Deprecation: this is a warning, and it includes a comma']); - test('Literal collapse - ' + name, function() { - expect(utils.collapseLiteralStrings(expanded)).toEqual(collapsed); - }); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\"escaped quotes\\"" "Mon, 27 Feb 2017 14:52:14 GMT"' + ) + ).toEqual([ + '#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', + ]); + expect( + utils.extractDeprecationMessages( + '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\"escaped quotes\\""' + ) + ).toEqual([ + '#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', + ]); }); - _.each(expandingTests.split(/^=+$/m), function(fixture) { - if (fixture.trim() === '') { - return; - } - fixture = fixture.split(/^-+$/m); - const name = fixture[0].trim(); - const collapsed = fixture[1].trim(); - const expanded = fixture[2].trim(); - - test('Literal expand - ' + name, function() { - expect(utils.expandLiteralStrings(collapsed)).toEqual(expanded); - }); - - test('extract deprecation messages', function() { - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT"' - ) - ).toEqual(['#! Deprecation: this is a warning']); - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning"' - ) - ).toEqual(['#! Deprecation: this is a warning']); - - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning" "Mon, 27 Feb 2017 14:52:14 GMT", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning" "Mon, 27 Feb 2017 14:52:14 GMT"' - ) - ).toEqual(['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']); - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning", 299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a second warning"' - ) - ).toEqual(['#! Deprecation: this is a warning', '#! Deprecation: this is a second warning']); - - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma" "Mon, 27 Feb 2017 14:52:14 GMT"' - ) - ).toEqual(['#! Deprecation: this is a warning, and it includes a comma']); - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes a comma"' - ) - ).toEqual(['#! Deprecation: this is a warning, and it includes a comma']); - - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\"escaped quotes\\"" "Mon, 27 Feb 2017 14:52:14 GMT"' - ) - ).toEqual([ - '#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', - ]); - expect( - utils.extractDeprecationMessages( - '299 Elasticsearch-6.0.0-alpha1-SNAPSHOT-abcdef1 "this is a warning, and it includes an escaped backslash \\\\ and a pair of \\"escaped quotes\\""' - ) - ).toEqual([ - '#! Deprecation: this is a warning, and it includes an escaped backslash \\ and a pair of "escaped quotes"', - ]); - }); - - test('unescape', function() { - expect(utils.unescape('escaped backslash \\\\')).toEqual('escaped backslash \\'); - expect(utils.unescape('a pair of \\"escaped quotes\\"')).toEqual( - 'a pair of "escaped quotes"' - ); - expect(utils.unescape('escaped quotes do not have to come in pairs: \\"')).toEqual( - 'escaped quotes do not have to come in pairs: "' - ); - }); + test('unescape', function() { + expect(utils.unescape('escaped backslash \\\\')).toEqual('escaped backslash \\'); + expect(utils.unescape('a pair of \\"escaped quotes\\"')).toEqual('a pair of "escaped quotes"'); + expect(utils.unescape('escaped quotes do not have to come in pairs: \\"')).toEqual( + 'escaped quotes do not have to come in pairs: "' + ); + }); - test('split on unquoted comma followed by space', function() { - expect(utils.splitOnUnquotedCommaSpace('a, b')).toEqual(['a', 'b']); - expect(utils.splitOnUnquotedCommaSpace('a,b, c')).toEqual(['a,b', 'c']); - expect(utils.splitOnUnquotedCommaSpace('"a, b"')).toEqual(['"a, b"']); - expect(utils.splitOnUnquotedCommaSpace('"a, b", c')).toEqual(['"a, b"', 'c']); - expect(utils.splitOnUnquotedCommaSpace('"a, b\\", c"')).toEqual(['"a, b\\", c"']); - expect(utils.splitOnUnquotedCommaSpace(', a, b')).toEqual(['', 'a', 'b']); - expect(utils.splitOnUnquotedCommaSpace('a, b, ')).toEqual(['a', 'b', '']); - expect(utils.splitOnUnquotedCommaSpace('\\"a, b", "c, d\\", e", f"')).toEqual([ - '\\"a', - 'b", "c', - 'd\\"', - 'e", f"', - ]); - }); + test('split on unquoted comma followed by space', function() { + expect(utils.splitOnUnquotedCommaSpace('a, b')).toEqual(['a', 'b']); + expect(utils.splitOnUnquotedCommaSpace('a,b, c')).toEqual(['a,b', 'c']); + expect(utils.splitOnUnquotedCommaSpace('"a, b"')).toEqual(['"a, b"']); + expect(utils.splitOnUnquotedCommaSpace('"a, b", c')).toEqual(['"a, b"', 'c']); + expect(utils.splitOnUnquotedCommaSpace('"a, b\\", c"')).toEqual(['"a, b\\", c"']); + expect(utils.splitOnUnquotedCommaSpace(', a, b')).toEqual(['', 'a', 'b']); + expect(utils.splitOnUnquotedCommaSpace('a, b, ')).toEqual(['a', 'b', '']); + expect(utils.splitOnUnquotedCommaSpace('\\"a, b", "c, d\\", e", f"')).toEqual([ + '\\"a', + 'b", "c', + 'd\\"', + 'e", f"', + ]); }); }); diff --git a/src/plugins/console/public/lib/utils/utils.ts b/src/plugins/console/public/lib/utils/index.ts similarity index 59% rename from src/plugins/console/public/lib/utils/utils.ts rename to src/plugins/console/public/lib/utils/index.ts index 0b10938abe704..f66c952dd3af7 100644 --- a/src/plugins/console/public/lib/utils/utils.ts +++ b/src/plugins/console/public/lib/utils/index.ts @@ -18,6 +18,10 @@ */ import _ from 'lodash'; +import { + expandLiteralStrings, + collapseLiteralStrings, +} from '../../../../es_ui_shared/console_lang/lib'; export function textFromRequest(request: any) { let data = request.data; @@ -56,59 +60,6 @@ export function formatRequestBodyDoc(data: string[], indent: boolean) { }; } -export function collapseLiteralStrings(data: any) { - const splitData = data.split(`"""`); - for (let idx = 1; idx < splitData.length - 1; idx += 2) { - splitData[idx] = JSON.stringify(splitData[idx]); - } - return splitData.join(''); -} - -/* - The following regex describes global match on: - 1. one colon followed by any number of space characters - 2. one double quote (not escaped, special case for JSON in JSON). - 3. greedily match any non double quote and non newline char OR any escaped double quote char (non-capturing). - 4. handle a special case where an escaped slash may be the last character - 5. one double quote - - For instance: `: "some characters \" here"` - Will match and be expanded to: `"""some characters " here"""` - - */ - -const LITERAL_STRING_CANDIDATES = /((:[\s\r\n]*)([^\\])"(\\"|[^"\n])*\\?")/g; - -export function expandLiteralStrings(data: string) { - return data.replace(LITERAL_STRING_CANDIDATES, (match, string) => { - // Expand to triple quotes if there are _any_ slashes - if (string.match(/\\./)) { - const firstDoubleQuoteIdx = string.indexOf('"'); - const lastDoubleQuoteIdx = string.lastIndexOf('"'); - - // Handle a special case where we may have a value like "\"test\"". We don't - // want to expand this to """"test"""" - so we terminate before processing the string - // further if we detect this either at the start or end of the double quote section. - - if (string[firstDoubleQuoteIdx + 1] === '\\' && string[firstDoubleQuoteIdx + 2] === '"') { - return string; - } - - if (string[lastDoubleQuoteIdx - 1] === '"' && string[lastDoubleQuoteIdx - 2] === '\\') { - return string; - } - - const colonAndAnySpacing = string.slice(0, firstDoubleQuoteIdx); - const rawStringifiedValue = string.slice(firstDoubleQuoteIdx, string.length); - // Remove one level of JSON stringification - const jsonValue = JSON.parse(rawStringifiedValue); - return `${colonAndAnySpacing}"""${jsonValue}"""`; - } else { - return string; - } - }); -} - export function extractDeprecationMessages(warnings: string) { // pattern for valid warning header const re = /\d{3} [0-9a-zA-Z!#$%&'*+-.^_`|~]+ \"((?:\t| |!|[\x23-\x5b]|[\x5d-\x7e]|[\x80-\xff]|\\\\|\\")*)\"(?: \"[^"]*\")?/; diff --git a/src/plugins/es_ui_shared/console_lang/ace/modes/index.d.ts b/src/plugins/es_ui_shared/console_lang/ace/modes/index.d.ts new file mode 100644 index 0000000000000..06c9f9a51ea68 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/ace/modes/index.d.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Editor } from 'brace'; + +export declare const ElasticsearchSqlHighlightRules: FunctionConstructor; +export declare const ScriptHighlightRules: FunctionConstructor; +export declare const XJsonHighlightRules: FunctionConstructor; + +export declare const XJsonMode: FunctionConstructor; + +/** + * @param otherRules Another Ace ruleset + * @param embedUnder The state name under which the rules will be embedded. Defaults to "json". + */ +export declare const addXJsonToRules: (otherRules: any, embedUnder?: string) => void; diff --git a/src/plugins/es_ui_shared/console_lang/ace/modes/index.js b/src/plugins/es_ui_shared/console_lang/ace/modes/index.js new file mode 100644 index 0000000000000..955f23116f659 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/ace/modes/index.js @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { + ElasticsearchSqlHighlightRules, + ScriptHighlightRules, + XJsonHighlightRules, + addXJsonToRules, +} from './lexer_rules'; + +export { XJsonMode, installXJsonMode } from './x_json'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/elasticsearch_sql_highlight_rules.ts b/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/elasticsearch_sql_highlight_rules.ts similarity index 100% rename from src/plugins/console/public/application/models/legacy_core_editor/mode/elasticsearch_sql_highlight_rules.ts rename to src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/elasticsearch_sql_highlight_rules.ts diff --git a/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/index.js b/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/index.js new file mode 100644 index 0000000000000..be11fd726b7f2 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/index.js @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { ElasticsearchSqlHighlightRules } from './elasticsearch_sql_highlight_rules'; +export { ScriptHighlightRules } from './script_highlight_rules'; +export { XJsonHighlightRules, addToRules as addXJsonToRules } from './x_json_highlight_rules'; diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/script_highlight_rules.js b/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/script_highlight_rules.js similarity index 100% rename from src/plugins/console/public/application/models/legacy_core_editor/mode/script_highlight_rules.js rename to src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/script_highlight_rules.js diff --git a/src/plugins/console/public/application/models/legacy_core_editor/mode/x_json_highlight_rules.js b/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/x_json_highlight_rules.js similarity index 85% rename from src/plugins/console/public/application/models/legacy_core_editor/mode/x_json_highlight_rules.js rename to src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/x_json_highlight_rules.js index d0a79e84e809d..14323b9320330 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/mode/x_json_highlight_rules.js +++ b/src/plugins/es_ui_shared/console_lang/ace/modes/lexer_rules/x_json_highlight_rules.js @@ -17,11 +17,16 @@ * under the License. */ -const _ = require('lodash'); +import * as _ from 'lodash'; +import ace from 'brace'; +import 'brace/mode/json'; import { ElasticsearchSqlHighlightRules } from './elasticsearch_sql_highlight_rules'; const { ScriptHighlightRules } = require('./script_highlight_rules'); +const { JsonHighlightRules } = ace.acequire('ace/mode/json_highlight_rules'); +const oop = ace.acequire('ace/lib/oop'); + const jsonRules = function(root) { root = root ? root : 'json'; const rules = {}; @@ -146,6 +151,30 @@ const jsonRules = function(root) { return rules; }; +export function XJsonHighlightRules() { + this.$rules = { + ...jsonRules('start'), + }; + + this.embedRules(ScriptHighlightRules, 'script-', [ + { + token: 'punctuation.end_triple_quote', + regex: '"""', + next: 'pop', + }, + ]); + + this.embedRules(ElasticsearchSqlHighlightRules, 'sql-', [ + { + token: 'punctuation.end_triple_quote', + regex: '"""', + next: 'pop', + }, + ]); +} + +oop.inherits(XJsonHighlightRules, JsonHighlightRules); + export function addToRules(otherRules, embedUnder) { otherRules.$rules = _.defaultsDeep(otherRules.$rules, jsonRules(embedUnder)); otherRules.embedRules(ScriptHighlightRules, 'script-', [ diff --git a/src/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts b/src/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts new file mode 100644 index 0000000000000..caa7b518b8b66 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { XJsonMode } from './x_json_mode'; diff --git a/src/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json_mode.ts b/src/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json_mode.ts new file mode 100644 index 0000000000000..9f804c29a5d27 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json_mode.ts @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ace from 'brace'; + +import { XJsonHighlightRules } from '../index'; + +const oop = ace.acequire('ace/lib/oop'); +const { Mode: JSONMode } = ace.acequire('ace/mode/json'); +const { Tokenizer: AceTokenizer } = ace.acequire('ace/tokenizer'); +const { MatchingBraceOutdent } = ace.acequire('ace/mode/matching_brace_outdent'); +const { CstyleBehaviour } = ace.acequire('ace/mode/behaviour/cstyle'); +const { FoldMode: CStyleFoldMode } = ace.acequire('ace/mode/folding/cstyle'); + +export function XJsonMode(this: any) { + const ruleset: any = new XJsonHighlightRules(); + ruleset.normalizeRules(); + this.$tokenizer = new AceTokenizer(ruleset.getRules()); + this.$outdent = new MatchingBraceOutdent(); + this.$behaviour = new CstyleBehaviour(); + this.foldingRules = new CStyleFoldMode(); +} + +oop.inherits(XJsonMode, JSONMode); diff --git a/src/plugins/es_ui_shared/console_lang/index.ts b/src/plugins/es_ui_shared/console_lang/index.ts new file mode 100644 index 0000000000000..a4958911af412 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/index.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Lib is intentionally not included in this barrel export file to separate worker logic +// from being imported with pure functions + +export { + ElasticsearchSqlHighlightRules, + ScriptHighlightRules, + XJsonHighlightRules, + addXJsonToRules, + XJsonMode, +} from './ace/modes'; diff --git a/src/plugins/es_ui_shared/console_lang/lib/index.ts b/src/plugins/es_ui_shared/console_lang/lib/index.ts new file mode 100644 index 0000000000000..bf7f0290d4158 --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/lib/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { collapseLiteralStrings, expandLiteralStrings } from './json_xjson_translation_tools'; diff --git a/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts b/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts new file mode 100644 index 0000000000000..92c14ade791cd --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/json_xjson_translation_tools.test.ts @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import _ from 'lodash'; +// @ts-ignore +import collapsingTests from './utils_string_collapsing.txt'; +// @ts-ignore +import expandingTests from './utils_string_expanding.txt'; + +import * as utils from '../index'; + +describe('JSON to XJSON conversion tools', () => { + it('will collapse multiline strings', () => { + const multiline = '{ "foo": """bar\nbaz""" }'; + expect(utils.collapseLiteralStrings(multiline)).toEqual('{ "foo": "bar\\nbaz" }'); + }); + + it('will collapse multiline strings with CRLF endings', () => { + const multiline = '{ "foo": """bar\r\nbaz""" }'; + expect(utils.collapseLiteralStrings(multiline)).toEqual('{ "foo": "bar\\r\\nbaz" }'); + }); +}); + +_.each(collapsingTests.split(/^=+$/m), function(fixture) { + if (fixture.trim() === '') { + return; + } + fixture = fixture.split(/^-+$/m); + const name = fixture[0].trim(); + const expanded = fixture[1].trim(); + const collapsed = fixture[2].trim(); + + test('Literal collapse - ' + name, function() { + expect(utils.collapseLiteralStrings(expanded)).toEqual(collapsed); + }); +}); + +_.each(expandingTests.split(/^=+$/m), function(fixture) { + if (fixture.trim() === '') { + return; + } + fixture = fixture.split(/^-+$/m); + const name = fixture[0].trim(); + const collapsed = fixture[1].trim(); + const expanded = fixture[2].trim(); + + test('Literal expand - ' + name, function() { + expect(utils.expandLiteralStrings(collapsed)).toEqual(expanded); + }); +}); diff --git a/src/plugins/console/public/lib/utils/__tests__/utils_string_collapsing.txt b/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_collapsing.txt similarity index 100% rename from src/plugins/console/public/lib/utils/__tests__/utils_string_collapsing.txt rename to src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_collapsing.txt diff --git a/src/plugins/console/public/lib/utils/__tests__/utils_string_expanding.txt b/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt similarity index 100% rename from src/plugins/console/public/lib/utils/__tests__/utils_string_expanding.txt rename to src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/__tests__/utils_string_expanding.txt diff --git a/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/index.ts b/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/index.ts new file mode 100644 index 0000000000000..28f1aca95efab --- /dev/null +++ b/src/plugins/es_ui_shared/console_lang/lib/json_xjson_translation_tools/index.ts @@ -0,0 +1,71 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export function collapseLiteralStrings(data: string) { + const splitData = data.split(`"""`); + for (let idx = 1; idx < splitData.length - 1; idx += 2) { + splitData[idx] = JSON.stringify(splitData[idx]); + } + return splitData.join(''); +} + +/* + The following regex describes global match on: + 1. one colon followed by any number of space characters + 2. one double quote (not escaped, special case for JSON in JSON). + 3. greedily match any non double quote and non newline char OR any escaped double quote char (non-capturing). + 4. handle a special case where an escaped slash may be the last character + 5. one double quote + + For instance: `: "some characters \" here"` + Will match and be expanded to: `"""some characters " here"""` + + */ + +const LITERAL_STRING_CANDIDATES = /((:[\s\r\n]*)([^\\])"(\\"|[^"\n])*\\?")/g; + +export function expandLiteralStrings(data: string) { + return data.replace(LITERAL_STRING_CANDIDATES, (match, string) => { + // Expand to triple quotes if there are _any_ slashes + if (string.match(/\\./)) { + const firstDoubleQuoteIdx = string.indexOf('"'); + const lastDoubleQuoteIdx = string.lastIndexOf('"'); + + // Handle a special case where we may have a value like "\"test\"". We don't + // want to expand this to """"test"""" - so we terminate before processing the string + // further if we detect this either at the start or end of the double quote section. + + if (string[firstDoubleQuoteIdx + 1] === '\\' && string[firstDoubleQuoteIdx + 2] === '"') { + return string; + } + + if (string[lastDoubleQuoteIdx - 1] === '"' && string[lastDoubleQuoteIdx - 2] === '\\') { + return string; + } + + const colonAndAnySpacing = string.slice(0, firstDoubleQuoteIdx); + const rawStringifiedValue = string.slice(firstDoubleQuoteIdx, string.length); + // Remove one level of JSON stringification + const jsonValue = JSON.parse(rawStringifiedValue); + return `${colonAndAnySpacing}"""${jsonValue}"""`; + } else { + return string; + } + }); +} diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index a12c951ad13a8..67e3617a85115 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export * from './components/json_editor'; +export { JsonEditor, OnJsonEditorUpdateHandler } from './components/json_editor'; diff --git a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/index.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/index.ts new file mode 100644 index 0000000000000..ae3c9962ecadb --- /dev/null +++ b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { installXJsonMode, XJsonMode } from './x_json'; diff --git a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts new file mode 100644 index 0000000000000..ae3c9962ecadb --- /dev/null +++ b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { installXJsonMode, XJsonMode } from './x_json'; diff --git a/x-pack/plugins/searchprofiler/public/application/editor/worker/index.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts similarity index 100% rename from x-pack/plugins/searchprofiler/public/application/editor/worker/index.ts rename to x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts diff --git a/x-pack/plugins/searchprofiler/public/application/editor/worker/worker.d.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/worker.d.ts similarity index 100% rename from x-pack/plugins/searchprofiler/public/application/editor/worker/worker.d.ts rename to x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/worker.d.ts diff --git a/x-pack/plugins/searchprofiler/public/application/editor/worker/worker.js b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/worker.js similarity index 100% rename from x-pack/plugins/searchprofiler/public/application/editor/worker/worker.js rename to x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/worker.js diff --git a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json.ts new file mode 100644 index 0000000000000..bfeca045bea02 --- /dev/null +++ b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/x_json.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import ace from 'brace'; +import { XJsonMode } from '../../../../../../../src/plugins/es_ui_shared/console_lang'; +import { workerModule } from './worker'; +const { WorkerClient } = ace.acequire('ace/worker/worker_client'); + +// Then clobber `createWorker` method to install our worker source. Per ace's wiki: https://github.com/ajaxorg/ace/wiki/Syntax-validation +(XJsonMode.prototype as any).createWorker = function(session: ace.IEditSession) { + const xJsonWorker = new WorkerClient(['ace'], workerModule, 'JsonWorker'); + + xJsonWorker.attachToDocument(session.getDocument()); + + xJsonWorker.on('annotate', function(e: { data: any }) { + session.setAnnotations(e.data); + }); + + xJsonWorker.on('terminate', function() { + session.clearAnnotations(); + }); + + return xJsonWorker; +}; + +export { XJsonMode }; + +export function installXJsonMode(editor: ace.Editor) { + const session = editor.getSession(); + session.setMode(new (XJsonMode as any)()); +} diff --git a/x-pack/plugins/es_ui_shared/console_lang/index.ts b/x-pack/plugins/es_ui_shared/console_lang/index.ts new file mode 100644 index 0000000000000..b5fe3a554e34d --- /dev/null +++ b/x-pack/plugins/es_ui_shared/console_lang/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { XJsonMode, installXJsonMode } from './ace/modes'; diff --git a/x-pack/plugins/es_ui_shared/console_lang/mocks.ts b/x-pack/plugins/es_ui_shared/console_lang/mocks.ts new file mode 100644 index 0000000000000..68480282ddc03 --- /dev/null +++ b/x-pack/plugins/es_ui_shared/console_lang/mocks.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +jest.mock('./ace/modes/x_json/worker', () => ({ + workerModule: { id: 'ace/mode/json_worker', src: '' }, +})); diff --git a/x-pack/plugins/searchprofiler/public/application/containers/main/main.tsx b/x-pack/plugins/searchprofiler/public/application/containers/main/main.tsx index 90617ba1c5167..aa6c20aa6a7f3 100644 --- a/x-pack/plugins/searchprofiler/public/application/containers/main/main.tsx +++ b/x-pack/plugins/searchprofiler/public/application/containers/main/main.tsx @@ -5,7 +5,6 @@ */ import React, { useCallback } from 'react'; -import _ from 'lodash'; import { EuiPage, diff --git a/x-pack/plugins/searchprofiler/public/application/editor/editor.test.tsx b/x-pack/plugins/searchprofiler/public/application/editor/editor.test.tsx index a70d70f117edb..ad56b3957eb74 100644 --- a/x-pack/plugins/searchprofiler/public/application/editor/editor.test.tsx +++ b/x-pack/plugins/searchprofiler/public/application/editor/editor.test.tsx @@ -6,9 +6,7 @@ import 'brace'; import 'brace/mode/json'; -jest.mock('./worker', () => { - return { workerModule: { id: 'ace/mode/json_worker', src: '' } }; -}); +import '../../../../es_ui_shared/console_lang/mocks'; import { registerTestBed } from '../../../../../test_utils'; import { Editor, Props } from '.'; diff --git a/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts b/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts index e5aad16bc4af2..6f19ce12eb639 100644 --- a/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts +++ b/x-pack/plugins/searchprofiler/public/application/editor/init_editor.ts @@ -5,7 +5,7 @@ */ import ace from 'brace'; -import { installXJsonMode } from './x_json_mode'; +import { installXJsonMode } from '../../../../es_ui_shared/console_lang'; export function initializeEditor({ el, diff --git a/x-pack/plugins/searchprofiler/public/application/editor/x_json_highlight_rules.ts b/x-pack/plugins/searchprofiler/public/application/editor/x_json_highlight_rules.ts deleted file mode 100644 index 1e6e774db2e52..0000000000000 --- a/x-pack/plugins/searchprofiler/public/application/editor/x_json_highlight_rules.ts +++ /dev/null @@ -1,139 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import ace from 'brace'; -const oop = ace.acequire('ace/lib/oop'); -const { JsonHighlightRules } = ace.acequire('ace/mode/json_highlight_rules'); -const { TextHighlightRules } = ace.acequire('ace/mode/text_highlight_rules'); - -/* - * The rules below were copied from ./src/legacy/core_plugins/console/public/src/sense_editor/mode/x_json_highlight_rules.js - * - * It is very likely that this code will move (or be removed) in future but for now - * it enables syntax highlight for extended json. - */ - -const xJsonRules = { - start: [ - { - token: [ - 'variable', - 'whitespace', - 'ace.punctuation.colon', - 'whitespace', - 'punctuation.start_triple_quote', - ], - regex: '("(?:[^"]*_)?script"|"inline"|"source")(\\s*?)(:)(\\s*?)(""")', - next: 'script-start', - merge: false, - push: true, - }, - { - token: 'variable', // single line - regex: '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]\\s*(?=:)', - }, - { - token: 'punctuation.start_triple_quote', - regex: '"""', - next: 'string_literal', - merge: false, - push: true, - }, - { - token: 'string', // single line - regex: '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]', - }, - { - token: 'constant.numeric', // hex - regex: '0[xX][0-9a-fA-F]+\\b', - }, - { - token: 'constant.numeric', // float - regex: '[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b', - }, - { - token: 'constant.language.boolean', - regex: '(?:true|false)\\b', - }, - { - token: 'invalid.illegal', // single quoted strings are not allowed - regex: "['](?:(?:\\\\.)|(?:[^'\\\\]))*?[']", - }, - { - token: 'invalid.illegal', // comments are not allowed - regex: '\\/\\/.*$', - }, - { - token: 'paren.lparen', - merge: false, - regex: '{', - next: 'start', - push: true, - }, - { - token: 'paren.lparen', - merge: false, - regex: '[[(]', - }, - { - token: 'paren.rparen', - merge: false, - regex: '[\\])]', - }, - { - token: 'paren.rparen', - regex: '}', - merge: false, - next: 'pop', - }, - { - token: 'punctuation.comma', - regex: ',', - }, - { - token: 'punctuation.colon', - regex: ':', - }, - { - token: 'whitespace', - regex: '\\s+', - }, - { - token: 'text', - regex: '.+?', - }, - ], - string_literal: [ - { - token: 'punctuation.end_triple_quote', - regex: '"""', - next: 'pop', - }, - { - token: 'multi_string', - regex: '.', - }, - ], -}; - -function XJsonHighlightRules(this: any) { - this.$rules = xJsonRules; -} - -oop.inherits(XJsonHighlightRules, JsonHighlightRules); - -export function getRules() { - const ruleset: any = new (XJsonHighlightRules as any)(); - ruleset.embedRules(TextHighlightRules, 'text-', [ - { - token: 'punctuation.end_triple_quote', - regex: '"""', - next: 'pop', - }, - ]); - ruleset.normalizeRules(); - return ruleset.getRules(); -} diff --git a/x-pack/plugins/searchprofiler/public/application/editor/x_json_mode.ts b/x-pack/plugins/searchprofiler/public/application/editor/x_json_mode.ts deleted file mode 100644 index 7ac1e6105d97f..0000000000000 --- a/x-pack/plugins/searchprofiler/public/application/editor/x_json_mode.ts +++ /dev/null @@ -1,54 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -// Copied and modified from src/legacy/core_plugins/console/public/src/sense_editor/mode/input.js - -import ace from 'brace'; - -import * as xJsonRules from './x_json_highlight_rules'; -import { workerModule } from './worker'; - -const oop = ace.acequire('ace/lib/oop'); -const { Mode: JSONMode } = ace.acequire('ace/mode/json'); -const { Tokenizer: AceTokenizer } = ace.acequire('ace/tokenizer'); -const { MatchingBraceOutdent } = ace.acequire('ace/mode/matching_brace_outdent'); -const { CstyleBehaviour } = ace.acequire('ace/mode/behaviour/cstyle'); -const { FoldMode: CStyleFoldMode } = ace.acequire('ace/mode/folding/cstyle'); -const { WorkerClient } = ace.acequire('ace/worker/worker_client'); - -function XJsonMode(this: any) { - this.$tokenizer = new AceTokenizer(xJsonRules.getRules()); - this.$outdent = new MatchingBraceOutdent(); - this.$behaviour = new CstyleBehaviour(); - this.foldingRules = new CStyleFoldMode(); -} - -// Order here matters here: - -// 1. We first inherit -oop.inherits(XJsonMode, JSONMode); - -// 2. Then clobber `createWorker` method to install our worker source. Per ace's wiki: https://github.com/ajaxorg/ace/wiki/Syntax-validation -XJsonMode.prototype.createWorker = function(session: ace.IEditSession) { - const xJsonWorker = new WorkerClient(['ace'], workerModule, 'JsonWorker'); - - xJsonWorker.attachToDocument(session.getDocument()); - - xJsonWorker.on('annotate', function(e: { data: any }) { - session.setAnnotations(e.data); - }); - - xJsonWorker.on('terminate', function() { - session.clearAnnotations(); - }); - - return xJsonWorker; -}; - -export function installXJsonMode(editor: ace.Editor) { - const session = editor.getSession(); - session.setMode(new (XJsonMode as any)()); -} diff --git a/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts b/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts index 4267fd0d2f901..99687de0f1440 100644 --- a/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts +++ b/x-pack/plugins/searchprofiler/public/application/utils/check_for_json_errors.ts @@ -4,12 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -// Convert triple quotes into regular quotes and escape internal quotes. -function collapseLiteralStrings(data: string) { - return data.replace(/"""(?:\s*\r?\n)?((?:.|\r?\n)*?)(?:\r?\n\s*)?"""/g, function(match, literal) { - return JSON.stringify(literal); - }); -} +import { collapseLiteralStrings } from '../../../../../../src/plugins/es_ui_shared/console_lang/lib'; export function checkForParseErrors(json: string) { const sanitizedJson = collapseLiteralStrings(json); diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts index 26be3421b534e..93b8cce153d3b 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_json.test.ts @@ -3,6 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import '../../../es_ui_shared/console_lang/mocks'; + import { act } from 'react-dom/test-utils'; import { setupEnvironment, pageHelpers, nextTick, wrapBodyResponse } from './helpers'; import { WatchCreateJsonTestBed } from './helpers/watch_create_json.helpers'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx index 431eb1cae0608..943233d3c14ed 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_create_threshold.test.tsx @@ -3,6 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + +import '../../../es_ui_shared/console_lang/mocks'; + import React from 'react'; import { act } from 'react-dom/test-utils'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts index 545bfbdf7cbc2..51285a5786b00 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_edit.test.ts @@ -3,6 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import '../../../es_ui_shared/console_lang/mocks'; + import { act } from 'react-dom/test-utils'; import axiosXhrAdapter from 'axios/lib/adapters/xhr'; import axios from 'axios'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts index a0327c6dfa1db..3370e42b2417f 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_list.test.ts @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import '../../../es_ui_shared/console_lang/mocks'; import { act } from 'react-dom/test-utils'; import * as fixtures from '../../test/fixtures'; diff --git a/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts b/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts index 973c14893f342..20b7b526705c0 100644 --- a/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts +++ b/x-pack/plugins/watcher/__jest__/client_integration/watch_status.test.ts @@ -3,6 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import '../../../es_ui_shared/console_lang/mocks'; + import { act } from 'react-dom/test-utils'; import { setupEnvironment, pageHelpers, nextTick } from './helpers'; import { WatchStatusTestBed } from './helpers/watch_status.helpers'; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx index 91185ac604b34..b3a09d3bc0e65 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_form.tsx @@ -20,6 +20,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; + import { serializeJsonWatch } from '../../../../../../common/lib/serialization'; import { ErrableFormRow, SectionError, Error as ServerError } from '../../../../components'; import { onWatchSave } from '../../watch_edit_actions'; @@ -28,6 +29,8 @@ import { goToWatchList } from '../../../../lib/navigation'; import { RequestFlyout } from '../request_flyout'; import { useAppContext } from '../../../../app_context'; +import { useXJsonMode } from './use_x_json_mode'; + export const JsonWatchEditForm = () => { const { links: { putWatchApiUrl }, @@ -35,6 +38,7 @@ export const JsonWatchEditForm = () => { } = useAppContext(); const { watch, setWatchProperty } = useContext(WatchContext); + const { xJsonMode, convertToJson, setXJson, xJson } = useXJsonMode(watch.watchString); const { errors } = watch.validate(); const hasErrors = !!Object.keys(errors).find(errorKey => errors[errorKey].length >= 1); @@ -160,9 +164,9 @@ export const JsonWatchEditForm = () => { errors={jsonErrors} > { defaultMessage: 'Code editor', } )} - value={watch.watchString} - onChange={(json: string) => { + value={xJson} + onChange={(xjson: string) => { if (validationError) { setValidationError(null); } - setWatchProperty('watchString', json); + setXJson(xjson); + // Keep the watch in sync with the editor content + setWatchProperty('watchString', convertToJson(xjson)); }} /> diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx index 9ac80ade89daa..c906d05be64be 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/json_watch_edit_simulate.tsx @@ -40,6 +40,8 @@ import { JsonWatchEditSimulateResults } from './json_watch_edit_simulate_results import { getTimeUnitLabel } from '../../../../lib/get_time_unit_label'; import { useAppContext } from '../../../../app_context'; +import { useXJsonMode } from './use_x_json_mode'; + const actionModeOptions = Object.keys(ACTION_MODES).map(mode => ({ text: ACTION_MODES[mode], value: ACTION_MODES[mode], @@ -94,6 +96,8 @@ export const JsonWatchEditSimulate = ({ ignoreCondition, } = executeDetails; + const { setXJson, convertToJson, xJsonMode, xJson } = useXJsonMode(alternativeInput); + const columns = [ { field: 'actionId', @@ -371,22 +375,23 @@ export const JsonWatchEditSimulate = ({ > { + value={xJson} + onChange={(xjson: string) => { + setXJson(xjson); setExecuteDetails( new ExecuteDetails({ ...executeDetails, - alternativeInput: json, + alternativeInput: convertToJson(xjson), }) ); }} diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/use_x_json_mode.ts b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/use_x_json_mode.ts new file mode 100644 index 0000000000000..7aefb0554e0e8 --- /dev/null +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/json_watch_edit/use_x_json_mode.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ +import { useState } from 'react'; +import { XJsonMode } from '../../../../../../../es_ui_shared/console_lang'; +import { + collapseLiteralStrings, + expandLiteralStrings, +} from '../../../../../../../../../src/plugins/es_ui_shared/console_lang/lib'; + +export const xJsonMode = new XJsonMode(); + +export const useXJsonMode = (json: string) => { + const [xJson, setXJson] = useState(expandLiteralStrings(json)); + + return { + xJson, + setXJson, + xJsonMode, + convertToJson: collapseLiteralStrings, + }; +}; diff --git a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx index c1ebcdc262863..b79f9eee01834 100644 --- a/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx +++ b/x-pack/plugins/watcher/public/application/sections/watch_edit/components/threshold_watch_edit/action_fields/webhook_action_fields.tsx @@ -245,7 +245,7 @@ export const WebhookActionFields: React.FunctionComponent = ({ mode="json" width="100%" height="200px" - theme="github" + theme="textmate" data-test-subj="webhookBodyEditor" aria-label={i18n.translate( 'xpack.watcher.sections.watchEdit.threshold.webhookAction.bodyCodeEditorAriaLabel', From 545240065ab323fabc708435bf535e62d238cdd8 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 17 Feb 2020 10:47:50 +0000 Subject: [PATCH 26/65] [ML] Categorization examples privilege check (#57375) * [ML] Categorization examples privilege check * adding privileges check to endpoint * fixing typo * moving privilege check to router * removing unused variable * rebasing master * removing comment Co-authored-by: Elastic Machine --- .../ml/server/models/job_service/index.js | 7 +--- .../new_job/categorization/examples.ts | 7 ++-- .../server/new_platform/job_service_schema.ts | 14 +------- .../plugins/ml/server/routes/job_service.ts | 36 +++++++++++++++++-- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/index.js b/x-pack/legacy/plugins/ml/server/models/job_service/index.js index 6f409e70e68b8..70b855e80a770 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/index.js +++ b/x-pack/legacy/plugins/ml/server/models/job_service/index.js @@ -8,11 +8,7 @@ import { datafeedsProvider } from './datafeeds'; import { jobsProvider } from './jobs'; import { groupsProvider } from './groups'; import { newJobCapsProvider } from './new_job_caps'; -import { - newJobChartsProvider, - categorizationExamplesProvider, - topCategoriesProvider, -} from './new_job'; +import { newJobChartsProvider, topCategoriesProvider } from './new_job'; export function jobServiceProvider(callAsCurrentUser) { return { @@ -21,7 +17,6 @@ export function jobServiceProvider(callAsCurrentUser) { ...groupsProvider(callAsCurrentUser), ...newJobCapsProvider(callAsCurrentUser), ...newJobChartsProvider(callAsCurrentUser), - ...categorizationExamplesProvider(callAsCurrentUser), ...topCategoriesProvider(callAsCurrentUser), }; } diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts b/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts index 76473bd55db7f..ea2c71b04f56d 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts +++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job/categorization/examples.ts @@ -17,7 +17,10 @@ import { ValidationResults } from './validation_results'; const CHUNK_SIZE = 100; -export function categorizationExamplesProvider(callWithRequest: callWithRequestType) { +export function categorizationExamplesProvider( + callWithRequest: callWithRequestType, + callWithInternalUser: callWithRequestType +) { const validationResults = new ValidationResults(); async function categorizationExamples( @@ -109,7 +112,7 @@ export function categorizationExamplesProvider(callWithRequest: callWithRequestT } async function loadTokens(examples: string[], analyzer: CategorizationAnalyzer) { - const { tokens }: { tokens: Token[] } = await callWithRequest('indices.analyze', { + const { tokens }: { tokens: Token[] } = await callWithInternalUser('indices.analyze', { body: { ...getAnalyzer(analyzer), text: examples, diff --git a/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts b/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts index b37fcba737802..deb62678a777c 100644 --- a/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts +++ b/x-pack/legacy/plugins/ml/server/new_platform/job_service_schema.ts @@ -6,18 +6,6 @@ import { schema } from '@kbn/config-schema'; -const analyzerSchema = { - tokenizer: schema.string(), - filter: schema.maybe( - schema.arrayOf( - schema.object({ - type: schema.string(), - stopwords: schema.arrayOf(schema.maybe(schema.string())), - }) - ) - ), -}; - export const categorizationFieldExamplesSchema = { indexPatternTitle: schema.string(), query: schema.any(), @@ -26,7 +14,7 @@ export const categorizationFieldExamplesSchema = { timeField: schema.maybe(schema.string()), start: schema.number(), end: schema.number(), - analyzer: schema.object(analyzerSchema), + analyzer: schema.any(), }; export const chartSchema = { diff --git a/x-pack/legacy/plugins/ml/server/routes/job_service.ts b/x-pack/legacy/plugins/ml/server/routes/job_service.ts index 9aa3960e59e4c..5ddbd4cdfd5a5 100644 --- a/x-pack/legacy/plugins/ml/server/routes/job_service.ts +++ b/x-pack/legacy/plugins/ml/server/routes/job_service.ts @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ +import Boom from 'boom'; import { schema } from '@kbn/config-schema'; +import { IScopedClusterClient } from 'src/core/server'; import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory'; import { wrapError } from '../client/error_wrapper'; import { RouteInitialization } from '../new_platform/plugin'; +import { isSecurityDisabled } from '../lib/security_utils'; import { categorizationFieldExamplesSchema, chartSchema, @@ -21,11 +24,31 @@ import { } from '../new_platform/job_service_schema'; // @ts-ignore no declaration module import { jobServiceProvider } from '../models/job_service'; +import { categorizationExamplesProvider } from '../models/job_service/new_job'; /** * Routes for job service */ export function jobServiceRoutes({ xpackMainPlugin, router }: RouteInitialization) { + async function hasPermissionToCreateJobs( + callAsCurrentUser: IScopedClusterClient['callAsCurrentUser'] + ) { + if (isSecurityDisabled(xpackMainPlugin) === true) { + return true; + } + + const resp = await callAsCurrentUser('ml.privilegeCheck', { + body: { + cluster: [ + 'cluster:admin/xpack/ml/job/put', + 'cluster:admin/xpack/ml/job/open', + 'cluster:admin/xpack/ml/datafeeds/put', + ], + }, + }); + return resp.has_all_requested; + } + /** * @apiGroup JobService * @@ -545,8 +568,17 @@ export function jobServiceRoutes({ xpackMainPlugin, router }: RouteInitializatio }, licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { try { - const { validateCategoryExamples } = jobServiceProvider( - context.ml!.mlClient.callAsCurrentUser + // due to the use of the _analyze endpoint which is called by the kibana user, + // basic job creation privileges are required to use this endpoint + if ((await hasPermissionToCreateJobs(context.ml!.mlClient.callAsCurrentUser)) === false) { + throw Boom.forbidden( + 'Insufficient privileges, the machine_learning_admin role is required.' + ); + } + + const { validateCategoryExamples } = categorizationExamplesProvider( + context.ml!.mlClient.callAsCurrentUser, + context.ml!.mlClient.callAsInternalUser ); const { indexPatternTitle, From 54f5cc1ce384e6bcc9d431e0b60f095778ab1490 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Mon, 17 Feb 2020 11:53:42 +0100 Subject: [PATCH 27/65] [ML] Anomaly Detection: Fixes hiding date picker for settings pages. (#57544) - Fixes hiding the global date picker on anomaly detection settings pages. - Consolidates various timefilter usages for enabling/disabling the date picker into a useTimefilter() hook. --- .../application/contexts/kibana/index.ts | 1 + .../contexts/kibana/use_timefilter.test.ts | 65 +++++++++++++++++++ .../contexts/kibana/use_timefilter.ts | 38 +++++++++++ .../datavisualizer_selector.tsx | 7 +- .../file_based/file_datavisualizer.tsx | 7 +- .../datavisualizer/index_based/page.tsx | 15 ++--- .../application/routing/routes/explorer.tsx | 8 +-- .../application/routing/routes/jobs_list.tsx | 8 +-- .../application/routing/routes/overview.tsx | 2 + .../routing/routes/settings/calendar_list.tsx | 3 + .../routes/settings/calendar_new_edit.tsx | 3 + .../routing/routes/settings/filter_list.tsx | 3 + .../routes/settings/filter_list_new_edit.tsx | 3 + .../routing/routes/settings/settings.tsx | 3 + .../routes/timeseriesexplorer.test.tsx | 30 +-------- .../routing/routes/timeseriesexplorer.tsx | 10 +-- .../settings/calendars/edit/new_calendar.js | 3 - 17 files changed, 139 insertions(+), 70 deletions(-) create mode 100644 x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts create mode 100644 x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts index 7ebbd45fd372a..2add1b6ea161c 100644 --- a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts +++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/index.ts @@ -6,3 +6,4 @@ export { useMlKibana, StartServices, MlKibanaReactContextValue } from './kibana_context'; export { useUiSettings } from './use_ui_settings_context'; +export { useTimefilter } from './use_timefilter'; diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts new file mode 100644 index 0000000000000..98ddd874c695c --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { renderHook } from '@testing-library/react-hooks'; +import { useTimefilter } from './use_timefilter'; + +jest.mock('./kibana_context', () => ({ + useMlKibana: () => { + return { + services: { + data: { + query: { + timefilter: { + timefilter: { + disableTimeRangeSelector: jest.fn(), + disableAutoRefreshSelector: jest.fn(), + enableTimeRangeSelector: jest.fn(), + enableAutoRefreshSelector: jest.fn(), + }, + }, + }, + }, + }, + }; + }, +})); + +describe('useTimefilter', () => { + test('will not trigger any date picker settings by default', () => { + const { result } = renderHook(() => useTimefilter()); + const timefilter = result.current; + + expect(timefilter.disableTimeRangeSelector).toHaveBeenCalledTimes(0); + expect(timefilter.disableAutoRefreshSelector).toHaveBeenCalledTimes(0); + expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0); + expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0); + }); + + test('custom disabled overrides', () => { + const { result } = renderHook(() => + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }) + ); + const timefilter = result.current; + + expect(timefilter.disableTimeRangeSelector).toHaveBeenCalledTimes(1); + expect(timefilter.disableAutoRefreshSelector).toHaveBeenCalledTimes(1); + expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0); + expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(0); + }); + + test('custom enabled overrides', () => { + const { result } = renderHook(() => + useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true }) + ); + const timefilter = result.current; + + expect(timefilter.disableTimeRangeSelector).toHaveBeenCalledTimes(0); + expect(timefilter.disableAutoRefreshSelector).toHaveBeenCalledTimes(0); + expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(1); + expect(timefilter.enableTimeRangeSelector).toHaveBeenCalledTimes(1); + }); +}); diff --git a/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts new file mode 100644 index 0000000000000..374e101f63dc8 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/application/contexts/kibana/use_timefilter.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { useEffect } from 'react'; + +import { useMlKibana } from './kibana_context'; + +interface UseTimefilterOptions { + timeRangeSelector?: boolean; + autoRefreshSelector?: boolean; +} + +export const useTimefilter = ({ + timeRangeSelector, + autoRefreshSelector, +}: UseTimefilterOptions = {}) => { + const { services } = useMlKibana(); + const { timefilter } = services.data.query.timefilter; + + useEffect(() => { + if (timeRangeSelector === true) { + timefilter.enableTimeRangeSelector(); + } else if (timeRangeSelector === false) { + timefilter.disableTimeRangeSelector(); + } + + if (autoRefreshSelector === true) { + timefilter.enableAutoRefreshSelector(); + } else if (autoRefreshSelector === false) { + timefilter.disableAutoRefreshSelector(); + } + }, [timeRangeSelector, autoRefreshSelector]); + + return timefilter; +}; diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx index ae0c034f972d6..0f56f78c708ee 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { isFullLicense } from '../license/check_license'; -import { useMlKibana } from '../contexts/kibana'; +import { useTimefilter } from '../contexts/kibana'; import { NavigationMenu } from '../components/navigation_menu'; @@ -49,10 +49,7 @@ function startTrialDescription() { } export const DatavisualizerSelector: FC = () => { - const { services } = useMlKibana(); - const { timefilter } = services.data.query.timefilter; - timefilter.disableTimeRangeSelector(); - timefilter.disableAutoRefreshSelector(); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); const startTrialVisible = isFullLicense() === false; diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx index 9dcb9d25692e9..5c32d62c39f84 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/file_datavisualizer.tsx @@ -7,7 +7,7 @@ import React, { FC, Fragment } from 'react'; import { IUiSettingsClient } from 'src/core/public'; -import { useMlKibana } from '../../contexts/kibana'; +import { useTimefilter } from '../../contexts/kibana'; import { NavigationMenu } from '../../components/navigation_menu'; import { getIndexPatternsContract } from '../../util/index_utils'; @@ -19,10 +19,7 @@ export interface FileDataVisualizerPageProps { } export const FileDataVisualizerPage: FC = ({ kibanaConfig }) => { - const { services } = useMlKibana(); - const { timefilter } = services.data.query.timefilter; - timefilter.disableTimeRangeSelector(); - timefilter.disableAutoRefreshSelector(); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); const indexPatterns = getIndexPatternsContract(); return ( diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx index a6508ea868724..84c07651d323d 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/index_based/page.tsx @@ -38,7 +38,7 @@ import { FullTimeRangeSelector } from '../../components/full_time_range_selector import { mlTimefilterRefresh$ } from '../../services/timefilter_refresh_service'; import { useMlContext, SavedSearchQuery } from '../../contexts/ml'; import { kbnTypeToMLJobType } from '../../util/field_types_utils'; -import { useMlKibana } from '../../contexts/kibana'; +import { useTimefilter } from '../../contexts/kibana'; import { timeBasedIndexCheck, getQueryFromSavedSearch } from '../../util/index_utils'; import { TimeBuckets } from '../../util/time_buckets'; import { useUrlState } from '../../util/url_state'; @@ -97,11 +97,13 @@ function getDefaultPageState(): DataVisualizerPageState { } export const Page: FC = () => { - const { services } = useMlKibana(); const mlContext = useMlContext(); - const { timefilter } = services.data.query.timefilter; const { combinedQuery, currentIndexPattern, currentSavedSearch, kibanaConfig } = mlContext; + const timefilter = useTimefilter({ + timeRangeSelector: currentIndexPattern.timeFieldName !== undefined, + autoRefreshSelector: true, + }); const dataLoader = new DataLoader(currentIndexPattern, kibanaConfig); const [globalState, setGlobalState] = useUrlState('_g'); @@ -122,13 +124,6 @@ export const Page: FC = () => { const [lastRefresh, setLastRefresh] = useState(0); useEffect(() => { - if (currentIndexPattern.timeFieldName !== undefined) { - timefilter.enableTimeRangeSelector(); - } else { - timefilter.disableTimeRangeSelector(); - } - - timefilter.enableAutoRefreshSelector(); timeBasedIndexCheck(currentIndexPattern, true); }, []); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx index b0046f7b8d699..2c6726338d2f1 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/explorer.tsx @@ -29,7 +29,7 @@ import { useTableInterval } from '../../components/controls/select_interval'; import { useTableSeverity } from '../../components/controls/select_severity'; import { useUrlState } from '../../util/url_state'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../breadcrumbs'; -import { useMlKibana } from '../../contexts/kibana'; +import { useTimefilter } from '../../contexts/kibana'; const breadcrumbs = [ ML_BREADCRUMB, @@ -70,8 +70,7 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim const [appState, setAppState] = useUrlState('_a'); const [globalState, setGlobalState] = useUrlState('_g'); const [lastRefresh, setLastRefresh] = useState(0); - const { services } = useMlKibana(); - const { timefilter } = services.data.query.timefilter; + const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true }); const { jobIds } = useJobSelection(jobsWithTimeRange, getDateFormatTz()); @@ -111,9 +110,6 @@ const ExplorerUrlStateManager: FC = ({ jobsWithTim }, [globalState?.time?.from, globalState?.time?.to]); useEffect(() => { - timefilter.enableTimeRangeSelector(); - timefilter.enableAutoRefreshSelector(); - const viewByFieldName = appState?.mlExplorerSwimlane?.viewByFieldName; if (viewByFieldName !== undefined) { explorerService.setViewBySwimlaneFieldName(viewByFieldName); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx index ca2c0750397e5..c1d686d356dda 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/jobs_list.tsx @@ -14,8 +14,8 @@ import { MlRoute, PageLoader, PageProps } from '../router'; import { useResolver } from '../use_resolver'; import { basicResolvers } from '../resolvers'; import { JobsPage } from '../../jobs/jobs_list'; +import { useTimefilter } from '../../contexts/kibana'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../breadcrumbs'; -import { useMlKibana } from '../../contexts/kibana'; const breadcrumbs = [ ML_BREADCRUMB, @@ -36,8 +36,7 @@ export const jobListRoute: MlRoute = { const PageWrapper: FC = ({ deps }) => { const { context } = useResolver(undefined, undefined, deps.config, basicResolvers(deps)); - const { services } = useMlKibana(); - const { timefilter } = services.data.query.timefilter; + const timefilter = useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true }); const [globalState, setGlobalState] = useUrlState('_g'); @@ -48,9 +47,6 @@ const PageWrapper: FC = ({ deps }) => { const blockRefresh = refreshValue === 0 || refreshPause === true; useEffect(() => { - timefilter.disableTimeRangeSelector(); - timefilter.enableAutoRefreshSelector(); - // If the refreshInterval defaults to 0s/pause=true, set it to 30s/pause=false, // otherwise pass on the globalState's settings to the date picker. const refreshInterval = diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx index 85227c11582d9..b1e00158efb94 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/overview.tsx @@ -16,6 +16,7 @@ import { checkFullLicense } from '../../license/check_license'; import { checkGetJobsPrivilege } from '../../privilege/check_privilege'; import { getMlNodeCount } from '../../ml_nodes_check'; import { loadMlServerInfo } from '../../services/ml_server_info'; +import { useTimefilter } from '../../contexts/kibana'; import { ML_BREADCRUMB } from '../breadcrumbs'; const breadcrumbs = [ @@ -41,6 +42,7 @@ const PageWrapper: FC = ({ deps }) => { getMlNodeCount, loadMlServerInfo, }); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); return ( diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx index fdbfcb3397c75..c1bfaa2fe6c1e 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_list.tsx @@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; +import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; @@ -45,6 +46,8 @@ const PageWrapper: FC = ({ deps }) => { getMlNodeCount, }); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); + const canCreateCalendar = checkPermission('canCreateCalendar'); const canDeleteCalendar = checkPermission('canDeleteCalendar'); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx index 7f622a1bba62b..7af2e49e3a69e 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/calendar_new_edit.tsx @@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; +import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes'; @@ -77,6 +78,8 @@ const PageWrapper: FC = ({ location, mode, deps }) => { checkMlNodesAvailable, }); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); + const canCreateCalendar = checkPermission('canCreateCalendar'); const canDeleteCalendar = checkPermission('canDeleteCalendar'); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx index 6a4ce271bff17..9c5c06b76247c 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list.tsx @@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; +import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; @@ -46,6 +47,8 @@ const PageWrapper: FC = ({ deps }) => { getMlNodeCount, }); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); + const canCreateFilter = checkPermission('canCreateFilter'); const canDeleteFilter = checkPermission('canDeleteFilter'); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx index 4fa15ebaac21a..752b889490e58 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/filter_list_new_edit.tsx @@ -15,6 +15,7 @@ import { i18n } from '@kbn/i18n'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; +import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { checkMlNodesAvailable } from '../../../ml_nodes_check/check_ml_nodes'; @@ -77,6 +78,8 @@ const PageWrapper: FC = ({ location, mode, deps }) => { checkMlNodesAvailable, }); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); + const canCreateFilter = checkPermission('canCreateFilter'); const canDeleteFilter = checkPermission('canDeleteFilter'); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx index 846512503ede5..10efb2dcc60c7 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/settings/settings.tsx @@ -14,6 +14,7 @@ import React, { FC } from 'react'; import { MlRoute, PageLoader, PageProps } from '../../router'; import { useResolver } from '../../use_resolver'; +import { useTimefilter } from '../../../contexts/kibana'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; @@ -35,6 +36,8 @@ const PageWrapper: FC = ({ deps }) => { getMlNodeCount, }); + useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false }); + const canGetFilters = checkPermission('canGetFilters'); const canGetCalendars = checkPermission('canGetCalendars'); diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx index 0ae42aa44e089..8633947374a8b 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.test.tsx @@ -12,33 +12,7 @@ import { I18nProvider } from '@kbn/i18n/react'; import { TimeSeriesExplorerUrlStateManager } from './timeseriesexplorer'; -jest.mock('ui/new_platform', () => ({ - npStart: { - plugins: { - data: { - query: { - timefilter: { - timefilter: { - enableTimeRangeSelector: jest.fn(), - enableAutoRefreshSelector: jest.fn(), - getRefreshInterval: jest.fn(), - setRefreshInterval: jest.fn(), - getTime: jest.fn(), - isAutoRefreshSelectorEnabled: jest.fn(), - isTimeRangeSelectorEnabled: jest.fn(), - getRefreshIntervalUpdate$: jest.fn(), - getTimeUpdate$: jest.fn(), - getEnabledUpdated$: jest.fn(), - }, - history: { get: jest.fn() }, - }, - }, - }, - }, - }, -})); - -jest.mock('../../contexts/kibana', () => ({ +jest.mock('../../contexts/kibana/kibana_context', () => ({ useMlKibana: () => { return { services: { @@ -47,6 +21,8 @@ jest.mock('../../contexts/kibana', () => ({ query: { timefilter: { timefilter: { + disableTimeRangeSelector: jest.fn(), + disableAutoRefreshSelector: jest.fn(), enableTimeRangeSelector: jest.fn(), enableAutoRefreshSelector: jest.fn(), getRefreshInterval: jest.fn(), diff --git a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx index 5bc2435db078c..f8a6f6c454fc0 100644 --- a/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx +++ b/x-pack/legacy/plugins/ml/public/application/routing/routes/timeseriesexplorer.tsx @@ -35,7 +35,7 @@ import { useRefresh } from '../use_refresh'; import { useResolver } from '../use_resolver'; import { basicResolvers } from '../resolvers'; import { ANOMALY_DETECTION_BREADCRUMB, ML_BREADCRUMB } from '../breadcrumbs'; -import { useMlKibana } from '../../contexts/kibana'; +import { useTimefilter } from '../../contexts/kibana'; export const timeSeriesExplorerRoute: MlRoute = { path: '/timeseriesexplorer', @@ -88,8 +88,7 @@ export const TimeSeriesExplorerUrlStateManager: FC(); - const { services } = useMlKibana(); - const { timefilter } = services.data.query.timefilter; + const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true }); const refresh = useRefresh(); useEffect(() => { @@ -106,11 +105,6 @@ export const TimeSeriesExplorerUrlStateManager: FC { - timefilter.enableTimeRangeSelector(); - timefilter.enableAutoRefreshSelector(); - }, []); - // We cannot simply infer bounds from the globalState's `time` attribute // with `moment` since it can contain custom strings such as `now-15m`. // So when globalState's `time` changes, we update the timefilter and use diff --git a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js index 0489528fa0f63..935e67ec05eff 100644 --- a/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js +++ b/x-pack/legacy/plugins/ml/public/application/settings/calendars/edit/new_calendar.js @@ -50,9 +50,6 @@ class NewCalendarUI extends Component { } componentDidMount() { - const { timefilter } = this.props.kibana.services.data.query.timefilter; - timefilter.disableTimeRangeSelector(); - timefilter.disableAutoRefreshSelector(); this.formSetup(); } From 5c7af8656fea9642d70d9e3c6a4b8699a9969d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Mon, 17 Feb 2020 11:21:01 +0000 Subject: [PATCH 28/65] [Telemetry] Fix bug introduced in #55859 (#57441) * [Telemetry] Refactor to TS Monitoring telemetry_collection files * [Telemetry] Fetch side documents generated by monitoring to build up the Kibana plugins stats * Update x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts Co-Authored-By: Ahmad Bamieh * Fix import in test file * Move mocha tests to Jest + TS * Fix extended telemetry in functional tests * Fix types * [Telemetry] Fix bug in usage_collector wrong function override * Revert integration tests (change not needed) Co-authored-by: Ahmad Bamieh Co-authored-by: Elastic Machine --- .../telemetry/server/collection_manager.ts | 6 +- .../server/collector/collector.ts | 2 +- .../server/collector/usage_collector.ts | 6 +- .../fixtures/beats_stats_results.json | 0 .../create_query.js => create_query.test.ts} | 11 +- .../{create_query.js => create_query.ts} | 22 +- ...get_all_stats.js => get_all_stats.test.ts} | 58 +++-- .../{get_all_stats.js => get_all_stats.ts} | 60 +++-- ...beats_stats.js => get_beats_stats.test.ts} | 52 ++--- ...{get_beats_stats.js => get_beats_stats.ts} | 206 ++++++++++++++---- ...ter_uuids.js => get_cluster_uuids.test.ts} | 21 +- .../get_es_stats.js => get_es_stats.test.ts} | 21 +- .../{get_es_stats.js => get_es_stats.ts} | 33 ++- ..._stats.js => get_high_level_stats.test.ts} | 27 ++- ...level_stats.js => get_high_level_stats.ts} | 148 ++++++++++--- ...bana_stats.js => get_kibana_stats.test.ts} | 164 ++++++++------ ...et_kibana_stats.js => get_kibana_stats.ts} | 117 +++++++--- .../register_monitoring_collection.ts | 1 - 18 files changed, 673 insertions(+), 282 deletions(-) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__ => __mocks__}/fixtures/beats_stats_results.json (100%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/create_query.js => create_query.test.ts} (89%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{create_query.js => create_query.ts} (80%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_all_stats.js => get_all_stats.test.ts} (78%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_all_stats.js => get_all_stats.ts} (66%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_beats_stats.js => get_beats_stats.test.ts} (75%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_beats_stats.js => get_beats_stats.ts} (60%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_cluster_uuids.js => get_cluster_uuids.test.ts} (77%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_es_stats.js => get_es_stats.test.ts} (82%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_es_stats.js => get_es_stats.ts} (71%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_high_level_stats.js => get_high_level_stats.test.ts} (91%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_high_level_stats.js => get_high_level_stats.ts} (66%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{__tests__/get_kibana_stats.js => get_kibana_stats.test.ts} (79%) rename x-pack/legacy/plugins/monitoring/server/telemetry_collection/{get_kibana_stats.js => get_kibana_stats.ts} (58%) diff --git a/src/legacy/core_plugins/telemetry/server/collection_manager.ts b/src/legacy/core_plugins/telemetry/server/collection_manager.ts index 933c249cd7279..0394dea343adf 100644 --- a/src/legacy/core_plugins/telemetry/server/collection_manager.ts +++ b/src/legacy/core_plugins/telemetry/server/collection_manager.ts @@ -41,8 +41,8 @@ export interface StatsCollectionConfig { usageCollection: UsageCollectionSetup; callCluster: CallCluster; server: any; - start: string; - end: string; + start: string | number; + end: string | number; } export type StatsGetterConfig = UnencryptedStatsGetterConfig | EncryptedStatsGetterConfig; @@ -193,7 +193,7 @@ export class TelemetryCollectionManager { } } catch (err) { statsCollectionConfig.server.log( - ['debu', 'telemetry', 'collection'], + ['debug', 'telemetry', 'collection'], `Failed to collect any usage with registered collections.` ); // swallow error to try next collection; diff --git a/src/plugins/usage_collection/server/collector/collector.ts b/src/plugins/usage_collection/server/collector/collector.ts index e102dc2a64ee8..91951aa2f3edf 100644 --- a/src/plugins/usage_collection/server/collector/collector.ts +++ b/src/plugins/usage_collection/server/collector/collector.ts @@ -85,7 +85,7 @@ export class Collector { protected defaultFormatterForBulkUpload(result: T) { return { type: this.type, - payload: result, + payload: (result as unknown) as U, }; } } diff --git a/src/plugins/usage_collection/server/collector/usage_collector.ts b/src/plugins/usage_collection/server/collector/usage_collector.ts index 05c701bd3abf4..bf861a94fccff 100644 --- a/src/plugins/usage_collection/server/collector/usage_collector.ts +++ b/src/plugins/usage_collection/server/collector/usage_collector.ts @@ -24,14 +24,14 @@ export class UsageCollector ex T, U > { - protected defaultUsageFormatterForBulkUpload(result: T) { + protected defaultFormatterForBulkUpload(result: T) { return { type: KIBANA_STATS_TYPE, - payload: { + payload: ({ usage: { [this.type]: result, }, - }, + } as unknown) as U, }; } } diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/fixtures/beats_stats_results.json b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/beats_stats_results.json similarity index 100% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/fixtures/beats_stats_results.json rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/__mocks__/fixtures/beats_stats_results.json diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/create_query.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.test.ts similarity index 89% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/create_query.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.test.ts index 63c779ab4b520..a85d084f83d83 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/create_query.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.test.ts @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import { set } from 'lodash'; -import { createTypeFilter, createQuery } from '../create_query.js'; +import { createTypeFilter, createQuery } from './create_query'; describe('Create Type Filter', () => { it('Builds a type filter syntax', () => { const typeFilter = createTypeFilter('my_type'); - expect(typeFilter).to.eql({ + expect(typeFilter).toStrictEqual({ bool: { should: [{ term: { _type: 'my_type' } }, { term: { type: 'my_type' } }] }, }); }); @@ -36,7 +35,7 @@ describe('Create Query', () => { ], }, }; - expect(result).to.be.eql(expected); + expect(result).toStrictEqual(expected); }); it('Uses `type` option to add type filter with minimal fields', () => { @@ -47,7 +46,7 @@ describe('Create Query', () => { { term: { _type: 'test-type-yay' } }, { term: { type: 'test-type-yay' } }, ]); - expect(result).to.be.eql(expected); + expect(result).toStrictEqual(expected); }); it('Uses `type` option to add type filter with all other option fields', () => { @@ -77,6 +76,6 @@ describe('Create Query', () => { ], }, }; - expect(result).to.be.eql(expected); + expect(result).toStrictEqual(expected); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.ts similarity index 80% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.ts index 6fcbb677b307d..9a801094458bd 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/create_query.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { defaults } from 'lodash'; import moment from 'moment'; /* @@ -14,7 +13,7 @@ import moment from 'moment'; * TODO: this backwards compatibility helper will only be supported for 5.x-6. This * function should be removed in 7.0 */ -export const createTypeFilter = type => { +export const createTypeFilter = (type: string) => { return { bool: { should: [{ term: { _type: type } }, { term: { type } }], @@ -22,6 +21,18 @@ export const createTypeFilter = type => { }; }; +export interface QueryOptions { + type?: string; + filters?: object[]; + clusterUuid?: string; + start?: string | number; + end?: string | number; +} + +interface RangeFilter { + range: { [key: string]: { format?: string; gte?: string | number; lte?: string | number } }; +} + /* * Creates the boilerplace for querying monitoring data, including filling in * start time and end time, and injecting additional filters. @@ -36,9 +47,8 @@ export const createTypeFilter = type => { * @param {Date} options.start - numeric timestamp (optional) * @param {Date} options.end - numeric timestamp (optional) */ -export function createQuery(options) { - options = defaults(options, { filters: [] }); - const { type, clusterUuid, start, end, filters } = options; +export function createQuery(options: QueryOptions) { + const { type, clusterUuid, start, end, filters = [] } = options; let typeFilter; if (type) { @@ -50,7 +60,7 @@ export function createQuery(options) { clusterUuidFilter = { term: { cluster_uuid: clusterUuid } }; } - let timeRangeFilter; + let timeRangeFilter: RangeFilter | undefined; if (start || end) { timeRangeFilter = { range: { diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_all_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts similarity index 78% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_all_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts index f27fde50242f4..470642f9dd8a3 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_all_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.test.ts @@ -4,12 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import sinon from 'sinon'; -import { addStackStats, getAllStats, handleAllStats } from '../get_all_stats'; +import { addStackStats, getAllStats, handleAllStats } from './get_all_stats'; +import { ESClusterStats } from './get_es_stats'; +import { KibanaStats } from './get_kibana_stats'; +import { ClustersHighLevelStats } from './get_high_level_stats'; -// FAILING: https://github.com/elastic/kibana/issues/51371 -describe.skip('get_all_stats', () => { +describe('get_all_stats', () => { const size = 123; const start = 0; const end = 1; @@ -100,9 +101,6 @@ describe.skip('get_all_stats', () => { describe('getAllStats', () => { it('returns clusters', async () => { - const clusterUuidsResponse = { - aggregations: { cluster_uuids: { buckets: [{ key: 'a' }] } }, - }; const esStatsResponse = { hits: { hits: [{ _id: 'a', _source: { cluster_uuid: 'a' } }], @@ -177,15 +175,25 @@ describe.skip('get_all_stats', () => { callCluster .withArgs('search') .onCall(0) - .returns(Promise.resolve(clusterUuidsResponse)) - .onCall(1) .returns(Promise.resolve(esStatsResponse)) - .onCall(2) + .onCall(1) .returns(Promise.resolve(kibanaStatsResponse)) + .onCall(2) + .returns(Promise.resolve(logstashStatsResponse)) .onCall(3) - .returns(Promise.resolve(logstashStatsResponse)); + .returns(Promise.resolve({})) // Beats stats + .onCall(4) + .returns(Promise.resolve({})); // Beats state - expect(await getAllStats({ callCluster, server, start, end })).to.eql(allClusters); + expect( + await getAllStats([{ clusterUuid: 'a' }], { + callCluster: callCluster as any, + usageCollection: {} as any, + server, + start, + end, + }) + ).toStrictEqual(allClusters); }); it('returns empty clusters', async () => { @@ -195,21 +203,33 @@ describe.skip('get_all_stats', () => { callCluster.withArgs('search').returns(Promise.resolve(clusterUuidsResponse)); - expect(await getAllStats({ callCluster, server, start, end })).to.eql([]); + expect( + await getAllStats([], { + callCluster: callCluster as any, + usageCollection: {} as any, + server, + start, + end, + }) + ).toStrictEqual([]); }); }); describe('handleAllStats', () => { it('handles response', () => { - const clusters = handleAllStats(esClusters, { kibana: kibanaStats, logstash: logstashStats }); + const clusters = handleAllStats(esClusters as ESClusterStats[], { + kibana: (kibanaStats as unknown) as KibanaStats, + logstash: (logstashStats as unknown) as ClustersHighLevelStats, + beats: {}, + }); - expect(clusters).to.eql(expectedClusters); + expect(clusters).toStrictEqual(expectedClusters); }); it('handles no clusters response', () => { - const clusters = handleAllStats([], {}); + const clusters = handleAllStats([], {} as any); - expect(clusters).to.have.length(0); + expect(clusters).toHaveLength(0); }); }); @@ -230,9 +250,9 @@ describe.skip('get_all_stats', () => { }, }; - addStackStats(cluster, stats, 'xyz'); + addStackStats(cluster as ESClusterStats, stats, 'xyz'); - expect(cluster.stack_stats.xyz).to.be(stats.a); + expect((cluster as any).stack_stats.xyz).toStrictEqual(stats.a); }); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts similarity index 66% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts index 87281a19141ae..aa5e937387daf 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_all_stats.ts @@ -6,22 +6,26 @@ import { get, set, merge } from 'lodash'; +import { StatsGetter } from 'src/legacy/core_plugins/telemetry/server/collection_manager'; import { LOGSTASH_SYSTEM_ID, KIBANA_SYSTEM_ID, BEATS_SYSTEM_ID } from '../../common/constants'; -import { getElasticsearchStats } from './get_es_stats'; -import { getKibanaStats } from './get_kibana_stats'; +import { getElasticsearchStats, ESClusterStats } from './get_es_stats'; +import { getKibanaStats, KibanaStats } from './get_kibana_stats'; import { getBeatsStats } from './get_beats_stats'; import { getHighLevelStats } from './get_high_level_stats'; +type PromiseReturnType any> = ReturnType extends Promise + ? R + : T; + /** * Get statistics for all products joined by Elasticsearch cluster. + * Returns the array of clusters joined with the Kibana and Logstash instances. * - * @param {Object} server The Kibana server instance used to call ES as the internal user - * @param {function} callCluster The callWithRequest or callWithInternalUser handler - * @param {Date} start The starting range to request data - * @param {Date} end The ending range to request data - * @return {Promise} The array of clusters joined with the Kibana and Logstash instances. */ -export async function getAllStats(clustersDetails, { server, callCluster, start, end }) { +export const getAllStats: StatsGetter = async ( + clustersDetails, + { server, callCluster, start, end } +) => { const clusterUuids = clustersDetails.map(clusterDetails => clusterDetails.clusterUuid); const [esClusters, kibana, logstash, beats] = await Promise.all([ @@ -32,7 +36,7 @@ export async function getAllStats(clustersDetails, { server, callCluster, start, ]); return handleAllStats(esClusters, { kibana, logstash, beats }); -} +}; /** * Combine the statistics from the stack to create "cluster" stats that associate all products together based on the cluster @@ -41,9 +45,21 @@ export async function getAllStats(clustersDetails, { server, callCluster, start, * @param {Array} clusters The Elasticsearch clusters * @param {Object} kibana The Kibana instances keyed by Cluster UUID * @param {Object} logstash The Logstash nodes keyed by Cluster UUID - * @return {Array} The clusters joined with the Kibana and Logstash instances under each cluster's {@code stack_stats}. + * + * Returns the clusters joined with the Kibana and Logstash instances under each cluster's {@code stack_stats}. */ -export function handleAllStats(clusters, { kibana, logstash, beats }) { +export function handleAllStats( + clusters: ESClusterStats[], + { + kibana, + logstash, + beats, + }: { + kibana: KibanaStats; + logstash: PromiseReturnType; + beats: PromiseReturnType; + } +) { return clusters.map(cluster => { // if they are using Kibana or Logstash, then add it to the cluster details under cluster.stack_stats addStackStats(cluster, kibana, KIBANA_SYSTEM_ID); @@ -62,8 +78,12 @@ export function handleAllStats(clusters, { kibana, logstash, beats }) { * @param {Object} allProductStats Product stats, keyed by Cluster UUID * @param {String} product The product name being added (e.g., 'kibana' or 'logstash') */ -export function addStackStats(cluster, allProductStats, product) { - const productStats = get(allProductStats, cluster.cluster_uuid); +export function addStackStats( + cluster: ESClusterStats & { stack_stats?: { [product: string]: K } }, + allProductStats: T, + product: string +) { + const productStats = allProductStats[cluster.cluster_uuid]; // Don't add it if they're not using (or configured to report stats) this product for this cluster if (productStats) { @@ -75,12 +95,20 @@ export function addStackStats(cluster, allProductStats, product) { } } -export function mergeXPackStats(cluster, allProductStats, path, product) { +export function mergeXPackStats( + cluster: ESClusterStats & { stack_stats?: { xpack?: { [product: string]: unknown } } }, + allProductStats: T, + path: string, + product: string +) { const productStats = get(allProductStats, cluster.cluster_uuid + '.' + path); if (productStats || productStats === 0) { - if (!get(cluster, 'stack_stats.xpack')) { - set(cluster, 'stack_stats.xpack', {}); + if (!cluster.stack_stats) { + cluster.stack_stats = {}; + } + if (!cluster.stack_stats.xpack) { + cluster.stack_stats.xpack = {}; } const mergeStats = {}; diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_beats_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.test.ts similarity index 75% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_beats_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.test.ts index 522be71555fba..30888e1af3f53 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_beats_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.test.ts @@ -4,10 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fetchBeatsStats, processResults } from '../get_beats_stats'; +import { fetchBeatsStats, processResults } from './get_beats_stats'; import sinon from 'sinon'; -import expect from '@kbn/expect'; -import beatsStatsResultSet from './fixtures/beats_stats_results'; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const beatsStatsResultSet = require('./__mocks__/fixtures/beats_stats_results'); const getBaseOptions = () => ({ clusters: {}, @@ -22,8 +22,8 @@ describe('Get Beats Stats', () => { const clusterUuids = ['aCluster', 'bCluster', 'cCluster']; const start = 100; const end = 200; - let server; - let callCluster; + let server = { config: () => ({ get: sinon.stub() }) }; + let callCluster = sinon.stub(); beforeEach(() => { const getStub = { get: sinon.stub() }; @@ -32,34 +32,34 @@ describe('Get Beats Stats', () => { callCluster = sinon.stub(); }); - it('should set `from: 0, to: 10000` in the query', () => { - fetchBeatsStats(server, callCluster, clusterUuids, start, end); + it('should set `from: 0, to: 10000` in the query', async () => { + await fetchBeatsStats(server, callCluster, clusterUuids, start, end, {} as any); const { args } = callCluster.firstCall; const [api, { body }] = args; - expect(api).to.be('search'); - expect(body.from).to.be(0); - expect(body.size).to.be(10000); + expect(api).toEqual('search'); + expect(body.from).toEqual(0); + expect(body.size).toEqual(10000); }); - it('should set `from: 10000, from: 10000` in the query', () => { - fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 1 }); + it('should set `from: 10000, from: 10000` in the query', async () => { + await fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 1 } as any); const { args } = callCluster.firstCall; const [api, { body }] = args; - expect(api).to.be('search'); - expect(body.from).to.be(10000); - expect(body.size).to.be(10000); + expect(api).toEqual('search'); + expect(body.from).toEqual(10000); + expect(body.size).toEqual(10000); }); - it('should set `from: 20000, from: 10000` in the query', () => { - fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 2 }); + it('should set `from: 20000, from: 10000` in the query', async () => { + await fetchBeatsStats(server, callCluster, clusterUuids, start, end, { page: 2 } as any); const { args } = callCluster.firstCall; const [api, { body }] = args; - expect(api).to.be('search'); - expect(body.from).to.be(20000); - expect(body.size).to.be(10000); + expect(api).toEqual('search'); + expect(body.from).toEqual(20000); + expect(body.size).toEqual(10000); }); }); @@ -68,9 +68,9 @@ describe('Get Beats Stats', () => { const resultsEmpty = undefined; const options = getBaseOptions(); - processResults(resultsEmpty, options); + processResults(resultsEmpty as any, options); - expect(options.clusters).to.eql({}); + expect(options.clusters).toStrictEqual({}); }); it('should summarize single result with some missing fields', () => { @@ -92,9 +92,9 @@ describe('Get Beats Stats', () => { }; const options = getBaseOptions(); - processResults(results, options); + processResults(results as any, options); - expect(options.clusters).to.eql({ + expect(options.clusters).toStrictEqual({ FlV4ckTxQ0a78hmBkzzc9A: { count: 1, versions: {}, @@ -122,11 +122,11 @@ describe('Get Beats Stats', () => { const options = getBaseOptions(); // beatsStatsResultSet is an array of many small query results - beatsStatsResultSet.forEach(results => { + beatsStatsResultSet.forEach((results: any) => { processResults(results, options); }); - expect(options.clusters).to.eql({ + expect(options.clusters).toStrictEqual({ W7hppdX7R229Oy3KQbZrTw: { count: 5, versions: { '7.0.0-alpha1': 5 }, diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts similarity index 60% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts index 5722228b60207..975a3bfee6333 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_beats_stats.ts @@ -5,6 +5,8 @@ */ import { get } from 'lodash'; +import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager'; +import { SearchResponse } from 'elasticsearch'; import { createQuery } from './create_query'; import { INDEX_PATTERN_BEATS } from '../../common/constants'; @@ -33,6 +35,107 @@ const getBaseStats = () => ({ }, }); +export interface BeatsStats { + cluster_uuid: string; + beats_stats?: { + beat?: { + version?: string; + type?: string; + host?: string; + }; + metrics?: { + libbeat?: { + output?: { + type?: string; + }; + pipeline?: { + events?: { + published?: number; + }; + }; + }; + }; + }; + beats_state?: { + beat?: { + type?: string; + }; + state?: { + input?: { + names: string[]; + count: number; + }; + module?: { + names: string[]; + count: number; + }; + heartbeat?: HeartbeatBase; + functionbeat?: { + functions?: { + count?: number; + }; + }; + host?: { + architecture: string; + os: { platform: string }; + }; + }; + }; +} + +interface HeartbeatBase { + monitors: number; + endpoints: number; + // I have to add the '| number' bit because otherwise TS complains about 'monitors' and 'endpoints' not being of type HeartbeatBase + [key: string]: HeartbeatBase | number | undefined; +} + +export interface BeatsBaseStats { + // stats + versions: { [version: string]: number }; + types: { [type: string]: number }; + outputs: { [outputType: string]: number }; + count: number; + eventsPublished: number; + hosts: number; + // state + input: { + count: number; + names: string[]; + }; + module: { + count: number; + names: string[]; + }; + architecture: { + count: number; + architectures: BeatsArchitecture[]; + }; + heartbeat?: HeartbeatBase; + functionbeat?: { + functions: { + count: number; + }; + }; +} + +export interface BeatsProcessOptions { + clusters: { [clusterUuid: string]: BeatsBaseStats }; // the result object to be built up + clusterHostSets: { [clusterUuid: string]: Set }; // passed to processResults for tracking state in the results generation + clusterInputSets: { [clusterUuid: string]: Set }; // passed to processResults for tracking state in the results generation + clusterModuleSets: { [clusterUuid: string]: Set }; // passed to processResults for tracking state in the results generation + clusterArchitectureMaps: { + // passed to processResults for tracking state in the results generation + [clusterUuid: string]: Map; + }; +} + +export interface BeatsArchitecture { + name: string; + architecture: string; + count: number; +} + /* * Update a clusters object with processed beat stats * @param {Array} results - array of Beats docs from ES @@ -41,12 +144,18 @@ const getBaseStats = () => ({ * @param {Object} clusterModuleSets - the object keyed by cluster UUIDs to count the unique modules */ export function processResults( - results = [], - { clusters, clusterHostSets, clusterInputSets, clusterModuleSets, clusterArchitectureMaps } + results: SearchResponse, + { + clusters, + clusterHostSets, + clusterInputSets, + clusterModuleSets, + clusterArchitectureMaps, + }: BeatsProcessOptions ) { - const currHits = get(results, 'hits.hits', []); + const currHits = results?.hits?.hits || []; currHits.forEach(hit => { - const clusterUuid = get(hit, '_source.cluster_uuid'); + const clusterUuid = hit._source.cluster_uuid; if (clusters[clusterUuid] === undefined) { clusters[clusterUuid] = getBaseStats(); clusterHostSets[clusterUuid] = new Set(); @@ -57,30 +166,30 @@ export function processResults( const processBeatsStatsResults = () => { const { versions, types, outputs } = clusters[clusterUuid]; - const thisVersion = get(hit, '_source.beats_stats.beat.version'); + const thisVersion = hit._source.beats_stats?.beat?.version; if (thisVersion !== undefined) { const thisVersionAccum = versions[thisVersion] || 0; versions[thisVersion] = thisVersionAccum + 1; } - const thisType = get(hit, '_source.beats_stats.beat.type'); + const thisType = hit._source.beats_stats?.beat?.type; if (thisType !== undefined) { const thisTypeAccum = types[thisType] || 0; types[thisType] = thisTypeAccum + 1; } - const thisOutput = get(hit, '_source.beats_stats.metrics.libbeat.output.type'); + const thisOutput = hit._source.beats_stats?.metrics?.libbeat?.output?.type; if (thisOutput !== undefined) { const thisOutputAccum = outputs[thisOutput] || 0; outputs[thisOutput] = thisOutputAccum + 1; } - const thisEvents = get(hit, '_source.beats_stats.metrics.libbeat.pipeline.events.published'); + const thisEvents = hit._source.beats_stats?.metrics?.libbeat?.pipeline?.events?.published; if (thisEvents !== undefined) { clusters[clusterUuid].eventsPublished += thisEvents; } - const thisHost = get(hit, '_source.beats_stats.beat.host'); + const thisHost = hit._source.beats_stats?.beat?.host; if (thisHost !== undefined) { const hostsMap = clusterHostSets[clusterUuid]; hostsMap.add(thisHost); @@ -89,7 +198,7 @@ export function processResults( }; const processBeatsStateResults = () => { - const stateInput = get(hit, '_source.beats_state.state.input'); + const stateInput = hit._source.beats_state?.state?.input; if (stateInput !== undefined) { const inputSet = clusterInputSets[clusterUuid]; stateInput.names.forEach(name => inputSet.add(name)); @@ -97,8 +206,8 @@ export function processResults( clusters[clusterUuid].input.count += stateInput.count; } - const stateModule = get(hit, '_source.beats_state.state.module'); - const statsType = get(hit, '_source.beats_state.beat.type'); + const stateModule = hit._source.beats_state?.state?.module; + const statsType = hit._source.beats_state?.beat?.type; if (stateModule !== undefined) { const moduleSet = clusterModuleSets[clusterUuid]; stateModule.names.forEach(name => moduleSet.add(statsType + '.' + name)); @@ -106,7 +215,7 @@ export function processResults( clusters[clusterUuid].module.count += stateModule.count; } - const heartbeatState = get(hit, '_source.beats_state.state.heartbeat'); + const heartbeatState = hit._source.beats_state?.state?.heartbeat; if (heartbeatState !== undefined) { if (!clusters[clusterUuid].hasOwnProperty('heartbeat')) { clusters[clusterUuid].heartbeat = { @@ -114,7 +223,7 @@ export function processResults( endpoints: 0, }; } - const clusterHb = clusters[clusterUuid].heartbeat; + const clusterHb = clusters[clusterUuid].heartbeat!; clusterHb.monitors += heartbeatState.monitors; clusterHb.endpoints += heartbeatState.endpoints; @@ -133,12 +242,12 @@ export function processResults( endpoints: 0, }; } - clusterHb[proto].monitors += val.monitors; - clusterHb[proto].endpoints += val.endpoints; + (clusterHb[proto] as HeartbeatBase).monitors += val.monitors; + (clusterHb[proto] as HeartbeatBase).endpoints += val.endpoints; } } - const functionbeatState = get(hit, '_source.beats_state.state.functionbeat'); + const functionbeatState = hit._source.beats_state?.state?.functionbeat; if (functionbeatState !== undefined) { if (!clusters[clusterUuid].hasOwnProperty('functionbeat')) { clusters[clusterUuid].functionbeat = { @@ -148,14 +257,11 @@ export function processResults( }; } - clusters[clusterUuid].functionbeat.functions.count += get( - functionbeatState, - 'functions.count', - 0 - ); + clusters[clusterUuid].functionbeat!.functions.count += + functionbeatState.functions?.count || 0; } - const stateHost = get(hit, '_source.beats_state.state.host'); + const stateHost = hit._source.beats_state?.state?.host; if (stateHost !== undefined) { const hostMap = clusterArchitectureMaps[clusterUuid]; const hostKey = `${stateHost.architecture}/${stateHost.os.platform}`; @@ -198,14 +304,14 @@ export function processResults( * @return {Promise} */ async function fetchBeatsByType( - server, - callCluster, - clusterUuids, - start, - end, - { page = 0, ...options } = {}, - type -) { + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'], + end: StatsCollectionConfig['end'], + { page = 0, ...options }: { page?: number } & BeatsProcessOptions, + type: string +): Promise { const params = { index: INDEX_PATTERN_BEATS, ignoreUnavailable: true, @@ -232,7 +338,7 @@ async function fetchBeatsByType( { bool: { must_not: { term: { [`${type}.beat.type`]: 'apm-server' } }, - must: { term: { type: type } }, + must: { term: { type } }, }, }, ], @@ -244,8 +350,8 @@ async function fetchBeatsByType( }, }; - const results = await callCluster('search', params); - const hitsLength = get(results, 'hits.hits.length', 0); + const results = await callCluster>('search', params); + const hitsLength = results?.hits?.hits.length || 0; if (hitsLength > 0) { // further augment the clusters object with more stats processResults(results, options); @@ -265,20 +371,40 @@ async function fetchBeatsByType( return Promise.resolve(); } -export async function fetchBeatsStats(...args) { - return fetchBeatsByType(...args, 'beats_stats'); +export async function fetchBeatsStats( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'], + end: StatsCollectionConfig['end'], + options: { page?: number } & BeatsProcessOptions +) { + return fetchBeatsByType(server, callCluster, clusterUuids, start, end, options, 'beats_stats'); } -export async function fetchBeatsStates(...args) { - return fetchBeatsByType(...args, 'beats_state'); +export async function fetchBeatsStates( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'], + end: StatsCollectionConfig['end'], + options: { page?: number } & BeatsProcessOptions +) { + return fetchBeatsByType(server, callCluster, clusterUuids, start, end, options, 'beats_state'); } /* * Call the function for fetching and summarizing beats stats * @return {Object} - Beats stats in an object keyed by the cluster UUIDs */ -export async function getBeatsStats(server, callCluster, clusterUuids, start, end) { - const options = { +export async function getBeatsStats( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'], + end: StatsCollectionConfig['end'] +) { + const options: BeatsProcessOptions = { clusters: {}, // the result object to be built up clusterHostSets: {}, // passed to processResults for tracking state in the results generation clusterInputSets: {}, // passed to processResults for tracking state in the results generation diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_cluster_uuids.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_cluster_uuids.test.ts similarity index 77% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_cluster_uuids.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_cluster_uuids.test.ts index 1f62c677dbb21..4f952b9dec6da 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_cluster_uuids.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_cluster_uuids.test.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import sinon from 'sinon'; import { getClusterUuids, fetchClusterUuids, handleClusterUuidsResponse, -} from '../get_cluster_uuids'; +} from './get_cluster_uuids'; describe('get_cluster_uuids', () => { const callCluster = sinon.stub(); @@ -35,20 +34,24 @@ describe('get_cluster_uuids', () => { const expectedUuids = response.aggregations.cluster_uuids.buckets .map(bucket => bucket.key) .map(expectedUuid => ({ clusterUuid: expectedUuid })); - const start = new Date(); - const end = new Date(); + const start = new Date().toISOString(); + const end = new Date().toISOString(); describe('getClusterUuids', () => { it('returns cluster UUIDs', async () => { callCluster.withArgs('search').returns(Promise.resolve(response)); - expect(await getClusterUuids({ server, callCluster, start, end })).to.eql(expectedUuids); + expect( + await getClusterUuids({ server, callCluster, start, end, usageCollection: {} as any }) + ).toStrictEqual(expectedUuids); }); }); describe('fetchClusterUuids', () => { it('searches for clusters', async () => { callCluster.returns(Promise.resolve(response)); - expect(await fetchClusterUuids({ server, callCluster, start, end })).to.be(response); + expect( + await fetchClusterUuids({ server, callCluster, start, end, usageCollection: {} as any }) + ).toStrictEqual(response); }); }); @@ -56,12 +59,12 @@ describe('get_cluster_uuids', () => { // filterPath makes it easy to ignore anything unexpected because it will come back empty it('handles unexpected response', () => { const clusterUuids = handleClusterUuidsResponse({}); - expect(clusterUuids.length).to.be(0); + expect(clusterUuids.length).toStrictEqual(0); }); it('handles valid response', () => { const clusterUuids = handleClusterUuidsResponse(response); - expect(clusterUuids).to.eql(expectedUuids); + expect(clusterUuids).toStrictEqual(expectedUuids); }); it('handles no buckets response', () => { @@ -73,7 +76,7 @@ describe('get_cluster_uuids', () => { }, }); - expect(clusterUuids.length).to.be(0); + expect(clusterUuids.length).toStrictEqual(0); }); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_es_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.test.ts similarity index 82% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_es_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.test.ts index 536e831640fad..70ed2240b47d4 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_es_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.test.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import sinon from 'sinon'; import { fetchElasticsearchStats, getElasticsearchStats, handleElasticsearchStats, -} from '../get_es_stats'; +} from './get_es_stats'; describe('get_es_stats', () => { const callWith = sinon.stub(); @@ -41,7 +40,9 @@ describe('get_es_stats', () => { it('returns clusters', async () => { callWith.withArgs('search').returns(Promise.resolve(response)); - expect(await getElasticsearchStats(server, callWith, clusterUuids)).to.eql(expectedClusters); + expect(await getElasticsearchStats(server, callWith, clusterUuids)).toStrictEqual( + expectedClusters + ); }); }); @@ -49,28 +50,28 @@ describe('get_es_stats', () => { it('searches for clusters', async () => { callWith.returns(response); - expect(await fetchElasticsearchStats(server, callWith, clusterUuids)).to.be(response); + expect(await fetchElasticsearchStats(server, callWith, clusterUuids)).toStrictEqual(response); }); }); describe('handleElasticsearchStats', () => { // filterPath makes it easy to ignore anything unexpected because it will come back empty it('handles unexpected response', () => { - const clusters = handleElasticsearchStats({}); + const clusters = handleElasticsearchStats({} as any); - expect(clusters.length).to.be(0); + expect(clusters.length).toStrictEqual(0); }); it('handles valid response', () => { - const clusters = handleElasticsearchStats(response); + const clusters = handleElasticsearchStats(response as any); - expect(clusters).to.eql(expectedClusters); + expect(clusters).toStrictEqual(expectedClusters); }); it('handles no hits response', () => { - const clusters = handleElasticsearchStats({ hits: { hits: [] } }); + const clusters = handleElasticsearchStats({ hits: { hits: [] } } as any); - expect(clusters.length).to.be(0); + expect(clusters.length).toStrictEqual(0); }); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts similarity index 71% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts index 52d34258b5fa4..f0ae1163d3f52 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_es_stats.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash'; +import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager'; +import { SearchResponse } from 'elasticsearch'; import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants'; /** @@ -13,10 +14,14 @@ import { INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants'; * @param {Object} server The server instance * @param {function} callCluster The callWithRequest or callWithInternalUser handler * @param {Array} clusterUuids The string Cluster UUIDs to fetch details for - * @return {Promise} Array of the Elasticsearch clusters. */ -export function getElasticsearchStats(server, callCluster, clusterUuids) { - return fetchElasticsearchStats(server, callCluster, clusterUuids).then(handleElasticsearchStats); +export async function getElasticsearchStats( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[] +) { + const response = await fetchElasticsearchStats(server, callCluster, clusterUuids); + return handleElasticsearchStats(response); } /** @@ -25,9 +30,14 @@ export function getElasticsearchStats(server, callCluster, clusterUuids) { * @param {Object} server The server instance * @param {function} callCluster The callWithRequest or callWithInternalUser handler * @param {Array} clusterUuids Cluster UUIDs to limit the request against - * @return {Promise} Response for the aggregations to fetch details for the product. + * + * Returns the response for the aggregations to fetch details for the product. */ -export function fetchElasticsearchStats(server, callCluster, clusterUuids) { +export function fetchElasticsearchStats( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[] +) { const config = server.config(); const params = { index: INDEX_PATTERN_ELASTICSEARCH, @@ -67,13 +77,16 @@ export function fetchElasticsearchStats(server, callCluster, clusterUuids) { return callCluster('search', params); } +export interface ESClusterStats { + cluster_uuid: string; + type: 'cluster_stats'; +} + /** * Extract the cluster stats for each cluster. - * - * @return {Array} The Elasticsearch clusters. */ -export function handleElasticsearchStats(response) { - const clusters = get(response, 'hits.hits', []); +export function handleElasticsearchStats(response: SearchResponse) { + const clusters = response.hits?.hits || []; return clusters.map(cluster => cluster._source); } diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_high_level_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.test.ts similarity index 91% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_high_level_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.test.ts index 1c1f8dc888d01..76c80e2eb3d37 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_high_level_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.test.ts @@ -4,13 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import sinon from 'sinon'; import { fetchHighLevelStats, getHighLevelStats, handleHighLevelStatsResponse, -} from '../get_high_level_stats'; +} from './get_high_level_stats'; describe('get_high_level_stats', () => { const callWith = sinon.stub(); @@ -244,9 +243,9 @@ describe('get_high_level_stats', () => { it('returns clusters', async () => { callWith.withArgs('search').returns(Promise.resolve(response)); - expect(await getHighLevelStats(server, callWith, clusterUuids, start, end, product)).to.eql( - expectedClusters - ); + expect( + await getHighLevelStats(server, callWith, clusterUuids, start, end, product) + ).toStrictEqual(expectedClusters); }); }); @@ -254,30 +253,30 @@ describe('get_high_level_stats', () => { it('searches for clusters', async () => { callWith.returns(Promise.resolve(response)); - expect(await fetchHighLevelStats(server, callWith, clusterUuids, start, end, product)).to.be( - response - ); + expect( + await fetchHighLevelStats(server, callWith, clusterUuids, start, end, product) + ).toStrictEqual(response); }); }); describe('handleHighLevelStatsResponse', () => { // filterPath makes it easy to ignore anything unexpected because it will come back empty it('handles unexpected response', () => { - const clusters = handleHighLevelStatsResponse({}, product); + const clusters = handleHighLevelStatsResponse({} as any, product); - expect(clusters).to.eql({}); + expect(clusters).toStrictEqual({}); }); it('handles valid response', () => { - const clusters = handleHighLevelStatsResponse(response, product); + const clusters = handleHighLevelStatsResponse(response as any, product); - expect(clusters).to.eql(expectedClusters); + expect(clusters).toStrictEqual(expectedClusters); }); it('handles no hits response', () => { - const clusters = handleHighLevelStatsResponse({ hits: { hits: [] } }, product); + const clusters = handleHighLevelStatsResponse({ hits: { hits: [] } } as any, product); - expect(clusters).to.eql({}); + expect(clusters).toStrictEqual({}); }); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts similarity index 66% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts index b87f632308e4d..f67f80940d9f4 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_high_level_stats.ts @@ -5,6 +5,8 @@ */ import { get } from 'lodash'; +import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager'; +import { SearchResponse } from 'elasticsearch'; import { createQuery } from './create_query'; import { INDEX_PATTERN_KIBANA, @@ -17,13 +19,40 @@ import { TELEMETRY_QUERY_SOURCE, } from '../../common/constants'; +export interface ClusterCloudStats { + name: string; + count: number; + vms: number; + regions: Array<{ region: string; count: number }>; + vm_types: Array<{ vm_type: string; count: number }>; + zones: Array<{ zone: string; count: number }>; +} + +export interface ClusterHighLevelStats { + count: number; + versions: Array<{ version: string; count: number }>; + os: { + platforms: Array<{ platform: string; count: number }>; + platformReleases: Array<{ platformRelease: string; count: number }>; + distros: Array<{ distro: string; count: number }>; + distroReleases: Array<{ distroRelease: string; count: number }>; + }; + cloud: ClusterCloudStats[] | undefined; +} + +export interface ClustersHighLevelStats { + [clusterUuid: string]: ClusterHighLevelStats; +} + +type Counter = Map; + /** * Update a counter associated with the {@code key}. * * @param {Map} map Map to update the counter for the {@code key}. * @param {String} key The key to increment a counter for. */ -function incrementByKey(map, key) { +function incrementByKey(map: Counter, key?: string) { if (!key) { return; } @@ -37,13 +66,29 @@ function incrementByKey(map, key) { map.set(key, count + 1); } +interface InternalCloudMap { + count: number; + unique: Set; + vm_type: Counter; + region: Counter; + zone: Counter; +} + +interface CloudEntry { + id: string; + name: string; + vm_type: string; + region: string; + zone: string; +} + /** * Help to reduce Cloud metrics into unidentifiable metrics (e.g., count IDs so that they can be dropped). * * @param {Map} clouds Existing cloud data by cloud name. * @param {Object} cloud Cloud object loaded from Elasticsearch data. */ -function reduceCloudForCluster(cloudMap, cloud) { +function reduceCloudForCluster(cloudMap: Map, cloud?: CloudEntry) { if (!cloud) { return; } @@ -74,22 +119,48 @@ function reduceCloudForCluster(cloudMap, cloud) { incrementByKey(cloudByName.zone, cloud.zone); } +interface InternalClusterMap { + count: number; + versions: Counter; + cloudMap: Map; + os: { + platforms: Counter; + platformReleases: Counter; + distros: Counter; + distroReleases: Counter; + }; +} + +interface OSData { + platform?: string; + platformRelease?: string; + distro?: string; + distroRelease?: string; +} + /** * Group the instances (hits) by clusters. * * @param {Array} instances Array of hits from the request containing the cluster UUID and version. * @param {String} product The product to limit too ('kibana', 'logstash', 'beats') - * @return {Map} A map of the Cluster UUID to an {@link Object} containing the {@code count} and {@code versions} {@link Map} + * + * Returns a map of the Cluster UUID to an {@link Object} containing the {@code count} and {@code versions} {@link Map} */ -function groupInstancesByCluster(instances, product) { - const clusterMap = new Map(); +function groupInstancesByCluster( + instances: Array<{ _source: T }>, + product: string +) { + const clusterMap = new Map(); // hits are sorted arbitrarily by product UUID instances.map(instance => { - const clusterUuid = get(instance, '_source.cluster_uuid'); - const version = get(instance, `_source.${product}_stats.${product}.version`); - const cloud = get(instance, `_source.${product}_stats.cloud`); - const os = get(instance, `_source.${product}_stats.os`); + const clusterUuid = instance._source.cluster_uuid; + const version: string | undefined = get( + instance, + `_source.${product}_stats.${product}.version` + ); + const cloud: CloudEntry | undefined = get(instance, `_source.${product}_stats.cloud`); + const os: OSData | undefined = get(instance, `_source.${product}_stats.os`); if (clusterUuid) { let cluster = clusterMap.get(clusterUuid); @@ -134,16 +205,12 @@ function groupInstancesByCluster(instances, product) { * { [keyName]: key1, count: value1 }, * { [keyName]: key2, count: value2 } * ] - * - * @param {Map} map [description] - * @param {String} keyName [description] - * @return {Array} [description] */ -function mapToList(map, keyName) { - const list = []; +function mapToList(map: Map, keyName: string): T[] { + const list: T[] = []; for (const [key, count] of map) { - list.push({ [keyName]: key, count }); + list.push(({ [keyName]: key, count } as unknown) as T); } return list; @@ -154,7 +221,7 @@ function mapToList(map, keyName) { * * @param {*} product The product id, which should be in the constants file */ -function getIndexPatternForStackProduct(product) { +function getIndexPatternForStackProduct(product: string) { switch (product) { case KIBANA_SYSTEM_ID: return INDEX_PATTERN_KIBANA; @@ -176,23 +243,41 @@ function getIndexPatternForStackProduct(product) { * @param {Date} start Start time to limit the stats * @param {Date} end End time to limit the stats * @param {String} product The product to limit too ('kibana', 'logstash', 'beats') - * @return {Promise} Object keyed by the cluster UUIDs to make grouping easier. + * + * Returns an object keyed by the cluster UUIDs to make grouping easier. */ -export function getHighLevelStats(server, callCluster, clusterUuids, start, end, product) { - return fetchHighLevelStats( +export async function getHighLevelStats( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'], + end: StatsCollectionConfig['end'], + product: string +) { + const response = await fetchHighLevelStats( server, callCluster, clusterUuids, start, end, product - ).then(response => handleHighLevelStatsResponse(response, product)); + ); + return handleHighLevelStatsResponse(response, product); } -export async function fetchHighLevelStats(server, callCluster, clusterUuids, start, end, product) { +export async function fetchHighLevelStats< + T extends { cluster_uuid?: string } = { cluster_uuid?: string } +>( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'] | undefined, + end: StatsCollectionConfig['end'] | undefined, + product: string +): Promise> { const config = server.config(); const isKibanaIndex = product === KIBANA_SYSTEM_ID; - const filters = [{ terms: { cluster_uuid: clusterUuids } }]; + const filters: object[] = [{ terms: { cluster_uuid: clusterUuids } }]; // we should supply this from a parameter in the future so that this remains generic if (isKibanaIndex) { @@ -257,13 +342,17 @@ export async function fetchHighLevelStats(server, callCluster, clusterUuids, sta * * @param {Object} response The response from the aggregation * @param {String} product The product to limit too ('kibana', 'logstash', 'beats') - * @return {Object} Object keyed by the cluster UUIDs to make grouping easier. + * + * Returns an object keyed by the cluster UUIDs to make grouping easier. */ -export function handleHighLevelStatsResponse(response, product) { - const instances = get(response, 'hits.hits', []); +export function handleHighLevelStatsResponse( + response: SearchResponse<{ cluster_uuid?: string }>, + product: string +) { + const instances = response.hits?.hits || []; const clusterMap = groupInstancesByCluster(instances, product); - const clusters = {}; + const clusters: ClustersHighLevelStats = {}; for (const [clusterUuid, cluster] of clusterMap) { // it's unlikely this will be an array of more than one, but it is one just incase @@ -271,14 +360,15 @@ export function handleHighLevelStatsResponse(response, product) { // remap the clouds (most likely singular or empty) for (const [name, cloud] of cluster.cloudMap) { - clouds.push({ + const cloudStats: ClusterCloudStats = { name, count: cloud.count, vms: cloud.unique.size, regions: mapToList(cloud.region, 'region'), vm_types: mapToList(cloud.vm_type, 'vm_type'), zones: mapToList(cloud.zone, 'zone'), - }); + }; + clouds.push(cloudStats); } // map stats for product by cluster so that it can be joined with ES cluster stats diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_kibana_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.test.ts similarity index 79% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_kibana_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.test.ts index 98e0afa28fba3..0092e848c827b 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/__tests__/get_kibana_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.test.ts @@ -4,19 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { getUsageStats, combineStats, rollUpTotals, ensureTimeSpan } from '../get_kibana_stats'; -import expect from '@kbn/expect'; +import { + getUsageStats, + combineStats, + rollUpTotals, + ensureTimeSpan, + KibanaUsageStats, +} from './get_kibana_stats'; +import { SearchResponse } from 'elasticsearch'; describe('Get Kibana Stats', () => { describe('Make a map of usage stats for each cluster', () => { - it('passes through if there are no kibana instances', () => { - const rawStats = {}; - expect(getUsageStats(rawStats)).to.eql({}); + test('passes through if there are no kibana instances', () => { + const rawStats = {} as SearchResponse; + expect(getUsageStats(rawStats)).toStrictEqual({}); }); describe('with single cluster', () => { describe('single index', () => { - it('for a single unused instance', () => { + test('for a single unused instance', () => { const rawStats = { hits: { hits: [ @@ -39,7 +45,7 @@ describe('Get Kibana Stats', () => { }, ], }, - }; + } as any; const expected = { clusterone: { dashboard: { total: 0 }, @@ -53,10 +59,10 @@ describe('Get Kibana Stats', () => { }, }; - expect(getUsageStats(rawStats)).to.eql(expected); + expect(getUsageStats(rawStats)).toStrictEqual(expected); }); - it('for a single instance of active usage', () => { + test('for a single instance of active usage', () => { const rawStats = { hits: { hits: [ @@ -79,7 +85,7 @@ describe('Get Kibana Stats', () => { }, ], }, - }; + } as any; const expected = { clusterone: { dashboard: { total: 1 }, @@ -92,11 +98,49 @@ describe('Get Kibana Stats', () => { plugins: {}, }, }; + expect(getUsageStats(rawStats)).toStrictEqual(expected); + }); - expect(getUsageStats(rawStats)).to.eql(expected); + test('it merges the plugin stats and kibana', () => { + const rawStats = { + hits: { + hits: [ + { + _source: { + cluster_uuid: 'clusterone', + kibana_stats: { + kibana: { version: '7.0.0-alpha1-test02' }, + usage: { + dashboard: { total: 1 }, + visualization: { total: 3 }, + search: { total: 1 }, + index_pattern: { total: 1 }, + graph_workspace: { total: 1 }, + timelion_sheet: { total: 1 }, + index: '.kibana-test-01', + }, + }, + }, + }, + ], + }, + } as any; + const expected = { + clusterone: { + dashboard: { total: 1 }, + visualization: { total: 3 }, + search: { total: 1 }, + index_pattern: { total: 1 }, + graph_workspace: { total: 1 }, + timelion_sheet: { total: 1 }, + indices: 1, + plugins: {}, + }, + }; + expect(getUsageStats(rawStats)).toStrictEqual(expected); }); - it('flattens x-pack stats', () => { + test('flattens x-pack stats', () => { const rawStats = { hits: { hits: [ @@ -126,8 +170,9 @@ describe('Get Kibana Stats', () => { }, ], }, - }; - expect(getUsageStats(rawStats)).to.eql({ + } as any; + + expect(getUsageStats(rawStats)).toStrictEqual({ clusterone: { dashboard: { total: 1 }, visualization: { total: 3 }, @@ -143,7 +188,7 @@ describe('Get Kibana Stats', () => { }); describe('separate indices', () => { - it('with one unused instance', () => { + test('with one unused instance', () => { const rawStats = { hits: { hits: [ @@ -200,7 +245,7 @@ describe('Get Kibana Stats', () => { }, ], }, - }; + } as any; const expected = { clusterone: { dashboard: { total: 1 }, @@ -213,11 +258,10 @@ describe('Get Kibana Stats', () => { plugins: {}, }, }; - - expect(getUsageStats(rawStats)).to.eql(expected); + expect(getUsageStats(rawStats)).toStrictEqual(expected); }); - it('with all actively used instances', () => { + test('with all actively used instances', () => { const rawStats = { hits: { hits: [ @@ -274,7 +318,7 @@ describe('Get Kibana Stats', () => { }, ], }, - }; + } as any; const expected = { clusterone: { dashboard: { total: 4 }, @@ -287,15 +331,14 @@ describe('Get Kibana Stats', () => { plugins: {}, }, }; - - expect(getUsageStats(rawStats)).to.eql(expected); + expect(getUsageStats(rawStats)).toStrictEqual(expected); }); }); }); describe('with multiple clusters', () => { describe('separate indices', () => { - it('with all actively used instances', () => { + test('with all actively used instances', () => { const rawStats = { hits: { hits: [ @@ -369,7 +412,7 @@ describe('Get Kibana Stats', () => { }, ], }, - }; + } as any; const expected = { clusterone: { dashboard: { total: 4 }, @@ -392,29 +435,28 @@ describe('Get Kibana Stats', () => { plugins: {}, }, }; - - expect(getUsageStats(rawStats)).to.eql(expected); + expect(getUsageStats(rawStats)).toStrictEqual(expected); }); }); }); }); describe('Combines usage stats with high-level stats', () => { - it('passes through if there are no kibana instances', () => { + test('passes through if there are no kibana instances', () => { const highLevelStats = {}; const usageStats = {}; - expect(combineStats(highLevelStats, usageStats)).to.eql({}); + expect(combineStats(highLevelStats, usageStats)).toStrictEqual({}); }); describe('adds usage stats to high-level stats', () => { - it('for a single cluster', () => { + test('for a single cluster', () => { const highLevelStats = { clusterone: { count: 2, versions: [{ count: 2, version: '7.0.0-alpha1-test12' }], }, - }; + } as any; const usageStats = { clusterone: { dashboard: { total: 1 }, @@ -428,7 +470,7 @@ describe('Get Kibana Stats', () => { }, }; - expect(combineStats(highLevelStats, usageStats)).to.eql({ + expect(combineStats(highLevelStats, usageStats)).toStrictEqual({ clusterone: { count: 2, dashboard: { total: 1 }, @@ -444,7 +486,7 @@ describe('Get Kibana Stats', () => { }); }); - it('for multiple single clusters', () => { + test('for multiple single clusters', () => { const highLevelStats = { clusterone: { count: 2, @@ -454,7 +496,7 @@ describe('Get Kibana Stats', () => { count: 1, versions: [{ count: 1, version: '7.0.0-alpha1-test14' }], }, - }; + } as any; const usageStats = { clusterone: { dashboard: { total: 1 }, @@ -478,7 +520,7 @@ describe('Get Kibana Stats', () => { }, }; - expect(combineStats(highLevelStats, usageStats)).to.eql({ + expect(combineStats(highLevelStats, usageStats)).toStrictEqual({ clusterone: { count: 2, dashboard: { total: 1 }, @@ -508,16 +550,16 @@ describe('Get Kibana Stats', () => { }); describe('if usage stats are empty', () => { - it('returns just high-level stats', () => { + test('returns just high-level stats', () => { const highLevelStats = { clusterone: { count: 2, versions: [{ count: 2, version: '7.0.0-alpha1-test12' }], }, - }; + } as any; const usageStats = undefined; - expect(combineStats(highLevelStats, usageStats)).to.eql({ + expect(combineStats(highLevelStats, usageStats)).toStrictEqual({ clusterone: { count: 2, versions: [{ count: 2, version: '7.0.0-alpha1-test12' }], @@ -528,64 +570,64 @@ describe('Get Kibana Stats', () => { }); describe('Rolls up stats when there are multiple Kibana indices for a cluster', () => { - it('by combining the `total` fields where previous was 0', () => { - const rollUp = { my_field: { total: 0 } }; + test('by combining the `total` fields where previous was 0', () => { + const rollUp = { my_field: { total: 0 } } as any; const addOn = { my_field: { total: 1 } }; - expect(rollUpTotals(rollUp, addOn, 'my_field')).to.eql({ total: 1 }); + expect(rollUpTotals(rollUp, addOn, 'my_field' as any)).toStrictEqual({ total: 1 }); }); - it('by combining the `total` fields with > 1 for previous and addOn', () => { - const rollUp = { my_field: { total: 1 } }; + test('by combining the `total` fields with > 1 for previous and addOn', () => { + const rollUp = { my_field: { total: 1 } } as any; const addOn = { my_field: { total: 3 } }; - expect(rollUpTotals(rollUp, addOn, 'my_field')).to.eql({ total: 4 }); + expect(rollUpTotals(rollUp, addOn, 'my_field' as any)).toStrictEqual({ total: 4 }); }); }); describe('Ensure minimum time difference', () => { - it('should return start and end as is when none are provided', () => { + test('should return start and end as is when none are provided', () => { const { start, end } = ensureTimeSpan(undefined, undefined); - expect(start).to.be.undefined; - expect(end).to.be.undefined; + expect(start).toBe(undefined); + expect(end).toBe(undefined); }); - it('should return start and end as is when only end is provided', () => { + test('should return start and end as is when only end is provided', () => { const initialEnd = '2020-01-01T00:00:00Z'; const { start, end } = ensureTimeSpan(undefined, initialEnd); - expect(start).to.be.undefined; - expect(end).to.be.equal(initialEnd); + expect(start).toBe(undefined); + expect(end).toEqual(initialEnd); }); - it('should return start and end as is because they are already 24h away', () => { + test('should return start and end as is because they are already 24h away', () => { const initialStart = '2019-12-31T00:00:00Z'; const initialEnd = '2020-01-01T00:00:00Z'; const { start, end } = ensureTimeSpan(initialStart, initialEnd); - expect(start).to.be.equal(initialStart); - expect(end).to.be.equal(initialEnd); + expect(start).toEqual(initialStart); + expect(end).toEqual(initialEnd); }); - it('should return start and end as is because they are already 24h+ away', () => { + test('should return start and end as is because they are already 24h+ away', () => { const initialStart = '2019-12-31T00:00:00Z'; const initialEnd = '2020-01-01T01:00:00Z'; const { start, end } = ensureTimeSpan(initialStart, initialEnd); - expect(start).to.be.equal(initialStart); - expect(end).to.be.equal(initialEnd); + expect(start).toEqual(initialStart); + expect(end).toEqual(initialEnd); }); - it('should modify start to a date 24h before end', () => { + test('should modify start to a date 24h before end', () => { const initialStart = '2020-01-01T00:00:00.000Z'; const initialEnd = '2020-01-01T01:00:00.000Z'; const { start, end } = ensureTimeSpan(initialStart, initialEnd); - expect(start).to.be.equal('2019-12-31T01:00:00.000Z'); - expect(end).to.be.equal(initialEnd); + expect(start).toEqual('2019-12-31T01:00:00.000Z'); + expect(end).toEqual(initialEnd); }); - it('should modify start to a date 24h before now', () => { + test('should modify start to a date 24h before now', () => { const initialStart = new Date().toISOString(); const { start, end } = ensureTimeSpan(initialStart, undefined); - expect(start).to.not.be.equal(initialStart); - expect(end).to.be.undefined; + expect(start).not.toBe(initialStart); + expect(end).toBe(undefined); }); }); }); diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.js b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts similarity index 58% rename from x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.js rename to x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts index 1e22507c5baf4..e2ad64ce04c6b 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.js +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/get_kibana_stats.ts @@ -5,30 +5,78 @@ */ import moment from 'moment'; -import { get, isEmpty, omit } from 'lodash'; +import { isEmpty } from 'lodash'; +import { StatsCollectionConfig } from 'src/legacy/core_plugins/telemetry/server/collection_manager'; +import { SearchResponse } from 'elasticsearch'; import { KIBANA_SYSTEM_ID, TELEMETRY_COLLECTION_INTERVAL } from '../../common/constants'; -import { fetchHighLevelStats, handleHighLevelStatsResponse } from './get_high_level_stats'; +import { + fetchHighLevelStats, + handleHighLevelStatsResponse, + ClustersHighLevelStats, + ClusterHighLevelStats, +} from './get_high_level_stats'; -export function rollUpTotals(rolledUp, addOn, field) { - const rolledUpTotal = get(rolledUp, [field, 'total'], 0); - const addOnTotal = get(addOn, [field, 'total'], 0); +export function rollUpTotals( + rolledUp: ClusterUsageStats, + addOn: { [key: string]: { total?: number } | undefined }, + field: Exclude +) { + const rolledUpTotal = rolledUp[field]?.total || 0; + const addOnTotal = addOn[field]?.total || 0; return { total: rolledUpTotal + addOnTotal }; } -export function rollUpIndices(rolledUp) { +export function rollUpIndices(rolledUp: ClusterUsageStats) { return rolledUp.indices + 1; } +export interface KibanaUsageStats { + cluster_uuid: string; + kibana_stats?: { + usage?: { + index?: string; + } & { + [plugin: string]: { + total: number; + }; + }; + }; +} + +export interface ClusterUsageStats { + dashboard?: { total: number }; + visualization?: { total: number }; + search?: { total: number }; + index_pattern?: { total: number }; + graph_workspace?: { total: number }; + timelion_sheet?: { total: number }; + indices: number; + plugins?: { + xpack?: unknown; + [plugin: string]: unknown; + }; +} + +export interface ClustersUsageStats { + [clusterUuid: string]: ClusterUsageStats | undefined; +} + +export interface KibanaClusterStat extends Partial, ClusterHighLevelStats {} + +export interface KibanaStats { + [clusterUuid: string]: KibanaClusterStat; +} + /* * @param {Object} rawStats */ -export function getUsageStats(rawStats) { +export function getUsageStats(rawStats: SearchResponse) { const clusterIndexCache = new Set(); - const rawStatsHits = get(rawStats, 'hits.hits', []); + const rawStatsHits = rawStats.hits?.hits || []; // get usage stats per cluster / .kibana index return rawStatsHits.reduce((accum, currInstance) => { - const clusterUuid = get(currInstance, '_source.cluster_uuid'); - const currUsage = get(currInstance, '_source.kibana_stats.usage', {}); + const clusterUuid = currInstance._source.cluster_uuid; + const currUsage = currInstance._source.kibana_stats?.usage || {}; const clusterIndexCombination = clusterUuid + currUsage.index; // return early if usage data is empty or if this cluster/index has already been processed @@ -39,7 +87,7 @@ export function getUsageStats(rawStats) { // Get the stats that were read from any number of different .kibana indices in the cluster, // roll them up into cluster-wide totals - const rolledUpStats = get(accum, clusterUuid, { indices: 0 }); + const rolledUpStats = accum[clusterUuid] || { indices: 0 }; const stats = { dashboard: rollUpTotals(rolledUpStats, currUsage, 'dashboard'), visualization: rollUpTotals(rolledUpStats, currUsage, 'visualization'), @@ -51,21 +99,22 @@ export function getUsageStats(rawStats) { }; // Get the stats provided by telemetry collectors. - const pluginsNested = omit(currUsage, [ - 'index', - 'dashboard', - 'visualization', - 'search', - 'index_pattern', - 'graph_workspace', - 'timelion_sheet', - ]); + const { + index, + dashboard, + visualization, + search, + index_pattern, + graph_workspace, + timelion_sheet, + xpack, + ...pluginsTop + } = currUsage; // Stats filtered by telemetry collectors need to be flattened since they're pulled in a generic way. // A plugin might not provide flat stats if it implements formatForBulkUpload in its collector. // e.g: we want `xpack.reporting` to just be `reporting` - const top = omit(pluginsNested, 'xpack'); - const plugins = { ...top, ...pluginsNested.xpack }; + const plugins = { ...pluginsTop, ...xpack }; return { ...accum, @@ -74,10 +123,13 @@ export function getUsageStats(rawStats) { plugins, }, }; - }, {}); + }, {} as ClustersUsageStats); } -export function combineStats(highLevelStats, usageStats = {}) { +export function combineStats( + highLevelStats: ClustersHighLevelStats, + usageStats: ClustersUsageStats = {} +) { return Object.keys(highLevelStats).reduce((accum, currClusterUuid) => { return { ...accum, @@ -86,7 +138,7 @@ export function combineStats(highLevelStats, usageStats = {}) { ...usageStats[currClusterUuid], }, }; - }, {}); + }, {} as KibanaStats); } /** @@ -96,7 +148,10 @@ export function combineStats(highLevelStats, usageStats = {}) { * @param {date} [start] The start time from which to get the telemetry data * @param {date} [end] The end time from which to get the telemetry data */ -export function ensureTimeSpan(start, end) { +export function ensureTimeSpan( + start?: StatsCollectionConfig['start'], + end?: StatsCollectionConfig['end'] +) { // We only care if we have a start date, because that's the limit that might make us lose the document if (start) { const duration = moment.duration(TELEMETRY_COLLECTION_INTERVAL, 'milliseconds'); @@ -117,9 +172,15 @@ export function ensureTimeSpan(start, end) { * Monkey-patch the modules from get_high_level_stats and add in the * specialized usage data that comes with kibana stats (kibana_stats.usage). */ -export async function getKibanaStats(server, callCluster, clusterUuids, start, end) { +export async function getKibanaStats( + server: StatsCollectionConfig['server'], + callCluster: StatsCollectionConfig['callCluster'], + clusterUuids: string[], + start: StatsCollectionConfig['start'], + end: StatsCollectionConfig['end'] +) { const { start: safeStart, end: safeEnd } = ensureTimeSpan(start, end); - const rawStats = await fetchHighLevelStats( + const rawStats = await fetchHighLevelStats( server, callCluster, clusterUuids, diff --git a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts index 49a925d1dad0b..f0fda5229cb5c 100644 --- a/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts +++ b/x-pack/legacy/plugins/monitoring/server/telemetry_collection/register_monitoring_collection.ts @@ -5,7 +5,6 @@ */ import { telemetryCollectionManager } from '../../../../../../src/legacy/core_plugins/telemetry/server'; -// @ts-ignore import { getAllStats } from './get_all_stats'; import { getClusterUuids } from './get_cluster_uuids'; From f49581ce348743731121a6bed0d26fd14ca76580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Mon, 17 Feb 2020 12:05:46 +0000 Subject: [PATCH 29/65] [APM] Divide "Actions menu" into sections to improve readability (#56623) * transaction actions menu * transaction actions menu * fixing pr comments * fixing pr comments * fixing pr comments * fixing pr comments * fixing unit test * fixing unit test * using moment to calculate the timestamp * renaming labels * Changing section subtitle * fixing unit tests * replacing div for react fragment * refactoring * removing marginbottom property * div is needed to remove the margin from the correct element --- .../Links/DiscoverLinks/DiscoverLink.tsx | 29 +- .../components/shared/Links/InfraLink.tsx | 22 +- .../TransactionActionMenu.tsx | 277 ++++------------ .../__test__/TransactionActionMenu.test.tsx | 32 +- .../__test__/sections.test.ts | 204 ++++++++++++ .../shared/TransactionActionMenu/sections.ts | 299 ++++++++++++++++++ .../apm/public/context/ApmPluginContext.tsx | 2 + .../public/components/action_menu.tsx | 8 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 10 files changed, 631 insertions(+), 244 deletions(-) create mode 100644 x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts create mode 100644 x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx index 51d8b43dac0ea..b58a450d26644 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/DiscoverLinks/DiscoverLink.tsx @@ -5,6 +5,7 @@ */ import { EuiLink } from '@elastic/eui'; +import { Location } from 'history'; import React from 'react'; import url from 'url'; import rison, { RisonValue } from 'rison-node'; @@ -12,6 +13,7 @@ import { useLocation } from '../../../../hooks/useLocation'; import { getTimepickerRisonData } from '../rison_helpers'; import { APM_STATIC_INDEX_PATTERN_ID } from '../../../../../common/index_pattern_constants'; import { useApmPluginContext } from '../../../../hooks/useApmPluginContext'; +import { AppMountContextBasePath } from '../../../../context/ApmPluginContext'; interface Props { query: { @@ -30,10 +32,15 @@ interface Props { children: React.ReactNode; } -export function DiscoverLink({ query = {}, ...rest }: Props) { - const { core } = useApmPluginContext(); - const location = useLocation(); - +export const getDiscoverHref = ({ + basePath, + location, + query +}: { + basePath: AppMountContextBasePath; + location: Location; + query: Props['query']; +}) => { const risonQuery = { _g: getTimepickerRisonData(location.search), _a: { @@ -43,11 +50,23 @@ export function DiscoverLink({ query = {}, ...rest }: Props) { }; const href = url.format({ - pathname: core.http.basePath.prepend('/app/kibana'), + pathname: basePath.prepend('/app/kibana'), hash: `/discover?_g=${rison.encode(risonQuery._g)}&_a=${rison.encode( risonQuery._a as RisonValue )}` }); + return href; +}; + +export function DiscoverLink({ query = {}, ...rest }: Props) { + const { core } = useApmPluginContext(); + const location = useLocation(); + + const href = getDiscoverHref({ + basePath: core.http.basePath, + query, + location + }); return ; } diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx index 8ff5e3010d6cc..e4f3557a2ce51 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Links/InfraLink.tsx @@ -10,11 +10,13 @@ import React from 'react'; import url from 'url'; import { fromQuery } from './url_helpers'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; +import { AppMountContextBasePath } from '../../../context/ApmPluginContext'; interface InfraQueryParams { time?: number; from?: number; to?: number; + filter?: string; } interface Props extends EuiLinkAnchorProps { @@ -23,12 +25,24 @@ interface Props extends EuiLinkAnchorProps { children?: React.ReactNode; } -export function InfraLink({ path, query = {}, ...rest }: Props) { - const { core } = useApmPluginContext(); +export const getInfraHref = ({ + basePath, + query, + path +}: { + basePath: AppMountContextBasePath; + query: InfraQueryParams; + path?: string; +}) => { const nextSearch = fromQuery(query); - const href = url.format({ - pathname: core.http.basePath.prepend('/app/infra'), + return url.format({ + pathname: basePath.prepend('/app/infra'), hash: compact([path, nextSearch]).join('?') }); +}; + +export function InfraLink({ path, query = {}, ...rest }: Props) { + const { core } = useApmPluginContext(); + const href = getInfraHref({ basePath: core.http.basePath, query, path }); return ; } diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx index 040d29aaa56dd..99f0b0d4fc223 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx @@ -4,240 +4,85 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - EuiButtonEmpty, - EuiContextMenuItem, - EuiContextMenuPanel, - EuiFlexGroup, - EuiFlexItem, - EuiIcon, - EuiPopover, - EuiLink -} from '@elastic/eui'; -import url from 'url'; +import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import React, { useState, FunctionComponent } from 'react'; -import { pick } from 'lodash'; +import React, { FunctionComponent, useState } from 'react'; +import { + ActionMenu, + ActionMenuDivider, + Section, + SectionLink, + SectionLinks, + SectionSubtitle, + SectionTitle +} from '../../../../../../../plugins/observability/public'; import { Transaction } from '../../../../typings/es_schemas/ui/Transaction'; -import { DiscoverTransactionLink } from '../Links/DiscoverLinks/DiscoverTransactionLink'; -import { InfraLink } from '../Links/InfraLink'; -import { useUrlParams } from '../../../hooks/useUrlParams'; -import { fromQuery } from '../Links/url_helpers'; import { useApmPluginContext } from '../../../hooks/useApmPluginContext'; - -function getInfraMetricsQuery(transaction: Transaction) { - const plus5 = new Date(transaction['@timestamp']); - const minus5 = new Date(plus5.getTime()); - - plus5.setMinutes(plus5.getMinutes() + 5); - minus5.setMinutes(minus5.getMinutes() - 5); - - return { - from: minus5.getTime(), - to: plus5.getTime() - }; -} - -function ActionMenuButton({ onClick }: { onClick: () => void }) { - return ( - - {i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', { - defaultMessage: 'Actions' - })} - - ); -} +import { useLocation } from '../../../hooks/useLocation'; +import { useUrlParams } from '../../../hooks/useUrlParams'; +import { getSections } from './sections'; interface Props { readonly transaction: Transaction; } -interface InfraConfigItem { - icon: string; - label: string; - condition?: boolean; - path: string; - query: Record; -} - -export const TransactionActionMenu: FunctionComponent = ( - props: Props -) => { - const { transaction } = props; - +const ActionMenuButton = ({ onClick }: { onClick: () => void }) => ( + + {i18n.translate('xpack.apm.transactionActionMenu.actionsButtonLabel', { + defaultMessage: 'Actions' + })} + +); + +export const TransactionActionMenu: FunctionComponent = ({ + transaction +}: Props) => { const { core } = useApmPluginContext(); - - const [isOpen, setIsOpen] = useState(false); - + const location = useLocation(); const { urlParams } = useUrlParams(); - const hostName = transaction.host?.hostname; - const podId = transaction.kubernetes?.pod.uid; - const containerId = transaction.container?.id; - - const time = Math.round(transaction.timestamp.us / 1000); - const infraMetricsQuery = getInfraMetricsQuery(transaction); - - const infraConfigItems: InfraConfigItem[] = [ - { - icon: 'logsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showPodLogsLinkLabel', - { defaultMessage: 'Show pod logs' } - ), - condition: !!podId, - path: `/link-to/pod-logs/${podId}`, - query: { time } - }, - { - icon: 'logsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showContainerLogsLinkLabel', - { defaultMessage: 'Show container logs' } - ), - condition: !!containerId, - path: `/link-to/container-logs/${containerId}`, - query: { time } - }, - { - icon: 'logsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showHostLogsLinkLabel', - { defaultMessage: 'Show host logs' } - ), - condition: !!hostName, - path: `/link-to/host-logs/${hostName}`, - query: { time } - }, - { - icon: 'logsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showTraceLogsLinkLabel', - { defaultMessage: 'Show trace logs' } - ), - condition: true, - path: `/link-to/logs`, - query: { - time, - filter: `trace.id:"${transaction.trace.id}" OR ${transaction.trace.id}` - } - }, - { - icon: 'metricsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showPodMetricsLinkLabel', - { defaultMessage: 'Show pod metrics' } - ), - condition: !!podId, - path: `/link-to/pod-detail/${podId}`, - query: infraMetricsQuery - }, - { - icon: 'metricsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel', - { defaultMessage: 'Show container metrics' } - ), - condition: !!containerId, - path: `/link-to/container-detail/${containerId}`, - query: infraMetricsQuery - }, - { - icon: 'metricsApp', - label: i18n.translate( - 'xpack.apm.transactionActionMenu.showHostMetricsLinkLabel', - { defaultMessage: 'Show host metrics' } - ), - condition: !!hostName, - path: `/link-to/host-detail/${hostName}`, - query: infraMetricsQuery - } - ]; - - const infraItems = infraConfigItems.map( - ({ icon, label, condition, path, query }, index) => ({ - icon, - key: `infra-link-${index}`, - child: ( - - {label} - - ), - condition - }) - ); + const [isOpen, setIsOpen] = useState(false); - const uptimeLink = url.format({ - pathname: core.http.basePath.prepend('/app/uptime'), - hash: `/?${fromQuery( - pick( - { - dateRangeStart: urlParams.rangeFrom, - dateRangeEnd: urlParams.rangeTo, - search: `url.domain:"${transaction.url?.domain}"` - }, - (val: string) => !!val - ) - )}` + const sections = getSections({ + transaction, + basePath: core.http.basePath, + location, + urlParams }); - const menuItems = [ - ...infraItems, - { - icon: 'discoverApp', - key: 'discover-transaction', - condition: true, - child: ( - - {i18n.translate( - 'xpack.apm.transactionActionMenu.viewSampleDocumentLinkLabel', - { - defaultMessage: 'View sample document' - } - )} - - ) - }, - { - icon: 'uptimeApp', - key: 'uptime', - child: ( - - {i18n.translate('xpack.apm.transactionActionMenu.viewInUptime', { - defaultMessage: 'View monitor status' - })} - - ), - condition: transaction.url?.domain - } - ] - .filter(({ condition }) => condition) - .map(({ icon, key, child }) => ( - - - {child} - - - - - - )); - return ( - setIsOpen(!isOpen)} />} - isOpen={isOpen} closePopover={() => setIsOpen(false)} + isOpen={isOpen} anchorPosition="downRight" - panelPaddingSize="none" + button={ setIsOpen(!isOpen)} />} > - - + {sections.map((section, idx) => { + const isLastSection = idx !== sections.length - 1; + return ( +

+ {section.map(item => ( +
+ {item.title && {item.title}} + {item.subtitle && ( + {item.subtitle} + )} + + {item.actions.map(action => ( + + ))} + +
+ ))} + {isLastSection && } +
+ ); + })} + ); }; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx index 2bfa5cf1274fa..e9f89034f58ee 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx @@ -36,7 +36,7 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithMinimalData ); - expect(queryByText('Show trace logs')).not.toBeNull(); + expect(queryByText('Trace logs')).not.toBeNull(); }); it('should not render the pod links when there is no pod id', async () => { @@ -44,8 +44,8 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithMinimalData ); - expect(queryByText('Show pod logs')).toBeNull(); - expect(queryByText('Show pod metrics')).toBeNull(); + expect(queryByText('Pod logs')).toBeNull(); + expect(queryByText('Pod metrics')).toBeNull(); }); it('should render the pod links when there is a pod id', async () => { @@ -53,8 +53,8 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithKubernetesData ); - expect(queryByText('Show pod logs')).not.toBeNull(); - expect(queryByText('Show pod metrics')).not.toBeNull(); + expect(queryByText('Pod logs')).not.toBeNull(); + expect(queryByText('Pod metrics')).not.toBeNull(); }); it('should not render the container links when there is no container id', async () => { @@ -62,8 +62,8 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithMinimalData ); - expect(queryByText('Show container logs')).toBeNull(); - expect(queryByText('Show container metrics')).toBeNull(); + expect(queryByText('Container logs')).toBeNull(); + expect(queryByText('Container metrics')).toBeNull(); }); it('should render the container links when there is a container id', async () => { @@ -71,8 +71,8 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithContainerData ); - expect(queryByText('Show container logs')).not.toBeNull(); - expect(queryByText('Show container metrics')).not.toBeNull(); + expect(queryByText('Container logs')).not.toBeNull(); + expect(queryByText('Container metrics')).not.toBeNull(); }); it('should not render the host links when there is no hostname', async () => { @@ -80,8 +80,8 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithMinimalData ); - expect(queryByText('Show host logs')).toBeNull(); - expect(queryByText('Show host metrics')).toBeNull(); + expect(queryByText('Host logs')).toBeNull(); + expect(queryByText('Host metrics')).toBeNull(); }); it('should render the host links when there is a hostname', async () => { @@ -89,8 +89,8 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithHostData ); - expect(queryByText('Show host logs')).not.toBeNull(); - expect(queryByText('Show host metrics')).not.toBeNull(); + expect(queryByText('Host logs')).not.toBeNull(); + expect(queryByText('Host metrics')).not.toBeNull(); }); it('should not render the uptime link if there is no url available', async () => { @@ -98,7 +98,7 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithMinimalData ); - expect(queryByText('View monitor status')).toBeNull(); + expect(queryByText('Status')).toBeNull(); }); it('should not render the uptime link if there is no domain available', async () => { @@ -106,7 +106,7 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithUrlWithoutDomain ); - expect(queryByText('View monitor status')).toBeNull(); + expect(queryByText('Status')).toBeNull(); }); it('should render the uptime link if there is a url with a domain', async () => { @@ -114,7 +114,7 @@ describe('TransactionActionMenu component', () => { Transactions.transactionWithUrlAndDomain ); - expect(queryByText('View monitor status')).not.toBeNull(); + expect(queryByText('Status')).not.toBeNull(); }); it('should match the snapshot', async () => { diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts new file mode 100644 index 0000000000000..52c2d27eabb82 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/sections.test.ts @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { Location } from 'history'; +import { getSections } from '../sections'; +import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; +import { AppMountContextBasePath } from '../../../../context/ApmPluginContext'; + +describe('Transaction action menu', () => { + const basePath = ({ + prepend: jest.fn() + } as unknown) as AppMountContextBasePath; + const date = '2020-02-06T11:00:00.000Z'; + const timestamp = { us: new Date(date).getTime() }; + + it('shows required sections only', () => { + const transaction = ({ + timestamp, + trace: { id: '123' }, + transaction: { id: '123' }, + '@timestamp': date + } as unknown) as Transaction; + expect( + getSections({ + transaction, + basePath, + location: ({} as unknown) as Location, + urlParams: {} + }) + ).toEqual([ + [ + { + key: 'traceDetails', + title: 'Trace details', + subtitle: 'View trace logs to get further details.', + actions: [ + { + key: 'traceLogs', + label: 'Trace logs', + href: + '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123', + condition: true + } + ] + } + ], + [ + { + key: 'kibana', + actions: [ + { + key: 'sampleDocument', + label: 'View sample document', + href: + '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))', + condition: true + } + ] + } + ] + ]); + }); + + it('shows pod and required sections only', () => { + const transaction = ({ + kubernetes: { pod: { uid: '123' } }, + timestamp, + trace: { id: '123' }, + transaction: { id: '123' }, + '@timestamp': date + } as unknown) as Transaction; + expect( + getSections({ + transaction, + basePath, + location: ({} as unknown) as Location, + urlParams: {} + }) + ).toEqual([ + [ + { + key: 'podDetails', + title: 'Pod details', + subtitle: + 'View logs and metrics for this pod to get further details.', + actions: [ + { + key: 'podLogs', + label: 'Pod logs', + href: '#/link-to/pod-logs/123?time=1580986800', + condition: true + }, + { + key: 'podMetrics', + label: 'Pod metrics', + href: + '#/link-to/pod-detail/123?from=1580986500000&to=1580987100000', + condition: true + } + ] + }, + { + key: 'traceDetails', + title: 'Trace details', + subtitle: 'View trace logs to get further details.', + actions: [ + { + key: 'traceLogs', + label: 'Trace logs', + href: + '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123', + condition: true + } + ] + } + ], + [ + { + key: 'kibana', + actions: [ + { + key: 'sampleDocument', + label: 'View sample document', + href: + '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))', + condition: true + } + ] + } + ] + ]); + }); + + it('shows host and required sections only', () => { + const transaction = ({ + host: { hostname: 'foo' }, + timestamp, + trace: { id: '123' }, + transaction: { id: '123' }, + '@timestamp': date + } as unknown) as Transaction; + expect( + getSections({ + transaction, + basePath, + location: ({} as unknown) as Location, + urlParams: {} + }) + ).toEqual([ + [ + { + key: 'hostDetails', + title: 'Host details', + subtitle: 'View host logs and metrics to get further details.', + actions: [ + { + key: 'hostLogs', + label: 'Host logs', + href: '#/link-to/host-logs/foo?time=1580986800', + condition: true + }, + { + key: 'hostMetrics', + label: 'Host metrics', + href: + '#/link-to/host-detail/foo?from=1580986500000&to=1580987100000', + condition: true + } + ] + }, + { + key: 'traceDetails', + title: 'Trace details', + subtitle: 'View trace logs to get further details.', + actions: [ + { + key: 'traceLogs', + label: 'Trace logs', + href: + '#/link-to/logs?time=1580986800&filter=trace.id:%22123%22%20OR%20123', + condition: true + } + ] + } + ], + [ + { + key: 'kibana', + actions: [ + { + key: 'sampleDocument', + label: 'View sample document', + href: + '#/discover?_g=(refreshInterval:(pause:true,value:\'0\'),time:(from:now-24h,to:now))&_a=(index:apm_static_index_pattern_id,interval:auto,query:(language:kuery,query:\'processor.event:"transaction" AND transaction.id:"123" AND trace.id:"123"\'))', + condition: true + } + ] + } + ] + ]); + }); +}); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts new file mode 100644 index 0000000000000..77445a2600960 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/sections.ts @@ -0,0 +1,299 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import { Location } from 'history'; +import { pick, isEmpty } from 'lodash'; +import moment from 'moment'; +import url from 'url'; +import { Transaction } from '../../../../typings/es_schemas/ui/Transaction'; +import { IUrlParams } from '../../../context/UrlParamsContext/types'; +import { getDiscoverHref } from '../Links/DiscoverLinks/DiscoverLink'; +import { getDiscoverQuery } from '../Links/DiscoverLinks/DiscoverTransactionLink'; +import { getInfraHref } from '../Links/InfraLink'; +import { fromQuery } from '../Links/url_helpers'; +import { AppMountContextBasePath } from '../../../context/ApmPluginContext'; + +function getInfraMetricsQuery(transaction: Transaction) { + const timestamp = new Date(transaction['@timestamp']).getTime(); + const fiveMinutes = moment.duration(5, 'minutes').asMilliseconds(); + + return { + from: timestamp - fiveMinutes, + to: timestamp + fiveMinutes + }; +} + +interface Action { + key: string; + label: string; + href: string; + condition: boolean; +} + +interface Section { + key: string; + title?: string; + subtitle?: string; + actions: Action[]; +} + +type SectionRecord = Record; + +export const getSections = ({ + transaction, + basePath, + location, + urlParams +}: { + transaction: Transaction; + basePath: AppMountContextBasePath; + location: Location; + urlParams: IUrlParams; +}) => { + const hostName = transaction.host?.hostname; + const podId = transaction.kubernetes?.pod.uid; + const containerId = transaction.container?.id; + + const time = Math.round(transaction.timestamp.us / 1000); + const infraMetricsQuery = getInfraMetricsQuery(transaction); + + const uptimeLink = url.format({ + pathname: basePath.prepend('/app/uptime'), + hash: `/?${fromQuery( + pick( + { + dateRangeStart: urlParams.rangeFrom, + dateRangeEnd: urlParams.rangeTo, + search: `url.domain:"${transaction.url?.domain}"` + }, + (val: string) => !isEmpty(val) + ) + )}` + }); + + const podActions: Action[] = [ + { + key: 'podLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showPodLogsLinkLabel', + { defaultMessage: 'Pod logs' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/pod-logs/${podId}`, + query: { time } + }), + condition: !!podId + }, + { + key: 'podMetrics', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showPodMetricsLinkLabel', + { defaultMessage: 'Pod metrics' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/pod-detail/${podId}`, + query: infraMetricsQuery + }), + condition: !!podId + } + ]; + + const containerActions: Action[] = [ + { + key: 'containerLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showContainerLogsLinkLabel', + { defaultMessage: 'Container logs' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/container-logs/${containerId}`, + query: { time } + }), + condition: !!containerId + }, + { + key: 'containerMetrics', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel', + { defaultMessage: 'Container metrics' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/container-detail/${containerId}`, + query: infraMetricsQuery + }), + condition: !!containerId + } + ]; + + const hostActions: Action[] = [ + { + key: 'hostLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showHostLogsLinkLabel', + { defaultMessage: 'Host logs' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/host-logs/${hostName}`, + query: { time } + }), + condition: !!hostName + }, + { + key: 'hostMetrics', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showHostMetricsLinkLabel', + { defaultMessage: 'Host metrics' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/host-detail/${hostName}`, + query: infraMetricsQuery + }), + condition: !!hostName + } + ]; + + const logActions: Action[] = [ + { + key: 'traceLogs', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.showTraceLogsLinkLabel', + { defaultMessage: 'Trace logs' } + ), + href: getInfraHref({ + basePath, + path: `/link-to/logs`, + query: { + time, + filter: `trace.id:"${transaction.trace.id}" OR ${transaction.trace.id}` + } + }), + condition: true + } + ]; + + const uptimeActions: Action[] = [ + { + key: 'monitorStatus', + label: i18n.translate('xpack.apm.transactionActionMenu.viewInUptime', { + defaultMessage: 'Status' + }), + href: uptimeLink, + condition: !!transaction.url?.domain + } + ]; + + const kibanaActions: Action[] = [ + { + key: 'sampleDocument', + label: i18n.translate( + 'xpack.apm.transactionActionMenu.viewSampleDocumentLinkLabel', + { + defaultMessage: 'View sample document' + } + ), + href: getDiscoverHref({ + basePath, + query: getDiscoverQuery(transaction), + location + }), + condition: true + } + ]; + + const sectionRecord: SectionRecord = { + observability: [ + { + key: 'podDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.pod.title', { + defaultMessage: 'Pod details' + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.pod.subtitle', + { + defaultMessage: + 'View logs and metrics for this pod to get further details.' + } + ), + actions: podActions + }, + { + key: 'containerDetails', + title: i18n.translate( + 'xpack.apm.transactionActionMenu.container.title', + { + defaultMessage: 'Container details' + } + ), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.container.subtitle', + { + defaultMessage: + 'View logs and metrics for this container to get further details.' + } + ), + actions: containerActions + }, + { + key: 'hostDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.host.title', { + defaultMessage: 'Host details' + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.host.subtitle', + { + defaultMessage: 'View host logs and metrics to get further details.' + } + ), + actions: hostActions + }, + { + key: 'traceDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.trace.title', { + defaultMessage: 'Trace details' + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.trace.subtitle', + { + defaultMessage: 'View trace logs to get further details.' + } + ), + actions: logActions + }, + { + key: 'statusDetails', + title: i18n.translate('xpack.apm.transactionActionMenu.status.title', { + defaultMessage: 'Status details' + }), + subtitle: i18n.translate( + 'xpack.apm.transactionActionMenu.status.subtitle', + { + defaultMessage: 'View status to get further details.' + } + ), + actions: uptimeActions + } + ], + kibana: [{ key: 'kibana', actions: kibanaActions }] + }; + + // Filter out actions that shouldnt be shown and sections without any actions. + return Object.values(sectionRecord) + .map(sections => + sections + .map(section => ({ + ...section, + actions: section.actions.filter(action => action.condition) + })) + .filter(section => !isEmpty(section.actions)) + ) + .filter(sections => !isEmpty(sections)); +}; diff --git a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx index 86efd9b31974e..7a9aaa6dfb920 100644 --- a/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx +++ b/x-pack/legacy/plugins/apm/public/context/ApmPluginContext.tsx @@ -8,6 +8,8 @@ import { createContext } from 'react'; import { AppMountContext, PackageInfo } from 'kibana/public'; import { ApmPluginSetupDeps, ConfigSchema } from '../new-platform/plugin'; +export type AppMountContextBasePath = AppMountContext['core']['http']['basePath']; + export interface ApmPluginContextValue { config: ConfigSchema; core: AppMountContext['core']; diff --git a/x-pack/plugins/observability/public/components/action_menu.tsx b/x-pack/plugins/observability/public/components/action_menu.tsx index 6e964dde3aecf..a5f59ecd5506f 100644 --- a/x-pack/plugins/observability/public/components/action_menu.tsx +++ b/x-pack/plugins/observability/public/components/action_menu.tsx @@ -16,6 +16,7 @@ import { import React, { HTMLAttributes } from 'react'; import { EuiListGroupItemProps } from '@elastic/eui/src/components/list_group/list_group_item'; +import styled from 'styled-components'; type Props = EuiPopoverProps & HTMLAttributes; @@ -45,7 +46,12 @@ export const SectionLinks: React.FC<{}> = props => ( export const SectionSpacer: React.FC<{}> = () => ; -export const Section: React.FC<{}> = props => <>{props.children}; +export const Section = styled.div` + margin-bottom: 24px; + &:last-of-type { + margin-bottom: 0; + } +`; export type SectionLinkProps = EuiListGroupItemProps; export const SectionLink: React.FC = props => ( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ee2abeff74496..a59a6dbf12566 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3945,7 +3945,6 @@ "xpack.apm.tracesTable.tracesPerMinuteColumnLabel": "1 分あたりのトレース", "xpack.apm.tracesTable.tracesPerMinuteUnitLabel": "1分あたりトランザクション数", "xpack.apm.transactionActionMenu.actionsButtonLabel": "アクション", - "xpack.apm.transactionActionMenu.actionsLabel": "アクション", "xpack.apm.transactionActionMenu.showContainerLogsLinkLabel": "コンテナーログを表示", "xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel": "コンテナーメトリックを表示", "xpack.apm.transactionActionMenu.showHostLogsLinkLabel": "ホストログを表示", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3210c619d3e52..dbc9cab4261d8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3945,7 +3945,6 @@ "xpack.apm.tracesTable.tracesPerMinuteColumnLabel": "每分钟追溯次数", "xpack.apm.tracesTable.tracesPerMinuteUnitLabel": "tpm", "xpack.apm.transactionActionMenu.actionsButtonLabel": "操作", - "xpack.apm.transactionActionMenu.actionsLabel": "操作", "xpack.apm.transactionActionMenu.showContainerLogsLinkLabel": "显示容器日志", "xpack.apm.transactionActionMenu.showContainerMetricsLinkLabel": "显示容器指标", "xpack.apm.transactionActionMenu.showHostLogsLinkLabel": "显示主机日志", From 9388ff7b43d5743dfc8933690f1c67a7befef69d Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 17 Feb 2020 13:52:53 +0100 Subject: [PATCH 30/65] Fix auto refresh in visualizations and lens (#57667) --- .../visualize/np_ready/editor/editor.js | 8 ----- .../public/embeddable/visualize_embeddable.ts | 8 +++++ .../visualize_embeddable_factory.tsx | 7 +++- .../public/np_ready/public/mocks.ts | 1 + .../public/np_ready/public/plugin.ts | 13 +++++-- .../timefilter/timefilter_service.mock.ts | 3 +- .../embeddable/embeddable.test.tsx | 35 ++++++++++++++++++- .../embeddable/embeddable.tsx | 15 +++++++- .../embeddable/embeddable_factory.ts | 8 ++++- .../public/editor_frame_service/mocks.tsx | 9 ++--- .../public/editor_frame_service/service.tsx | 1 + 11 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js index 27fb9b63843c4..657104344662f 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js @@ -441,14 +441,6 @@ function VisualizeAppController( }) ); - subscriptions.add( - subscribeWithScope($scope, timefilter.getAutoRefreshFetch$(), { - next: () => { - $scope.vis.forceReload(); - }, - }) - ); - $scope.$on('$destroy', () => { if ($scope._handler) { $scope._handler.destroy(); diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts index 5e593398333c9..fddcf70c30605 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -34,6 +34,7 @@ import { esFilters, Filter, ISearchSource, + TimefilterContract, } from '../../../../../plugins/data/public'; import { EmbeddableInput, @@ -106,8 +107,10 @@ export class VisualizeEmbeddable extends Embeddable { this.handleChanges(); @@ -345,6 +352,7 @@ export class VisualizeEmbeddable extends Embeddable { diff --git a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx index 03471174753fa..2f00467a85cda 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx +++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx @@ -38,6 +38,7 @@ import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import { getCapabilities, getHttp, getTypes, getUISettings } from '../np_ready/public/services'; import { showNewVisModal } from '../np_ready/public/wizard'; +import { TimefilterContract } from '../../../../../plugins/data/public'; interface VisualizationAttributes extends SavedObjectAttributes { visState: string; @@ -51,7 +52,10 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< > { public readonly type = VISUALIZE_EMBEDDABLE_TYPE; - constructor(private getSavedVisualizationsLoader: () => SavedVisualizations) { + constructor( + private timefilter: TimefilterContract, + private getSavedVisualizationsLoader: () => SavedVisualizations + ) { super({ savedObjectMetaData: { name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }), @@ -114,6 +118,7 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory< const indexPattern = await getIndexPattern(savedObject); const indexPatterns = indexPattern ? [indexPattern] : []; return new VisualizeEmbeddable( + this.timefilter, { savedVisualization: savedObject, indexPatterns, diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts index a948757d7bd83..9fb87cadb2983 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts @@ -56,6 +56,7 @@ const createInstance = async () => { const plugin = new VisualizationsPlugin({} as PluginInitializerContext); const setup = plugin.setup(coreMock.createSetup(), { + data: dataPluginMock.createSetupContract(), expressions: expressionsPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createStartContract(), usageCollection: usageCollectionPluginMock.createSetupContract(), diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts index 36c04923e3fd0..20bed59faad88 100644 --- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts @@ -36,7 +36,10 @@ import { ExpressionsSetup } from '../../../../../../plugins/expressions/public'; import { IEmbeddableSetup } from '../../../../../../plugins/embeddable/public'; import { visualization as visualizationFunction } from './expressions/visualization_function'; import { visualization as visualizationRenderer } from './expressions/visualization_renderer'; -import { DataPublicPluginStart } from '../../../../../../plugins/data/public'; +import { + DataPublicPluginSetup, + DataPublicPluginStart, +} from '../../../../../../plugins/data/public'; import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public'; import { createSavedVisLoader, @@ -65,6 +68,7 @@ export interface VisualizationsSetupDeps { expressions: ExpressionsSetup; embeddable: IEmbeddableSetup; usageCollection: UsageCollectionSetup; + data: DataPublicPluginSetup; } export interface VisualizationsStartDeps { @@ -95,7 +99,7 @@ export class VisualizationsPlugin public setup( core: CoreSetup, - { expressions, embeddable, usageCollection }: VisualizationsSetupDeps + { expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps ): VisualizationsSetup { setUISettings(core.uiSettings); setUsageCollector(usageCollection); @@ -103,7 +107,10 @@ export class VisualizationsPlugin expressions.registerFunction(visualizationFunction); expressions.registerRenderer(visualizationRenderer); - const embeddableFactory = new VisualizeEmbeddableFactory(this.getSavedVisualizationsLoader); + const embeddableFactory = new VisualizeEmbeddableFactory( + data.query.timefilter.timefilter, + this.getSavedVisualizationsLoader + ); embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory); return { diff --git a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts index 2923cee60f898..80c13464ad98a 100644 --- a/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts +++ b/src/plugins/data/public/query/timefilter/timefilter_service.mock.ts @@ -18,6 +18,7 @@ */ import { TimefilterService, TimeHistoryContract, TimefilterContract } from '.'; +import { Observable } from 'rxjs'; export type TimefilterServiceClientContract = PublicMethodsOf; @@ -28,7 +29,7 @@ const createSetupContractMock = () => { getEnabledUpdated$: jest.fn(), getTimeUpdate$: jest.fn(), getRefreshIntervalUpdate$: jest.fn(), - getAutoRefreshFetch$: jest.fn(), + getAutoRefreshFetch$: jest.fn(() => new Observable()), getFetch$: jest.fn(), getTime: jest.fn(), setTime: jest.fn(), diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index a07bd475cdfcb..55363ebe4d8f3 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -4,10 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Subject } from 'rxjs'; import { Embeddable } from './embeddable'; import { ReactExpressionRendererProps } from 'src/plugins/expressions/public'; -import { Query, TimeRange, Filter } from 'src/plugins/data/public'; +import { Query, TimeRange, Filter, TimefilterContract } from 'src/plugins/data/public'; import { Document } from '../../persistence'; +import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks'; jest.mock('../../../../../../../src/plugins/inspector/public/', () => ({ isAvailable: false, @@ -44,6 +46,7 @@ describe('embeddable', () => { it('should render expression with expression renderer', () => { const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -64,6 +67,7 @@ describe('embeddable', () => { const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -89,6 +93,7 @@ describe('embeddable', () => { const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: false } }]; const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -112,6 +117,7 @@ describe('embeddable', () => { const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; const embeddable = new Embeddable( + dataPluginMock.createSetupContract().query.timefilter.timefilter, expressionRenderer, { editUrl: '', @@ -130,4 +136,31 @@ describe('embeddable', () => { expect(expressionRenderer).toHaveBeenCalledTimes(1); }); + + it('should re-render on auto refresh fetch observable', () => { + const timeRange: TimeRange = { from: 'now-15d', to: 'now' }; + const query: Query = { language: 'kquery', query: '' }; + const filters: Filter[] = [{ meta: { alias: 'test', negate: false, disabled: true } }]; + + const autoRefreshFetchSubject = new Subject(); + const timefilter = ({ + getAutoRefreshFetch$: () => autoRefreshFetchSubject.asObservable(), + } as unknown) as TimefilterContract; + + const embeddable = new Embeddable( + timefilter, + expressionRenderer, + { + editUrl: '', + editable: true, + savedVis, + }, + { id: '123', timeRange, query, filters } + ); + embeddable.render(mountpoint); + + autoRefreshFetchSubject.next(); + + expect(expressionRenderer).toHaveBeenCalledTimes(2); + }); }); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index a3a55f26ff7c2..252ba5c9bc0bc 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -7,7 +7,13 @@ import _ from 'lodash'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { Query, TimeRange, Filter, IIndexPattern } from 'src/plugins/data/public'; +import { + Query, + TimeRange, + Filter, + IIndexPattern, + TimefilterContract, +} from 'src/plugins/data/public'; import { Subscription } from 'rxjs'; import { ReactExpressionRendererType } from '../../../../../../../src/plugins/expressions/public'; import { @@ -43,6 +49,7 @@ export class Embeddable extends AbstractEmbeddable this.onContainerStateChanged(input)); this.onContainerStateChanged(initialInput); + + this.autoRefreshFetchSubscription = timefilter + .getAutoRefreshFetch$() + .subscribe(this.reload.bind(this)); } onContainerStateChanged(containerState: LensEmbeddableInput) { @@ -125,6 +137,7 @@ export class Embeddable extends AbstractEmbeddable, private savedObjectsClient: SavedObjectsClientContract, @@ -85,6 +90,7 @@ export class EmbeddableFactory extends AbstractEmbeddableFactory { ); return new Embeddable( + this.timefilter, this.expressionRenderer, { savedVis, diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx index cd121a1f96a2b..e606c69c8c386 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/mocks.tsx @@ -14,6 +14,7 @@ import { embeddablePluginMock } from '../../../../../../src/plugins/embeddable/p import { expressionsPluginMock } from '../../../../../../src/plugins/expressions/public/mocks'; import { DatasourcePublicAPI, FramePublicAPI, Datasource, Visualization } from '../types'; import { EditorFrameSetupPlugins, EditorFrameStartPlugins } from './service'; +import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; export function createMockVisualization(): jest.Mocked { return { @@ -103,7 +104,7 @@ export function createExpressionRendererMock(): jest.Mock< export function createMockSetupDependencies() { return ({ - data: {}, + data: dataPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createSetupContract(), expressions: expressionsPluginMock.createSetupContract(), } as unknown) as MockedSetupDependencies; @@ -111,11 +112,7 @@ export function createMockSetupDependencies() { export function createMockStartDependencies() { return ({ - data: { - indexPatterns: { - indexPatterns: {}, - }, - }, + data: dataPluginMock.createSetupContract(), embeddable: embeddablePluginMock.createStartContract(), expressions: expressionsPluginMock.createStartContract(), } as unknown) as MockedStartDependencies; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx index 9a3d724705a1a..7a0bb3a2cc50f 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.tsx @@ -79,6 +79,7 @@ export class EditorFrameService { plugins.embeddable.registerEmbeddableFactory( 'lens', new EmbeddableFactory( + plugins.data.query.timefilter.timefilter, core.http, core.application.capabilities, core.savedObjects.client, From ca5e25c1395397a2599c1aec76f752d4fda8d4e8 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 17 Feb 2020 14:59:47 +0100 Subject: [PATCH 31/65] Local actions (#57451) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 create UiActionsService * feat: 🎸 add UiActionsServvice.fork() method * feat: 🎸 instantiate UiActionsService in plugin * feat: 🎸 add UiActionsService.registerTrigger(), remove old * feat: 🎸 move attach/detachAction() methods to UiActionsService * refactor: 💡 move remaining actions API to UiActionsService * chore: 🤖 clean up /trigger folder * test: 💍 move registry tests into UiActiosnService tests * fix: 🐛 fix TypeScript typecheck errors * test: 💍 add .fork() trigger tests * feat: 🎸 remove actionIds from ui_actions Trigger interface * fix: 🐛 remove usage of actionIds * fix: 🐛 attach hello world action to trigger in plugin lifecycle * feat: 🎸 fork also trigger to action attachments * fix: 🐛 clear mapping registry in .clear(), improve type --- .../public/hello_world_trigger.ts | 2 - examples/ui_action_examples/public/plugin.ts | 7 +- .../ui_actions_explorer/public/plugin.tsx | 3 - src/plugins/embeddable/public/bootstrap.ts | 17 +- .../lib/panel/embeddable_panel.test.tsx | 5 +- src/plugins/embeddable/public/lib/types.ts | 1 - .../embeddable/public/tests/test_plugin.ts | 8 +- src/plugins/ui_actions/README.md | 14 +- .../incompatible_action_error.ts | 0 .../ui_actions/public/actions/index.ts | 5 +- .../public/actions/register_action.ts | 28 -- src/plugins/ui_actions/public/api.ts | 55 --- src/plugins/ui_actions/public/index.ts | 29 +- src/plugins/ui_actions/public/mocks.ts | 15 +- src/plugins/ui_actions/public/plugin.ts | 39 +- .../{tests/helpers.ts => service/index.ts} | 10 +- .../public/service/ui_actions_service.test.ts | 465 ++++++++++++++++++ .../public/service/ui_actions_service.ts | 194 ++++++++ src/plugins/ui_actions/public/tests/README.md | 2 + .../execute_trigger_actions.test.ts | 33 +- .../get_trigger_actions.test.ts | 5 +- .../get_trigger_compatible_actions.test.ts | 27 +- src/plugins/ui_actions/public/tests/index.ts | 2 +- .../ui_actions/public/tests/test_plugin.ts | 49 -- .../public/triggers/attach_action.ts | 37 -- .../public/triggers/detach_action.ts | 35 -- .../triggers/execute_trigger_actions.ts | 59 --- .../public/triggers/get_trigger.test.ts | 48 -- .../ui_actions/public/triggers/get_trigger.ts | 30 -- .../public/triggers/get_trigger_actions.ts | 29 -- .../get_trigger_compatible_actions.ts | 32 -- .../ui_actions/public/triggers/index.ts | 2 +- .../public/triggers/register_trigger.ts | 28 -- .../public/triggers/registry.test.ts | 149 ------ .../ui_actions/public/triggers/trigger.ts | 1 - src/plugins/ui_actions/public/types.ts | 35 +- 36 files changed, 765 insertions(+), 735 deletions(-) rename src/plugins/ui_actions/public/{triggers => actions}/incompatible_action_error.ts (100%) delete mode 100644 src/plugins/ui_actions/public/actions/register_action.ts delete mode 100644 src/plugins/ui_actions/public/api.ts rename src/plugins/ui_actions/public/{tests/helpers.ts => service/index.ts} (76%) create mode 100644 src/plugins/ui_actions/public/service/ui_actions_service.test.ts create mode 100644 src/plugins/ui_actions/public/service/ui_actions_service.ts create mode 100644 src/plugins/ui_actions/public/tests/README.md rename src/plugins/ui_actions/public/{triggers => tests}/execute_trigger_actions.test.ts (86%) rename src/plugins/ui_actions/public/{triggers => tests}/get_trigger_actions.test.ts (93%) rename src/plugins/ui_actions/public/{triggers => tests}/get_trigger_compatible_actions.test.ts (82%) delete mode 100644 src/plugins/ui_actions/public/tests/test_plugin.ts delete mode 100644 src/plugins/ui_actions/public/triggers/attach_action.ts delete mode 100644 src/plugins/ui_actions/public/triggers/detach_action.ts delete mode 100644 src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger.test.ts delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger.ts delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger_actions.ts delete mode 100644 src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts delete mode 100644 src/plugins/ui_actions/public/triggers/register_trigger.ts delete mode 100644 src/plugins/ui_actions/public/triggers/registry.test.ts diff --git a/examples/ui_action_examples/public/hello_world_trigger.ts b/examples/ui_action_examples/public/hello_world_trigger.ts index 999a7d9864707..929c9aecab17b 100644 --- a/examples/ui_action_examples/public/hello_world_trigger.ts +++ b/examples/ui_action_examples/public/hello_world_trigger.ts @@ -18,11 +18,9 @@ */ import { Trigger } from '../../../src/plugins/ui_actions/public'; -import { HELLO_WORLD_ACTION_TYPE } from './hello_world_action'; export const HELLO_WORLD_TRIGGER_ID = 'HELLO_WORLD_TRIGGER_ID'; export const helloWorldTrigger: Trigger = { id: HELLO_WORLD_TRIGGER_ID, - actionIds: [HELLO_WORLD_ACTION_TYPE], }; diff --git a/examples/ui_action_examples/public/plugin.ts b/examples/ui_action_examples/public/plugin.ts index ef0689227d6bd..bf62b4d973d4d 100644 --- a/examples/ui_action_examples/public/plugin.ts +++ b/examples/ui_action_examples/public/plugin.ts @@ -19,7 +19,7 @@ import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public'; import { UiActionsSetup, UiActionsStart } from '../../../src/plugins/ui_actions/public'; -import { createHelloWorldAction } from './hello_world_action'; +import { createHelloWorldAction, HELLO_WORLD_ACTION_TYPE } from './hello_world_action'; import { helloWorldTrigger } from './hello_world_trigger'; interface UiActionExamplesSetupDependencies { @@ -33,8 +33,9 @@ interface UiActionExamplesStartDependencies { export class UiActionExamplesPlugin implements Plugin { - public setup(core: CoreSetup, deps: UiActionExamplesSetupDependencies) { - deps.uiActions.registerTrigger(helloWorldTrigger); + public setup(core: CoreSetup, { uiActions }: UiActionExamplesSetupDependencies) { + uiActions.registerTrigger(helloWorldTrigger); + uiActions.attachAction(helloWorldTrigger.id, HELLO_WORLD_ACTION_TYPE); } public start(coreStart: CoreStart, deps: UiActionExamplesStartDependencies) { diff --git a/examples/ui_actions_explorer/public/plugin.tsx b/examples/ui_actions_explorer/public/plugin.tsx index 9c5f967a466bf..981ad97a31b46 100644 --- a/examples/ui_actions_explorer/public/plugin.tsx +++ b/examples/ui_actions_explorer/public/plugin.tsx @@ -56,15 +56,12 @@ export class UiActionsExplorerPlugin implements Plugin, deps: SetupDeps) { deps.uiActions.registerTrigger({ id: COUNTRY_TRIGGER, - actionIds: [], }); deps.uiActions.registerTrigger({ id: PHONE_TRIGGER, - actionIds: [], }); deps.uiActions.registerTrigger({ id: USER_TRIGGER, - actionIds: [], }); deps.uiActions.registerAction(lookUpWeatherAction); deps.uiActions.registerAction(viewInMapsAction); diff --git a/src/plugins/embeddable/public/bootstrap.ts b/src/plugins/embeddable/public/bootstrap.ts index 3ca84549c559d..9a364e84092ca 100644 --- a/src/plugins/embeddable/public/bootstrap.ts +++ b/src/plugins/embeddable/public/bootstrap.ts @@ -17,7 +17,7 @@ * under the License. */ -import { UiActionsSetup } from 'src/plugins/ui_actions/public'; +import { UiActionsSetup, Trigger } from 'src/plugins/ui_actions/public'; import { CONTEXT_MENU_TRIGGER, APPLY_FILTER_TRIGGER, @@ -34,35 +34,30 @@ import { * @param api */ export const bootstrap = (uiActions: UiActionsSetup) => { - const triggerContext = { + const triggerContext: Trigger = { id: CONTEXT_MENU_TRIGGER, title: 'Context menu', description: 'Triggered on top-right corner context-menu select.', - actionIds: [], }; - const triggerFilter = { + const triggerFilter: Trigger = { id: APPLY_FILTER_TRIGGER, title: 'Filter click', description: 'Triggered when user applies filter to an embeddable.', - actionIds: [], }; - const triggerBadge = { + const triggerBadge: Trigger = { id: PANEL_BADGE_TRIGGER, title: 'Panel badges', description: 'Actions appear in title bar when an embeddable loads in a panel', - actionIds: [], }; - const selectRangeTrigger = { + const selectRangeTrigger: Trigger = { id: SELECT_RANGE_TRIGGER, title: 'Select range', description: 'Applies a range filter', - actionIds: [], }; - const valueClickTrigger = { + const valueClickTrigger: Trigger = { id: VALUE_CLICK_TRIGGER, title: 'Value clicked', description: 'Value was clicked', - actionIds: [], }; const actionApplyFilter = createFilterAction(); diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx index 9982c632f36fb..79d59317767d9 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx @@ -25,7 +25,7 @@ import { nextTick } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { I18nProvider } from '@kbn/i18n/react'; import { CONTEXT_MENU_TRIGGER } from '../triggers'; -import { Action, UiActionsApi } from 'src/plugins/ui_actions/public'; +import { Action, UiActionsStart } from 'src/plugins/ui_actions/public'; import { Trigger, GetEmbeddableFactory, ViewMode } from '../types'; import { EmbeddableFactory, isErrorEmbeddable } from '../embeddables'; import { EmbeddablePanel } from './embeddable_panel'; @@ -52,7 +52,6 @@ const getEmbeddableFactory: GetEmbeddableFactory = (id: string) => embeddableFac const editModeAction = createEditModeAction(); const trigger: Trigger = { id: CONTEXT_MENU_TRIGGER, - actionIds: [editModeAction.id], }; const embeddableFactory = new ContactCardEmbeddableFactory( {} as any, @@ -177,7 +176,7 @@ test('HelloWorldContainer in view mode hides edit mode actions', async () => { const renderInEditModeAndOpenContextMenu = async ( embeddableInputs: any, - getActions: UiActionsApi['getTriggerCompatibleActions'] = () => Promise.resolve([]) + getActions: UiActionsStart['getTriggerCompatibleActions'] = () => Promise.resolve([]) ) => { const inspector = inspectorPluginMock.createStartContract(); diff --git a/src/plugins/embeddable/public/lib/types.ts b/src/plugins/embeddable/public/lib/types.ts index 1bd71163db44c..68ea5bc17f7c9 100644 --- a/src/plugins/embeddable/public/lib/types.ts +++ b/src/plugins/embeddable/public/lib/types.ts @@ -24,7 +24,6 @@ export interface Trigger { id: string; title?: string; description?: string; - actionIds: string[]; } export interface PropertySpec { diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts index d9e1a75d92bf3..1edc332780336 100644 --- a/src/plugins/embeddable/public/tests/test_plugin.ts +++ b/src/plugins/embeddable/public/tests/test_plugin.ts @@ -19,8 +19,8 @@ import { CoreSetup, CoreStart } from 'src/core/public'; // eslint-disable-next-line -import { uiActionsTestPlugin } from 'src/plugins/ui_actions/public/tests'; -import { UiActionsApi } from 'src/plugins/ui_actions/public'; +import { uiActionsPluginMock } from 'src/plugins/ui_actions/public/mocks'; +import { UiActionsStart } from 'src/plugins/ui_actions/public'; import { coreMock } from '../../../../core/public/mocks'; import { EmbeddablePublicPlugin, IEmbeddableSetup, IEmbeddableStart } from '../plugin'; @@ -30,14 +30,14 @@ export interface TestPluginReturn { coreStart: CoreStart; setup: IEmbeddableSetup; doStart: (anotherCoreStart?: CoreStart) => IEmbeddableStart; - uiActions: UiActionsApi; + uiActions: UiActionsStart; } export const testPlugin = ( coreSetup: CoreSetup = coreMock.createSetup(), coreStart: CoreStart = coreMock.createStart() ): TestPluginReturn => { - const uiActions = uiActionsTestPlugin(coreSetup, coreStart); + const uiActions = uiActionsPluginMock.createPlugin(coreSetup, coreStart); const initializerContext = {} as any; const plugin = new EmbeddablePublicPlugin(initializerContext); const setup = plugin.setup(coreSetup, { uiActions: uiActions.setup }); diff --git a/src/plugins/ui_actions/README.md b/src/plugins/ui_actions/README.md index 02942b7d5b406..c4e02b551c884 100644 --- a/src/plugins/ui_actions/README.md +++ b/src/plugins/ui_actions/README.md @@ -1,10 +1,10 @@ # UI Actions -An API for: - - creating custom functionality (`actions`) - - creating custom user interaction events (`triggers`) - - attaching and detaching `actions` to `triggers`. - - emitting `trigger` events - - executing `actions` attached to a given `trigger`. - - exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. +An API for: +- creating custom functionality (`actions`) +- creating custom user interaction events (`triggers`) +- attaching and detaching `actions` to `triggers`. +- emitting `trigger` events +- executing `actions` attached to a given `trigger`. +- exposing a context menu for the user to choose the appropriate action when there are multiple actions attached to a single trigger. diff --git a/src/plugins/ui_actions/public/triggers/incompatible_action_error.ts b/src/plugins/ui_actions/public/actions/incompatible_action_error.ts similarity index 100% rename from src/plugins/ui_actions/public/triggers/incompatible_action_error.ts rename to src/plugins/ui_actions/public/actions/incompatible_action_error.ts diff --git a/src/plugins/ui_actions/public/actions/index.ts b/src/plugins/ui_actions/public/actions/index.ts index feb9a8de62eb3..64bfd368e3dfa 100644 --- a/src/plugins/ui_actions/public/actions/index.ts +++ b/src/plugins/ui_actions/public/actions/index.ts @@ -17,5 +17,6 @@ * under the License. */ -export { Action } from './action'; -export { createAction } from './create_action'; +export * from './action'; +export * from './create_action'; +export * from './incompatible_action_error'; diff --git a/src/plugins/ui_actions/public/actions/register_action.ts b/src/plugins/ui_actions/public/actions/register_action.ts deleted file mode 100644 index 5738be63c9592..0000000000000 --- a/src/plugins/ui_actions/public/actions/register_action.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; - -export const registerAction: UiActionsApiPure['registerAction'] = ({ actions }) => action => { - if (actions.has(action.id)) { - throw new Error(`Action [action.id = ${action.id}] already registered.`); - } - - actions.set(action.id, action); -}; diff --git a/src/plugins/ui_actions/public/api.ts b/src/plugins/ui_actions/public/api.ts deleted file mode 100644 index 9a6fd04b14e10..0000000000000 --- a/src/plugins/ui_actions/public/api.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - UiActionsApi, - UiActionsDependenciesInternal, - UiActionsDependencies, - UiActionsApiPure, -} from './types'; -import { attachAction } from './triggers/attach_action'; -import { detachAction } from './triggers/detach_action'; -import { executeTriggerActions } from './triggers/execute_trigger_actions'; -import { getTrigger } from './triggers/get_trigger'; -import { getTriggerActions } from './triggers/get_trigger_actions'; -import { getTriggerCompatibleActions } from './triggers/get_trigger_compatible_actions'; -import { registerAction } from './actions/register_action'; -import { registerTrigger } from './triggers/register_trigger'; - -export const pureApi: UiActionsApiPure = { - attachAction, - detachAction, - executeTriggerActions, - getTrigger, - getTriggerActions, - getTriggerCompatibleActions, - registerAction, - registerTrigger, -}; - -export const createApi = (deps: UiActionsDependencies) => { - const partialApi: Partial = {}; - const depsInternal: UiActionsDependenciesInternal = { ...deps, api: partialApi }; - for (const [key, fn] of Object.entries(pureApi)) { - (partialApi as any)[key] = fn(depsInternal); - } - Object.freeze(partialApi); - const api = partialApi as UiActionsApi; - return { api, depsInternal }; -}; diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 427dbecb7aee4..83a08b11fa4c2 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -19,19 +19,30 @@ import { PluginInitializerContext } from '../../../core/public'; import { UiActionsPlugin } from './plugin'; +import { UiActionsService } from './service'; export function plugin(initializerContext: PluginInitializerContext) { return new UiActionsPlugin(initializerContext); } export { UiActionsSetup, UiActionsStart } from './plugin'; -export { - Action, - Trigger, - UiActionsApi, - GetActionsCompatibleWithTrigger, - ExecuteTriggerActions, -} from './types'; -export { createAction } from './actions'; +export { UiActionsServiceParams, UiActionsService } from './service'; +export { Action, createAction, IncompatibleActionError } from './actions'; export { buildContextMenuForActions } from './context_menu'; -export { IncompatibleActionError } from './triggers'; +export { Trigger } from './triggers'; + +/** + * @deprecated + * + * Use `UiActionsStart['getTriggerCompatibleActions']` or + * `UiActionsService['getTriggerCompatibleActions']` instead. + */ +export type GetActionsCompatibleWithTrigger = UiActionsService['getTriggerCompatibleActions']; + +/** + * @deprecated + * + * Use `UiActionsStart['executeTriggerActions']` or + * `UiActionsService['executeTriggerActions']` instead. + */ +export type ExecuteTriggerActions = UiActionsService['executeTriggerActions']; diff --git a/src/plugins/ui_actions/public/mocks.ts b/src/plugins/ui_actions/public/mocks.ts index 273c5dcf83e81..d2ba901f1040d 100644 --- a/src/plugins/ui_actions/public/mocks.ts +++ b/src/plugins/ui_actions/public/mocks.ts @@ -17,9 +17,9 @@ * under the License. */ +import { CoreSetup, CoreStart } from 'src/core/public'; import { UiActionsSetup, UiActionsStart } from '.'; import { plugin as pluginInitializer } from '.'; -// eslint-disable-next-line import { coreMock } from '../../../core/public/mocks'; export type Setup = jest.Mocked; @@ -45,17 +45,20 @@ const createStartContract = (): Start => { getTrigger: jest.fn(), getTriggerActions: jest.fn((id: string) => []), getTriggerCompatibleActions: jest.fn(), + clear: jest.fn(), + fork: jest.fn(), }; return startContract; }; -const createPlugin = async () => { +const createPlugin = ( + coreSetup: CoreSetup = coreMock.createSetup(), + coreStart: CoreStart = coreMock.createStart() +) => { const pluginInitializerContext = coreMock.createPluginInitializerContext(); - const coreSetup = coreMock.createSetup(); - const coreStart = coreMock.createStart(); const plugin = pluginInitializer(pluginInitializerContext); - const setup = await plugin.setup(coreSetup); + const setup = plugin.setup(coreSetup); return { pluginInitializerContext, @@ -63,7 +66,7 @@ const createPlugin = async () => { coreStart, plugin, setup, - doStart: async () => await plugin.start(coreStart), + doStart: (anotherCoreStart: CoreStart = coreStart) => plugin.start(anotherCoreStart), }; }; diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 12a9b7cbc6526..0874803db7d37 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -17,43 +17,30 @@ * under the License. */ -import { CoreStart, PluginInitializerContext, CoreSetup, Plugin } from 'src/core/public'; -import { UiActionsApi, ActionRegistry, TriggerRegistry } from './types'; -import { createApi } from './api'; - -export interface UiActionsSetup { - attachAction: UiActionsApi['attachAction']; - detachAction: UiActionsApi['detachAction']; - registerAction: UiActionsApi['registerAction']; - registerTrigger: UiActionsApi['registerTrigger']; -} +import { CoreStart, CoreSetup, Plugin, PluginInitializerContext } from 'src/core/public'; +import { UiActionsService } from './service'; + +export type UiActionsSetup = Pick< + UiActionsService, + 'attachAction' | 'detachAction' | 'registerAction' | 'registerTrigger' +>; -export type UiActionsStart = UiActionsApi; +export type UiActionsStart = PublicMethodsOf; export class UiActionsPlugin implements Plugin { - private readonly triggers: TriggerRegistry = new Map(); - private readonly actions: ActionRegistry = new Map(); - private api!: UiActionsApi; + private readonly service = new UiActionsService(); - constructor(initializerContext: PluginInitializerContext) { - this.api = createApi({ triggers: this.triggers, actions: this.actions }).api; - } + constructor(initializerContext: PluginInitializerContext) {} public setup(core: CoreSetup): UiActionsSetup { - return { - registerTrigger: this.api.registerTrigger, - registerAction: this.api.registerAction, - attachAction: this.api.attachAction, - detachAction: this.api.detachAction, - }; + return this.service; } public start(core: CoreStart): UiActionsStart { - return this.api; + return this.service; } public stop() { - this.actions.clear(); - this.triggers.clear(); + this.service.clear(); } } diff --git a/src/plugins/ui_actions/public/tests/helpers.ts b/src/plugins/ui_actions/public/service/index.ts similarity index 76% rename from src/plugins/ui_actions/public/tests/helpers.ts rename to src/plugins/ui_actions/public/service/index.ts index d1a4a71705a81..3998a2ea255cb 100644 --- a/src/plugins/ui_actions/public/tests/helpers.ts +++ b/src/plugins/ui_actions/public/service/index.ts @@ -17,12 +17,4 @@ * under the License. */ -import { UiActionsDependencies } from '../types'; - -export const createDeps = (): UiActionsDependencies => { - const deps: UiActionsDependencies = { - actions: new Map(), - triggers: new Map(), - }; - return deps; -}; +export * from './ui_actions_service'; diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts new file mode 100644 index 0000000000000..2bbe106c49a25 --- /dev/null +++ b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts @@ -0,0 +1,465 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { UiActionsService } from './ui_actions_service'; +import { Action } from '../actions'; +import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples'; +import { ActionRegistry, TriggerRegistry } from '../types'; +import { Trigger } from '../triggers'; + +const testAction1: Action = { + id: 'action1', + order: 1, + type: 'type1', + execute: async () => {}, + getDisplayName: () => 'test1', + getIconType: () => '', + isCompatible: async () => true, +}; + +const testAction2: Action = { + id: 'action2', + order: 2, + type: 'type2', + execute: async () => {}, + getDisplayName: () => 'test2', + getIconType: () => '', + isCompatible: async () => true, +}; + +describe('UiActionsService', () => { + test('can instantiate', () => { + new UiActionsService(); + }); + + describe('.registerTrigger()', () => { + test('can register a trigger', () => { + const service = new UiActionsService(); + service.registerTrigger({ + id: 'test', + }); + }); + }); + + describe('.getTrigger()', () => { + test('can get Trigger from registry', () => { + const service = new UiActionsService(); + service.registerTrigger({ + description: 'foo', + id: 'bar', + title: 'baz', + }); + + const trigger = service.getTrigger('bar'); + + expect(trigger).toEqual({ + description: 'foo', + id: 'bar', + title: 'baz', + }); + }); + + test('throws if trigger does not exist', () => { + const service = new UiActionsService(); + + expect(() => service.getTrigger('foo')).toThrowError( + 'Trigger [triggerId = foo] does not exist.' + ); + }); + }); + + describe('.registerAction()', () => { + test('can register an action', () => { + const service = new UiActionsService(); + service.registerAction({ + id: 'test', + execute: async () => {}, + getDisplayName: () => 'test', + getIconType: () => '', + isCompatible: async () => true, + type: 'test', + }); + }); + }); + + describe('.getTriggerActions()', () => { + const action1: Action = { + id: 'action1', + order: 1, + type: 'type1', + execute: async () => {}, + getDisplayName: () => 'test', + getIconType: () => '', + isCompatible: async () => true, + }; + const action2: Action = { + id: 'action2', + order: 2, + type: 'type2', + execute: async () => {}, + getDisplayName: () => 'test', + getIconType: () => '', + isCompatible: async () => true, + }; + + test('returns actions set on trigger', () => { + const service = new UiActionsService(); + + service.registerAction(action1); + service.registerAction(action2); + service.registerTrigger({ + description: 'foo', + id: 'trigger', + title: 'baz', + }); + + const list0 = service.getTriggerActions('trigger'); + + expect(list0).toHaveLength(0); + + service.attachAction('trigger', 'action1'); + const list1 = service.getTriggerActions('trigger'); + + expect(list1).toHaveLength(1); + expect(list1).toEqual([action1]); + + service.attachAction('trigger', 'action2'); + const list2 = service.getTriggerActions('trigger'); + + expect(list2).toHaveLength(2); + expect(!!list2.find(({ id }: any) => id === 'action1')).toBe(true); + expect(!!list2.find(({ id }: any) => id === 'action2')).toBe(true); + }); + }); + + describe('.getTriggerCompatibleActions()', () => { + test('can register and get actions', async () => { + const actions: ActionRegistry = new Map(); + const service = new UiActionsService({ actions }); + const helloWorldAction = createHelloWorldAction({} as any); + const length = actions.size; + + service.registerAction(helloWorldAction); + + expect(actions.size - length).toBe(1); + expect(actions.get(helloWorldAction.id)).toBe(helloWorldAction); + }); + + test('getTriggerCompatibleActions returns attached actions', async () => { + const service = new UiActionsService(); + const helloWorldAction = createHelloWorldAction({} as any); + + service.registerAction(helloWorldAction); + + const testTrigger: Trigger = { + id: 'MY-TRIGGER', + title: 'My trigger', + }; + service.registerTrigger(testTrigger); + service.attachAction('MY-TRIGGER', helloWorldAction.id); + + const compatibleActions = await service.getTriggerCompatibleActions('MY-TRIGGER', {}); + + expect(compatibleActions.length).toBe(1); + expect(compatibleActions[0].id).toBe(helloWorldAction.id); + }); + + test('filters out actions not applicable based on the context', async () => { + const service = new UiActionsService(); + const restrictedAction = createRestrictedAction<{ accept: boolean }>(context => { + return context.accept; + }); + + service.registerAction(restrictedAction); + + const testTrigger: Trigger = { + id: 'MY-TRIGGER', + title: 'My trigger', + }; + + service.registerTrigger(testTrigger); + service.attachAction(testTrigger.id, restrictedAction.id); + + const compatibleActions1 = await service.getTriggerCompatibleActions(testTrigger.id, { + accept: true, + }); + + expect(compatibleActions1.length).toBe(1); + + const compatibleActions2 = await service.getTriggerCompatibleActions(testTrigger.id, { + accept: false, + }); + + expect(compatibleActions2.length).toBe(0); + }); + + test(`throws an error with an invalid trigger ID`, async () => { + const service = new UiActionsService(); + + await expect(service.getTriggerCompatibleActions('I do not exist', {})).rejects.toMatchObject( + new Error('Trigger [triggerId = I do not exist] does not exist.') + ); + }); + + test('returns empty list if trigger not attached to any action', async () => { + const service = new UiActionsService(); + const testTrigger: Trigger = { + id: '123', + title: '123', + }; + service.registerTrigger(testTrigger); + + const actions = await service.getTriggerCompatibleActions(testTrigger.id, {}); + + expect(actions).toEqual([]); + }); + }); + + describe('.fork()', () => { + test('returns a new instance of the service', () => { + const service1 = new UiActionsService(); + const service2 = service1.fork(); + + expect(service1).not.toBe(service2); + expect(service2).toBeInstanceOf(UiActionsService); + }); + + test('triggers registered in original service are available in original an forked services', () => { + const service1 = new UiActionsService(); + service1.registerTrigger({ + id: 'foo', + }); + const service2 = service1.fork(); + + const trigger1 = service1.getTrigger('foo'); + const trigger2 = service2.getTrigger('foo'); + + expect(trigger1.id).toBe('foo'); + expect(trigger2.id).toBe('foo'); + }); + + test('triggers registered in forked service are not available in original service', () => { + const service1 = new UiActionsService(); + const service2 = service1.fork(); + + service2.registerTrigger({ + id: 'foo', + }); + + expect(() => service1.getTrigger('foo')).toThrowErrorMatchingInlineSnapshot( + `"Trigger [triggerId = foo] does not exist."` + ); + + const trigger2 = service2.getTrigger('foo'); + expect(trigger2.id).toBe('foo'); + }); + + test('forked service preserves trigger-to-actions mapping', () => { + const service1 = new UiActionsService(); + + service1.registerTrigger({ + id: 'foo', + }); + service1.registerAction(testAction1); + service1.attachAction('foo', testAction1.id); + + const service2 = service1.fork(); + + const actions1 = service1.getTriggerActions('foo'); + const actions2 = service2.getTriggerActions('foo'); + + expect(actions1).toHaveLength(1); + expect(actions2).toHaveLength(1); + expect(actions1[0].id).toBe(testAction1.id); + expect(actions2[0].id).toBe(testAction1.id); + }); + + test('new attachments in fork do not appear in original service', () => { + const service1 = new UiActionsService(); + + service1.registerTrigger({ + id: 'foo', + }); + service1.registerAction(testAction1); + service1.registerAction(testAction2); + service1.attachAction('foo', testAction1.id); + + const service2 = service1.fork(); + + expect(service1.getTriggerActions('foo')).toHaveLength(1); + expect(service2.getTriggerActions('foo')).toHaveLength(1); + + service2.attachAction('foo', testAction2.id); + + expect(service1.getTriggerActions('foo')).toHaveLength(1); + expect(service2.getTriggerActions('foo')).toHaveLength(2); + }); + + test('new attachments in original service do not appear in fork', () => { + const service1 = new UiActionsService(); + + service1.registerTrigger({ + id: 'foo', + }); + service1.registerAction(testAction1); + service1.registerAction(testAction2); + service1.attachAction('foo', testAction1.id); + + const service2 = service1.fork(); + + expect(service1.getTriggerActions('foo')).toHaveLength(1); + expect(service2.getTriggerActions('foo')).toHaveLength(1); + + service1.attachAction('foo', testAction2.id); + + expect(service1.getTriggerActions('foo')).toHaveLength(2); + expect(service2.getTriggerActions('foo')).toHaveLength(1); + }); + }); + + describe('registries', () => { + const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID'; + + test('can register trigger', () => { + const triggers: TriggerRegistry = new Map(); + const service = new UiActionsService({ triggers }); + + service.registerTrigger({ + description: 'foo', + id: 'bar', + title: 'baz', + }); + + expect(triggers.get('bar')).toEqual({ + description: 'foo', + id: 'bar', + title: 'baz', + }); + }); + + test('can register action', () => { + const actions: ActionRegistry = new Map(); + const service = new UiActionsService({ actions }); + + service.registerAction({ + id: HELLO_WORLD_ACTION_ID, + order: 13, + } as any); + + expect(actions.get(HELLO_WORLD_ACTION_ID)).toMatchObject({ + id: HELLO_WORLD_ACTION_ID, + order: 13, + }); + }); + + test('can attach an action to a trigger', () => { + const service = new UiActionsService(); + + const trigger: Trigger = { + id: 'MY-TRIGGER', + }; + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + service.registerTrigger(trigger); + service.registerAction(action); + service.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); + + const actions = service.getTriggerActions(trigger.id); + + expect(actions.length).toBe(1); + expect(actions[0].id).toBe(HELLO_WORLD_ACTION_ID); + }); + + test('can detach an action to a trigger', () => { + const service = new UiActionsService(); + + const trigger: Trigger = { + id: 'MY-TRIGGER', + }; + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + service.registerTrigger(trigger); + service.registerAction(action); + service.attachAction(trigger.id, HELLO_WORLD_ACTION_ID); + service.detachAction(trigger.id, HELLO_WORLD_ACTION_ID); + + const actions2 = service.getTriggerActions(trigger.id); + expect(actions2).toEqual([]); + }); + + test('detaching an invalid action from a trigger throws an error', async () => { + const service = new UiActionsService(); + + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + service.registerAction(action); + expect(() => service.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError( + 'No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID].' + ); + }); + + test('attaching an invalid action to a trigger throws an error', async () => { + const service = new UiActionsService(); + + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + service.registerAction(action); + expect(() => service.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError( + 'No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID].' + ); + }); + + test('cannot register another action with the same ID', async () => { + const service = new UiActionsService(); + + const action = { + id: HELLO_WORLD_ACTION_ID, + order: 25, + } as any; + + service.registerAction(action); + expect(() => service.registerAction(action)).toThrowError( + 'Action [action.id = HELLO_WORLD_ACTION_ID] already registered.' + ); + }); + + test('cannot register another trigger with the same ID', async () => { + const service = new UiActionsService(); + + const trigger = { id: 'MY-TRIGGER' } as any; + + service.registerTrigger(trigger); + expect(() => service.registerTrigger(trigger)).toThrowError( + 'Trigger [trigger.id = MY-TRIGGER] already registered.' + ); + }); + }); +}); diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts new file mode 100644 index 0000000000000..a62d2aa356435 --- /dev/null +++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts @@ -0,0 +1,194 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry } from '../types'; +import { Action } from '../actions'; +import { Trigger } from '../triggers/trigger'; +import { buildContextMenuForActions, openContextMenu } from '../context_menu'; + +export interface UiActionsServiceParams { + readonly triggers?: TriggerRegistry; + readonly actions?: ActionRegistry; + + /** + * A 1-to-N mapping from `Trigger` to zero or more `Action`. + */ + readonly triggerToActions?: TriggerToActionsRegistry; +} + +export class UiActionsService { + protected readonly triggers: TriggerRegistry; + protected readonly actions: ActionRegistry; + protected readonly triggerToActions: TriggerToActionsRegistry; + + constructor({ + triggers = new Map(), + actions = new Map(), + triggerToActions = new Map(), + }: UiActionsServiceParams = {}) { + this.triggers = triggers; + this.actions = actions; + this.triggerToActions = triggerToActions; + } + + public readonly registerTrigger = (trigger: Trigger) => { + if (this.triggers.has(trigger.id)) { + throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`); + } + + this.triggers.set(trigger.id, trigger); + this.triggerToActions.set(trigger.id, []); + }; + + public readonly getTrigger = (id: string) => { + const trigger = this.triggers.get(id); + + if (!trigger) { + throw new Error(`Trigger [triggerId = ${id}] does not exist.`); + } + + return trigger; + }; + + public readonly registerAction = (action: Action) => { + if (this.actions.has(action.id)) { + throw new Error(`Action [action.id = ${action.id}] already registered.`); + } + + this.actions.set(action.id, action); + }; + + public readonly attachAction = (triggerId: string, actionId: string): void => { + const trigger = this.triggers.get(triggerId); + + if (!trigger) { + throw new Error( + `No trigger [triggerId = ${triggerId}] exists, for attaching action [actionId = ${actionId}].` + ); + } + + const actionIds = this.triggerToActions.get(triggerId); + + if (!actionIds!.find(id => id === actionId)) { + this.triggerToActions.set(triggerId, [...actionIds!, actionId]); + } + }; + + public readonly detachAction = (triggerId: string, actionId: string) => { + const trigger = this.triggers.get(triggerId); + + if (!trigger) { + throw new Error( + `No trigger [triggerId = ${triggerId}] exists, for detaching action [actionId = ${actionId}].` + ); + } + + const actionIds = this.triggerToActions.get(triggerId); + + this.triggerToActions.set( + triggerId, + actionIds!.filter(id => id !== actionId) + ); + }; + + public readonly getTriggerActions = (triggerId: string) => { + // This line checks if trigger exists, otherwise throws. + this.getTrigger!(triggerId); + + const actionIds = this.triggerToActions.get(triggerId); + const actions = actionIds! + .map(actionId => this.actions.get(actionId)) + .filter(Boolean) as Action[]; + + return actions; + }; + + public readonly getTriggerCompatibleActions = async (triggerId: string, context: C) => { + const actions = this.getTriggerActions!(triggerId); + const isCompatibles = await Promise.all(actions.map(action => action.isCompatible(context))); + return actions.reduce( + (acc, action, i) => (isCompatibles[i] ? [...acc, action] : acc), + [] + ); + }; + + private async executeSingleAction(action: Action, actionContext: A) { + const href = action.getHref && action.getHref(actionContext); + + if (href) { + window.location.href = href; + return; + } + + await action.execute(actionContext); + } + + private async executeMultipleActions(actions: Action[], actionContext: C) { + const panel = await buildContextMenuForActions({ + actions, + actionContext, + closeMenu: () => session.close(), + }); + const session = openContextMenu([panel]); + } + + public readonly executeTriggerActions = async (triggerId: string, actionContext: C) => { + const actions = await this.getTriggerCompatibleActions!(triggerId, actionContext); + + if (!actions.length) { + throw new Error( + `No compatible actions found to execute for trigger [triggerId = ${triggerId}].` + ); + } + + if (actions.length === 1) { + await this.executeSingleAction(actions[0], actionContext); + return; + } + + await this.executeMultipleActions(actions, actionContext); + }; + + /** + * Removes all registered triggers and actions. + */ + public readonly clear = () => { + this.actions.clear(); + this.triggers.clear(); + this.triggerToActions.clear(); + }; + + /** + * "Fork" a separate instance of `UiActionsService` that inherits all existing + * triggers and actions, but going forward all new triggers and actions added + * to this instance of `UiActionsService` are only available within this instance. + */ + public readonly fork = (): UiActionsService => { + const triggers: TriggerRegistry = new Map(); + const actions: ActionRegistry = new Map(); + const triggerToActions: TriggerToActionsRegistry = new Map(); + + for (const [key, value] of this.triggers.entries()) triggers.set(key, value); + for (const [key, value] of this.actions.entries()) actions.set(key, value); + for (const [key, value] of this.triggerToActions.entries()) + triggerToActions.set(key, [...value]); + + return new UiActionsService({ triggers, actions, triggerToActions }); + }; +} diff --git a/src/plugins/ui_actions/public/tests/README.md b/src/plugins/ui_actions/public/tests/README.md new file mode 100644 index 0000000000000..8ea3a89e7c120 --- /dev/null +++ b/src/plugins/ui_actions/public/tests/README.md @@ -0,0 +1,2 @@ +This folder contains integration tests for the `ui_actions` plugin and +`test_samples` that other plugins can use in their tests. diff --git a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts similarity index 86% rename from src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts rename to src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts index 7f2506daee268..f8c196a623499 100644 --- a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.test.ts +++ b/src/plugins/ui_actions/public/tests/execute_trigger_actions.test.ts @@ -19,7 +19,8 @@ import { Action, createAction } from '../actions'; import { openContextMenu } from '../context_menu'; -import { UiActionsTestPluginReturn, uiActionsTestPlugin } from '../tests/test_plugin'; +import { uiActionsPluginMock } from '../mocks'; +import { Trigger } from '../triggers'; jest.mock('../context_menu'); @@ -37,14 +38,14 @@ function createTestAction(id: string, checkCompatibility: (context: A) => boo }); } -let uiActions: UiActionsTestPluginReturn; +let uiActions: ReturnType; const reset = () => { - uiActions = uiActionsTestPlugin(); + uiActions = uiActionsPluginMock.createPlugin(); uiActions.setup.registerTrigger({ id: CONTACT_USER_TRIGGER, - actionIds: ['SEND_MESSAGE_ACTION'], }); + uiActions.setup.attachAction(CONTACT_USER_TRIGGER, 'SEND_MESSAGE_ACTION'); executeFn.mockReset(); openContextMenuSpy.mockReset(); @@ -53,14 +54,15 @@ beforeEach(reset); test('executes a single action mapped to a trigger', async () => { const { setup, doStart } = uiActions; - const trigger = { + const trigger: Trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: ['test1'], }; const action = createTestAction('test1', () => true); + setup.registerTrigger(trigger); setup.registerAction(action); + setup.attachAction(trigger.id, 'test1'); const context = {}; const start = doStart(); @@ -72,12 +74,13 @@ test('executes a single action mapped to a trigger', async () => { test('throws an error if there are no compatible actions to execute', async () => { const { setup, doStart } = uiActions; - const trigger = { + const trigger: Trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: ['testaction'], }; + setup.registerTrigger(trigger); + setup.attachAction(trigger.id, 'testaction'); const context = {}; const start = doStart(); @@ -88,14 +91,15 @@ test('throws an error if there are no compatible actions to execute', async () = test('does not execute an incompatible action', async () => { const { setup, doStart } = uiActions; - const trigger = { + const trigger: Trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: ['test1'], }; const action = createTestAction<{ name: string }>('test1', ({ name }) => name === 'executeme'); + setup.registerTrigger(trigger); setup.registerAction(action); + setup.attachAction(trigger.id, 'test1'); const start = doStart(); const context = { @@ -108,16 +112,18 @@ test('does not execute an incompatible action', async () => { test('shows a context menu when more than one action is mapped to a trigger', async () => { const { setup, doStart } = uiActions; - const trigger = { + const trigger: Trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: ['test1', 'test2'], }; const action1 = createTestAction('test1', () => true); const action2 = createTestAction('test2', () => true); + setup.registerTrigger(trigger); setup.registerAction(action1); setup.registerAction(action2); + setup.attachAction(trigger.id, 'test1'); + setup.attachAction(trigger.id, 'test2'); expect(openContextMenu).toHaveBeenCalledTimes(0); @@ -134,7 +140,6 @@ test('passes whole action context to isCompatible()', async () => { const trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: ['test'], }; const action = createTestAction<{ foo: string }>('test', ({ foo }) => { expect(foo).toEqual('bar'); @@ -143,6 +148,8 @@ test('passes whole action context to isCompatible()', async () => { setup.registerTrigger(trigger); setup.registerAction(action); + setup.attachAction(trigger.id, 'test'); + const start = doStart(); const context = { foo: 'bar' }; diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts b/src/plugins/ui_actions/public/tests/get_trigger_actions.test.ts similarity index 93% rename from src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts rename to src/plugins/ui_actions/public/tests/get_trigger_actions.test.ts index aef4114ffb4c6..e91acd4c7151b 100644 --- a/src/plugins/ui_actions/public/triggers/get_trigger_actions.test.ts +++ b/src/plugins/ui_actions/public/tests/get_trigger_actions.test.ts @@ -18,7 +18,7 @@ */ import { Action } from '../actions'; -import { uiActionsTestPlugin } from '../tests/test_plugin'; +import { uiActionsPluginMock } from '../mocks'; const action1: Action = { id: 'action1', @@ -32,11 +32,10 @@ const action2: Action = { } as any; test('returns actions set on trigger', () => { - const { setup, doStart } = uiActionsTestPlugin(); + const { setup, doStart } = uiActionsPluginMock.createPlugin(); setup.registerAction(action1); setup.registerAction(action2); setup.registerTrigger({ - actionIds: [], description: 'foo', id: 'trigger', title: 'baz', diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts b/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts similarity index 82% rename from src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts rename to src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts index f4d2ea48ff6b9..a966003973aba 100644 --- a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.test.ts +++ b/src/plugins/ui_actions/public/tests/get_trigger_compatible_actions.test.ts @@ -18,35 +18,30 @@ */ import { createSayHelloAction } from '../tests/test_samples/say_hello_action'; -import { UiActionsTestPluginReturn, uiActionsTestPlugin } from '../tests/test_plugin'; +import { uiActionsPluginMock } from '../mocks'; import { createRestrictedAction, createHelloWorldAction } from '../tests/test_samples'; import { Action } from '../actions'; +import { Trigger } from '../triggers'; let action: Action<{ name: string }>; -let uiActions: UiActionsTestPluginReturn; +let uiActions: ReturnType; beforeEach(() => { - uiActions = uiActionsTestPlugin(); + uiActions = uiActionsPluginMock.createPlugin(); action = createSayHelloAction({} as any); uiActions.setup.registerAction(action); uiActions.setup.registerTrigger({ id: 'trigger', title: 'trigger', - actionIds: [], }); uiActions.setup.attachAction('trigger', action.id); }); -test('can register and get actions', async () => { - const { setup, plugin } = uiActions; +test('can register action', async () => { + const { setup } = uiActions; const helloWorldAction = createHelloWorldAction({} as any); - const length = (plugin as any).actions.size; setup.registerAction(helloWorldAction); - - expect((plugin as any).actions.size - length).toBe(1); - expect((plugin as any).actions.get(action.id)).toBe(action); - expect((plugin as any).actions.get(helloWorldAction.id)).toBe(helloWorldAction); }); test('getTriggerCompatibleActions returns attached actions', async () => { @@ -55,10 +50,9 @@ test('getTriggerCompatibleActions returns attached actions', async () => { setup.registerAction(helloWorldAction); - const testTrigger = { + const testTrigger: Trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: [], }; setup.registerTrigger(testTrigger); setup.attachAction('MY-TRIGGER', helloWorldAction.id); @@ -78,13 +72,13 @@ test('filters out actions not applicable based on the context', async () => { setup.registerAction(restrictedAction); - const testTrigger = { + const testTrigger: Trigger = { id: 'MY-TRIGGER', title: 'My trigger', - actionIds: [restrictedAction.id], }; setup.registerTrigger(testTrigger); + setup.attachAction(testTrigger.id, restrictedAction.id); const start = doStart(); let actions = await start.getTriggerCompatibleActions(testTrigger.id, { accept: true }); @@ -107,10 +101,9 @@ test(`throws an error with an invalid trigger ID`, async () => { test(`with a trigger mapping that maps to an non-existing action returns empty list`, async () => { const { setup, doStart } = uiActions; - const testTrigger = { + const testTrigger: Trigger = { id: '123', title: '123', - actionIds: ['I do not exist'], }; setup.registerTrigger(testTrigger); diff --git a/src/plugins/ui_actions/public/tests/index.ts b/src/plugins/ui_actions/public/tests/index.ts index 6f5610a7beb64..dbc34abb8acb4 100644 --- a/src/plugins/ui_actions/public/tests/index.ts +++ b/src/plugins/ui_actions/public/tests/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { uiActionsTestPlugin } from './test_plugin'; +export * from './test_samples'; diff --git a/src/plugins/ui_actions/public/tests/test_plugin.ts b/src/plugins/ui_actions/public/tests/test_plugin.ts deleted file mode 100644 index dcc42fd9f6fb2..0000000000000 --- a/src/plugins/ui_actions/public/tests/test_plugin.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { CoreSetup, CoreStart } from 'src/core/public'; -import { UiActionsPlugin, UiActionsSetup, UiActionsStart } from '../plugin'; - -export interface UiActionsTestPluginReturn { - plugin: UiActionsPlugin; - coreSetup: CoreSetup; - coreStart: CoreStart; - setup: UiActionsSetup; - doStart: (anotherCoreStart?: CoreStart) => UiActionsStart; -} - -export const uiActionsTestPlugin = ( - coreSetup: CoreSetup = {} as CoreSetup, - coreStart: CoreStart = {} as CoreStart -): UiActionsTestPluginReturn => { - const initializerContext = {} as any; - const plugin = new UiActionsPlugin(initializerContext); - const setup = plugin.setup(coreSetup); - - return { - plugin, - coreSetup, - coreStart, - setup, - doStart: (anotherCoreStart: CoreStart = coreStart) => { - const start = plugin.start(anotherCoreStart); - return start; - }, - }; -}; diff --git a/src/plugins/ui_actions/public/triggers/attach_action.ts b/src/plugins/ui_actions/public/triggers/attach_action.ts deleted file mode 100644 index 6c0beeae2bcd7..0000000000000 --- a/src/plugins/ui_actions/public/triggers/attach_action.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; - -export const attachAction: UiActionsApiPure['attachAction'] = ({ triggers }) => ( - triggerId, - actionId -) => { - const trigger = triggers.get(triggerId); - - if (!trigger) { - throw new Error( - `No trigger [triggerId = ${triggerId}] exists, for attaching action [actionId = ${actionId}].` - ); - } - - if (!trigger.actionIds.find(id => id === actionId)) { - trigger.actionIds.push(actionId); - } -}; diff --git a/src/plugins/ui_actions/public/triggers/detach_action.ts b/src/plugins/ui_actions/public/triggers/detach_action.ts deleted file mode 100644 index 710dcf9f5621b..0000000000000 --- a/src/plugins/ui_actions/public/triggers/detach_action.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; - -export const detachAction: UiActionsApiPure['detachAction'] = ({ triggers }) => ( - triggerId, - actionId -) => { - const trigger = triggers.get(triggerId); - - if (!trigger) { - throw new Error( - `No trigger [triggerId = ${triggerId}] exists, for detaching action [actionId = ${actionId}].` - ); - } - - trigger.actionIds = trigger.actionIds.filter(id => id !== actionId); -}; diff --git a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts b/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts deleted file mode 100644 index 71f69eb3bdc29..0000000000000 --- a/src/plugins/ui_actions/public/triggers/execute_trigger_actions.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; -import { buildContextMenuForActions, openContextMenu } from '../context_menu'; -import { Action } from '../actions'; - -const executeSingleAction = async (action: Action, actionContext: A) => { - const href = action.getHref && action.getHref(actionContext); - - // TODO: Do we need a `getHref()` special case? - if (href) { - window.location.href = href; - return; - } - - await action.execute(actionContext); -}; - -export const executeTriggerActions: UiActionsApiPure['executeTriggerActions'] = ({ api }) => async ( - triggerId, - actionContext -) => { - const actions = await api.getTriggerCompatibleActions!(triggerId, actionContext); - - if (!actions.length) { - throw new Error( - `No compatible actions found to execute for trigger [triggerId = ${triggerId}].` - ); - } - - if (actions.length === 1) { - await executeSingleAction(actions[0], actionContext); - return; - } - - const panel = await buildContextMenuForActions({ - actions, - actionContext, - closeMenu: () => session.close(), - }); - const session = openContextMenu([panel]); -}; diff --git a/src/plugins/ui_actions/public/triggers/get_trigger.test.ts b/src/plugins/ui_actions/public/triggers/get_trigger.test.ts deleted file mode 100644 index 88dd5a8990c9d..0000000000000 --- a/src/plugins/ui_actions/public/triggers/get_trigger.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { createApi } from '../api'; -import { createDeps } from '../tests/helpers'; - -test('can get Trigger from registry', () => { - const deps = createDeps(); - const { api } = createApi(deps); - api.registerTrigger({ - actionIds: [], - description: 'foo', - id: 'bar', - title: 'baz', - }); - - const trigger = api.getTrigger('bar'); - - expect(trigger).toEqual({ - actionIds: [], - description: 'foo', - id: 'bar', - title: 'baz', - }); -}); - -test('throws if trigger does not exist', () => { - const deps = createDeps(); - const { api } = createApi(deps); - - expect(() => api.getTrigger('foo')).toThrowError('Trigger [triggerId = foo] does not exist.'); -}); diff --git a/src/plugins/ui_actions/public/triggers/get_trigger.ts b/src/plugins/ui_actions/public/triggers/get_trigger.ts deleted file mode 100644 index 5c96200261a90..0000000000000 --- a/src/plugins/ui_actions/public/triggers/get_trigger.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; - -export const getTrigger: UiActionsApiPure['getTrigger'] = ({ triggers }) => id => { - const trigger = triggers.get(id); - - if (!trigger) { - throw new Error(`Trigger [triggerId = ${id}] does not exist.`); - } - - return trigger; -}; diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts b/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts deleted file mode 100644 index 37d7d5534c8c1..0000000000000 --- a/src/plugins/ui_actions/public/triggers/get_trigger_actions.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; -import { Action } from '../actions'; - -export const getTriggerActions: UiActionsApiPure['getTriggerActions'] = ({ - api, - actions, -}) => id => { - const trigger = api.getTrigger!(id); - return trigger.actionIds.map(actionId => actions.get(actionId)).filter(Boolean) as Action[]; -}; diff --git a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts b/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts deleted file mode 100644 index 8be0db7561db9..0000000000000 --- a/src/plugins/ui_actions/public/triggers/get_trigger_compatible_actions.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; -import { Action } from '../actions/action'; - -export const getTriggerCompatibleActions: UiActionsApiPure['getTriggerCompatibleActions'] = ({ - api, -}) => async (triggerId, context) => { - const actions = api.getTriggerActions!(triggerId); - const isCompatibles = await Promise.all(actions.map(action => action.isCompatible(context))); - return actions.reduce( - (acc, action, i) => (isCompatibles[i] ? [...acc, action] : acc), - [] - ); -}; diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index 3006a5428f45e..a34c6eda61ba0 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { IncompatibleActionError } from './incompatible_action_error'; +export { Trigger } from './trigger'; diff --git a/src/plugins/ui_actions/public/triggers/register_trigger.ts b/src/plugins/ui_actions/public/triggers/register_trigger.ts deleted file mode 100644 index c9a7bb211d05a..0000000000000 --- a/src/plugins/ui_actions/public/triggers/register_trigger.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { UiActionsApiPure } from '../types'; - -export const registerTrigger: UiActionsApiPure['registerTrigger'] = ({ triggers }) => trigger => { - if (triggers.has(trigger.id)) { - throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`); - } - - triggers.set(trigger.id, trigger); -}; diff --git a/src/plugins/ui_actions/public/triggers/registry.test.ts b/src/plugins/ui_actions/public/triggers/registry.test.ts deleted file mode 100644 index 6edb2b19a95e4..0000000000000 --- a/src/plugins/ui_actions/public/triggers/registry.test.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { createApi } from '../api'; -import { createDeps } from '../tests/helpers'; - -const HELLO_WORLD_ACTION_ID = 'HELLO_WORLD_ACTION_ID'; - -test('can register trigger', () => { - const deps = createDeps(); - const { api } = createApi(deps); - - api.registerTrigger({ - actionIds: [], - description: 'foo', - id: 'bar', - title: 'baz', - }); - - expect(deps.triggers.get('bar')).toEqual({ - actionIds: [], - description: 'foo', - id: 'bar', - title: 'baz', - }); -}); - -test('can register action', () => { - const deps = createDeps(); - const { api } = createApi(deps); - - api.registerAction({ - id: HELLO_WORLD_ACTION_ID, - order: 13, - } as any); - - expect(deps.actions.get(HELLO_WORLD_ACTION_ID)).toMatchObject({ - id: HELLO_WORLD_ACTION_ID, - order: 13, - }); -}); - -test('can attach an action to a trigger', () => { - const deps = createDeps(); - const { api } = createApi(deps); - const trigger = { - id: 'MY-TRIGGER', - actionIds: [], - }; - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - expect(trigger.actionIds).toEqual([]); - - api.registerTrigger(trigger); - api.registerAction(action); - api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); - - expect(trigger.actionIds).toEqual([HELLO_WORLD_ACTION_ID]); -}); - -test('can detach an action to a trigger', () => { - const deps = createDeps(); - const { api } = createApi(deps); - const trigger = { - id: 'MY-TRIGGER', - actionIds: [], - }; - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - expect(trigger.actionIds).toEqual([]); - - api.registerTrigger(trigger); - api.registerAction(action); - api.attachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); - api.detachAction('MY-TRIGGER', HELLO_WORLD_ACTION_ID); - - expect(trigger.actionIds).toEqual([]); -}); - -test('detaching an invalid action from a trigger throws an error', async () => { - const { api } = createApi({ actions: new Map(), triggers: new Map() }); - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - api.registerAction(action); - expect(() => api.detachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError( - 'No trigger [triggerId = i do not exist] exists, for detaching action [actionId = HELLO_WORLD_ACTION_ID].' - ); -}); - -test('attaching an invalid action to a trigger throws an error', async () => { - const { api } = createApi({ actions: new Map(), triggers: new Map() }); - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - api.registerAction(action); - expect(() => api.attachAction('i do not exist', HELLO_WORLD_ACTION_ID)).toThrowError( - 'No trigger [triggerId = i do not exist] exists, for attaching action [actionId = HELLO_WORLD_ACTION_ID].' - ); -}); - -test('cannot register another action with the same ID', async () => { - const { api } = createApi({ actions: new Map(), triggers: new Map() }); - const action = { - id: HELLO_WORLD_ACTION_ID, - order: 25, - } as any; - - api.registerAction(action); - expect(() => api.registerAction(action)).toThrowError( - 'Action [action.id = HELLO_WORLD_ACTION_ID] already registered.' - ); -}); - -test('cannot register another trigger with the same ID', async () => { - const { api } = createApi({ actions: new Map(), triggers: new Map() }); - const trigger = { id: 'MY-TRIGGER' } as any; - - api.registerTrigger(trigger); - expect(() => api.registerTrigger(trigger)).toThrowError( - 'Trigger [trigger.id = MY-TRIGGER] already registered.' - ); -}); diff --git a/src/plugins/ui_actions/public/triggers/trigger.ts b/src/plugins/ui_actions/public/triggers/trigger.ts index 3db11953053d5..ba83f5619e250 100644 --- a/src/plugins/ui_actions/public/triggers/trigger.ts +++ b/src/plugins/ui_actions/public/triggers/trigger.ts @@ -21,5 +21,4 @@ export interface Trigger { id: string; title?: string; description?: string; - actionIds: string[]; } diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index ed4728342b751..9bd6ffdef2af3 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -20,39 +20,6 @@ import { Action } from './actions/action'; import { Trigger } from './triggers/trigger'; -export { Action } from './actions'; -export { Trigger } from './triggers/trigger'; - -export type ExecuteTriggerActions = (triggerId: string, actionContext: A) => Promise; - -export type GetActionsCompatibleWithTrigger = ( - triggerId: string, - context: C -) => Promise; - -export interface UiActionsApi { - attachAction: (triggerId: string, actionId: string) => void; - detachAction: (triggerId: string, actionId: string) => void; - executeTriggerActions: ExecuteTriggerActions; - getTrigger: (id: string) => Trigger; - getTriggerActions: (id: string) => Action[]; - getTriggerCompatibleActions: (triggerId: string, context: C) => Promise>>; - registerAction: (action: Action) => void; - registerTrigger: (trigger: Trigger) => void; -} - -export interface UiActionsDependencies { - actions: ActionRegistry; - triggers: TriggerRegistry; -} - -export interface UiActionsDependenciesInternal extends UiActionsDependencies { - api: Readonly>; -} - -export type UiActionsApiPure = { - [K in keyof UiActionsApi]: (deps: UiActionsDependenciesInternal) => UiActionsApi[K]; -}; - export type TriggerRegistry = Map; export type ActionRegistry = Map; +export type TriggerToActionsRegistry = Map; From 85d737c401d492a715efcdf533efbd21d014d9c0 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 17 Feb 2020 15:02:21 +0100 Subject: [PATCH 32/65] ExecutionContract (#57559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add ExecutionContract class * test: 💍 add Execution and Executor tests * feat: 🎸 add .execute() method to ExpressionsService * refactor: 💡 replace ExpressionDataHandler by ExecutionContract * fix: 🐛 fix TypeScript typecheck errors * docs: ✏️ add JSDocs to ExecutionContract * refactor: 💡 make .ast and .expresions both optional * test: 💍 fix test * test: 💍 fix a test * test: 💍 fix interpreter functional tests --- .../common/execution/execution.test.ts | 37 ++++- .../expressions/common/execution/execution.ts | 37 ++++- .../execution/execution_contract.test.ts | 140 ++++++++++++++++++ .../common/execution/execution_contract.ts | 90 +++++++++++ .../expressions/common/execution/index.ts | 1 + .../executor/executor.execution.test.ts | 59 ++++++++ .../common/executor/executor.test.ts | 4 +- .../expressions/common/executor/executor.ts | 24 ++- .../common/service/expressions_services.ts | 24 ++- .../expressions/public/execute.test.ts | 100 ------------- src/plugins/expressions/public/execute.ts | 138 ----------------- src/plugins/expressions/public/index.ts | 2 +- src/plugins/expressions/public/loader.test.ts | 127 ++++------------ src/plugins/expressions/public/loader.ts | 42 +++--- src/plugins/expressions/public/mocks.tsx | 1 - src/plugins/expressions/public/plugin.test.ts | 9 ++ src/plugins/expressions/public/plugin.ts | 7 +- src/plugins/expressions/public/services.ts | 5 + .../public/np_ready/app/components/main.tsx | 11 +- .../public/np_ready/types.ts | 9 +- .../test_suites/run_pipeline/basic.ts | 2 +- .../test_suites/run_pipeline/helpers.ts | 6 +- 22 files changed, 477 insertions(+), 398 deletions(-) create mode 100644 src/plugins/expressions/common/execution/execution_contract.test.ts create mode 100644 src/plugins/expressions/common/execution/execution_contract.ts create mode 100644 src/plugins/expressions/common/executor/executor.execution.test.ts delete mode 100644 src/plugins/expressions/public/execute.test.ts delete mode 100644 src/plugins/expressions/public/execute.ts diff --git a/src/plugins/expressions/common/execution/execution.test.ts b/src/plugins/expressions/common/execution/execution.test.ts index 3937bd309327d..eeb1ed80e8d0d 100644 --- a/src/plugins/expressions/common/execution/execution.test.ts +++ b/src/plugins/expressions/common/execution/execution.test.ts @@ -21,6 +21,7 @@ import { Execution } from './execution'; import { parseExpression } from '../ast'; import { createUnitTestExecutor } from '../test_helpers'; import { ExpressionFunctionDefinition } from '../../public'; +import { ExecutionContract } from './execution_contract'; const createExecution = ( expression: string = 'foo bar=123', @@ -48,7 +49,7 @@ const run = async ( describe('Execution', () => { test('can instantiate', () => { const execution = createExecution('foo bar=123'); - expect(execution.params.ast.chain[0].arguments.bar).toEqual([123]); + expect(execution.state.get().ast.chain[0].arguments.bar).toEqual([123]); }); test('initial input is null at creation', () => { @@ -127,6 +128,40 @@ describe('Execution', () => { }); }); + describe('.expression', () => { + test('uses expression passed in to constructor', () => { + const expression = 'add val="1"'; + const executor = createUnitTestExecutor(); + const execution = new Execution({ + executor, + expression, + }); + expect(execution.expression).toBe(expression); + }); + + test('generates expression from AST if not passed to constructor', () => { + const expression = 'add val="1"'; + const executor = createUnitTestExecutor(); + const execution = new Execution({ + ast: parseExpression(expression), + executor, + }); + expect(execution.expression).toBe(expression); + }); + }); + + describe('.contract', () => { + test('is instance of ExecutionContract', () => { + const execution = createExecution('add val=1'); + expect(execution.contract).toBeInstanceOf(ExecutionContract); + }); + + test('execution returns the same expression string', () => { + const execution = createExecution('add val=1'); + expect(execution.expression).toBe(execution.contract.getExpression()); + }); + }); + describe('execution context', () => { test('context.variables is an object', async () => { const { result } = (await run('introspectContext key="variables"')) as any; diff --git a/src/plugins/expressions/common/execution/execution.ts b/src/plugins/expressions/common/execution/execution.ts index 7f4efafc13de8..2a272e187cffc 100644 --- a/src/plugins/expressions/common/execution/execution.ts +++ b/src/plugins/expressions/common/execution/execution.ts @@ -24,17 +24,25 @@ import { createError } from '../util'; import { Defer } from '../../../kibana_utils/common'; import { RequestAdapter, DataAdapter } from '../../../inspector/common'; import { isExpressionValueError } from '../expression_types/specs/error'; -import { ExpressionAstExpression, ExpressionAstFunction, parse } from '../ast'; +import { + ExpressionAstExpression, + ExpressionAstFunction, + parse, + formatExpression, + parseExpression, +} from '../ast'; import { ExecutionContext, DefaultInspectorAdapters } from './types'; import { getType } from '../expression_types'; import { ArgumentType, ExpressionFunction } from '../expression_functions'; import { getByAlias } from '../util/get_by_alias'; +import { ExecutionContract } from './execution_contract'; export interface ExecutionParams< ExtraContext extends Record = Record > { executor: Executor; - ast: ExpressionAstExpression; + ast?: ExpressionAstExpression; + expression?: string; context?: ExtraContext; } @@ -85,6 +93,19 @@ export class Execution< */ private readonly firstResultFuture = new Defer(); + /** + * Contract is a public representation of `Execution` instances. Contract we + * can return to other plugins for their consumption. + */ + public readonly contract: ExecutionContract< + ExtraContext, + Input, + Output, + InspectorAdapters + > = new ExecutionContract(this); + + public readonly expression: string; + public get result(): Promise { return this.firstResultFuture.promise; } @@ -94,7 +115,17 @@ export class Execution< } constructor(public readonly params: ExecutionParams) { - const { executor, ast } = params; + const { executor } = params; + + if (!params.ast && !params.expression) { + throw new TypeError('Execution params should contain at least .ast or .expression key.'); + } else if (params.ast && params.expression) { + throw new TypeError('Execution params cannot contain both .ast and .expression key.'); + } + + this.expression = params.expression || formatExpression(params.ast!); + const ast = params.ast || parseExpression(this.expression); + this.state = createExecutionContainer({ ...executor.state.get(), state: 'not-started', diff --git a/src/plugins/expressions/common/execution/execution_contract.test.ts b/src/plugins/expressions/common/execution/execution_contract.test.ts new file mode 100644 index 0000000000000..c33f8a1a0f36e --- /dev/null +++ b/src/plugins/expressions/common/execution/execution_contract.test.ts @@ -0,0 +1,140 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Execution } from './execution'; +import { parseExpression } from '../ast'; +import { createUnitTestExecutor } from '../test_helpers'; +import { ExecutionContract } from './execution_contract'; + +const createExecution = ( + expression: string = 'foo bar=123', + context: Record = {} +) => { + const executor = createUnitTestExecutor(); + const execution = new Execution({ + executor, + ast: parseExpression(expression), + context, + }); + return execution; +}; + +describe('ExecutionContract', () => { + test('can instantiate', () => { + const execution = createExecution('foo bar=123'); + const contract = new ExecutionContract(execution); + expect(contract).toBeInstanceOf(ExecutionContract); + }); + + test('can get the AST of expression', () => { + const execution = createExecution('foo bar=123'); + const contract = new ExecutionContract(execution); + expect(contract.getAst()).toMatchObject({ + type: 'expression', + chain: expect.any(Array), + }); + }); + + test('can get expression string', () => { + const execution = createExecution('foo bar=123'); + const contract = new ExecutionContract(execution); + expect(contract.getExpression()).toBe('foo bar=123'); + }); + + test('can cancel execution', () => { + const execution = createExecution('foo bar=123'); + const spy = jest.spyOn(execution, 'cancel'); + const contract = new ExecutionContract(execution); + + expect(spy).toHaveBeenCalledTimes(0); + contract.cancel(); + expect(spy).toHaveBeenCalledTimes(1); + }); + + test('can get inspector adapters', () => { + const execution = createExecution('foo bar=123'); + const contract = new ExecutionContract(execution); + expect(contract.inspect()).toMatchObject({ + data: expect.any(Object), + requests: expect.any(Object), + }); + }); + + test('can get error result of the expression execution', async () => { + const execution = createExecution('foo bar=123'); + const contract = new ExecutionContract(execution); + execution.start(); + + const result = await contract.getData(); + + expect(result).toMatchObject({ + type: 'error', + }); + }); + + test('can get result of the expression execution', async () => { + const execution = createExecution('var_set name="foo" value="bar" | var name="foo"'); + const contract = new ExecutionContract(execution); + execution.start(); + + const result = await contract.getData(); + + expect(result).toBe('bar'); + }); + + describe('isPending', () => { + test('is true if execution has not been started', async () => { + const execution = createExecution('var_set name="foo" value="bar" | var name="foo"'); + const contract = new ExecutionContract(execution); + expect(contract.isPending).toBe(true); + }); + + test('is true when execution just started', async () => { + const execution = createExecution('var_set name="foo" value="bar" | var name="foo"'); + const contract = new ExecutionContract(execution); + + execution.start(); + + expect(contract.isPending).toBe(true); + }); + + test('is false when execution finished successfully', async () => { + const execution = createExecution('var_set name="foo" value="bar" | var name="foo"'); + const contract = new ExecutionContract(execution); + + execution.start(); + await execution.result; + + expect(contract.isPending).toBe(false); + expect(execution.state.get().state).toBe('result'); + }); + + test('is false when execution finished with error', async () => { + const execution = createExecution('var_set name="foo" value="bar" | var name="foo"'); + const contract = new ExecutionContract(execution); + + execution.start(); + await execution.result; + execution.state.get().state = 'error'; + + expect(contract.isPending).toBe(false); + expect(execution.state.get().state).toBe('error'); + }); + }); +}); diff --git a/src/plugins/expressions/common/execution/execution_contract.ts b/src/plugins/expressions/common/execution/execution_contract.ts new file mode 100644 index 0000000000000..8c784352b9fdf --- /dev/null +++ b/src/plugins/expressions/common/execution/execution_contract.ts @@ -0,0 +1,90 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Execution } from './execution'; + +/** + * `ExecutionContract` is a wrapper around `Execution` class. It provides the + * same functionality but does not expose Expressions plugin internals. + */ +export class ExecutionContract< + ExtraContext extends Record = Record, + Input = unknown, + Output = unknown, + InspectorAdapters = unknown +> { + public get isPending(): boolean { + const state = this.execution.state.get().state; + const finished = state === 'error' || state === 'result'; + return !finished; + } + + constructor( + protected readonly execution: Execution + ) {} + + /** + * Cancel the execution of the expression. This will set abort signal + * (available in execution context) to aborted state, letting expression + * functions to stop their execution. + */ + cancel = () => { + this.execution.cancel(); + }; + + /** + * Returns the final output of expression, if any error happens still + * wraps that error into `ExpressionValueError` type and returns that. + * This function never throws. + */ + getData = async () => { + try { + return await this.execution.result; + } catch (e) { + return { + type: 'error', + error: { + type: e.type, + message: e.message, + stack: e.stack, + }, + }; + } + }; + + /** + * Get string representation of the expression. Returns the original string + * if execution was started from a string. If execution was started from an + * AST this method returns a string generated from AST. + */ + getExpression = () => { + return this.execution.expression; + }; + + /** + * Get AST used to execute the expression. + */ + getAst = () => this.execution.state.get().ast; + + /** + * Get Inspector adapters provided to all functions of expression through + * execution context. + */ + inspect = () => this.execution.inspectorAdapters; +} diff --git a/src/plugins/expressions/common/execution/index.ts b/src/plugins/expressions/common/execution/index.ts index 2452b0999d23e..fd5c0244438d7 100644 --- a/src/plugins/expressions/common/execution/index.ts +++ b/src/plugins/expressions/common/execution/index.ts @@ -20,3 +20,4 @@ export * from './types'; export * from './container'; export * from './execution'; +export * from './execution_contract'; diff --git a/src/plugins/expressions/common/executor/executor.execution.test.ts b/src/plugins/expressions/common/executor/executor.execution.test.ts new file mode 100644 index 0000000000000..eec7b5c907e29 --- /dev/null +++ b/src/plugins/expressions/common/executor/executor.execution.test.ts @@ -0,0 +1,59 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Executor } from './executor'; +import { parseExpression } from '../ast'; + +// eslint-disable-next-line +const { __getArgs } = require('../execution/execution'); + +jest.mock('../execution/execution', () => { + const mockedModule = { + args: undefined, + __getArgs: () => mockedModule.args, + Execution: function ExecutionMock(...args: any) { + mockedModule.args = args; + }, + }; + + return mockedModule; +}); + +describe('Executor mocked execution tests', () => { + describe('createExecution()', () => { + describe('when execution is created from string', () => { + test('passes expression string to Execution', () => { + const executor = new Executor(); + executor.createExecution('foo bar="baz"'); + + expect(__getArgs()[0].expression).toBe('foo bar="baz"'); + }); + }); + + describe('when execution is created from AST', () => { + test('does not pass in expression string', () => { + const executor = new Executor(); + const ast = parseExpression('foo bar="baz"'); + executor.createExecution(ast); + + expect(__getArgs()[0].expression).toBe(undefined); + }); + }); + }); +}); diff --git a/src/plugins/expressions/common/executor/executor.test.ts b/src/plugins/expressions/common/executor/executor.test.ts index 502728bb66403..4e43cedd18157 100644 --- a/src/plugins/expressions/common/executor/executor.test.ts +++ b/src/plugins/expressions/common/executor/executor.test.ts @@ -130,7 +130,7 @@ describe('Executor', () => { const execution = executor.createExecution('foo bar="baz"'); expect(execution).toBeInstanceOf(Execution); - expect(execution.params.ast.chain[0].function).toBe('foo'); + expect(execution.state.get().ast.chain[0].function).toBe('foo'); }); test('returns Execution object from AST', () => { @@ -139,7 +139,7 @@ describe('Executor', () => { const execution = executor.createExecution(ast); expect(execution).toBeInstanceOf(Execution); - expect(execution.params.ast.chain[0].function).toBe('foo'); + expect(execution.state.get().ast.chain[0].function).toBe('foo'); }); test('Execution inherits context from Executor', () => { diff --git a/src/plugins/expressions/common/executor/executor.ts b/src/plugins/expressions/common/executor/executor.ts index 5c27201b43fc0..24c1648a8cd0f 100644 --- a/src/plugins/expressions/common/executor/executor.ts +++ b/src/plugins/expressions/common/executor/executor.ts @@ -22,12 +22,12 @@ import { ExecutorState, ExecutorContainer } from './container'; import { createExecutorContainer } from './container'; import { AnyExpressionFunctionDefinition, ExpressionFunction } from '../expression_functions'; -import { Execution } from '../execution/execution'; +import { Execution, ExecutionParams } from '../execution/execution'; import { IRegistry } from '../types'; import { ExpressionType } from '../expression_types/expression_type'; import { AnyExpressionTypeDefinition } from '../expression_types/types'; import { getType } from '../expression_types'; -import { ExpressionAstExpression, ExpressionAstNode, parseExpression } from '../ast'; +import { ExpressionAstExpression, ExpressionAstNode } from '../ast'; import { typeSpecs } from '../expression_types/specs'; import { functionSpecs } from '../expression_functions/specs'; @@ -186,19 +186,27 @@ export class Executor = Record = Record>( + public createExecution< + ExtraContext extends Record = Record, + Input = unknown, + Output = unknown + >( ast: string | ExpressionAstExpression, context: ExtraContext = {} as ExtraContext - ): Execution { - if (typeof ast === 'string') ast = parseExpression(ast); - const execution = new Execution({ - ast, + ): Execution { + const params: ExecutionParams = { executor: this, context: { ...this.context, ...context, } as Context & ExtraContext, - }); + }; + + if (typeof ast === 'string') params.expression = ast; + else params.ast = ast; + + const execution = new Execution(params); + return execution; } } diff --git a/src/plugins/expressions/common/service/expressions_services.ts b/src/plugins/expressions/common/service/expressions_services.ts index 8543fbe0fced2..9663c05f0d7c2 100644 --- a/src/plugins/expressions/common/service/expressions_services.ts +++ b/src/plugins/expressions/common/service/expressions_services.ts @@ -20,6 +20,7 @@ import { Executor } from '../executor'; import { ExpressionRendererRegistry } from '../expression_renderers'; import { ExpressionAstExpression } from '../ast'; +import { ExecutionContract } from '../execution/execution_contract'; export type ExpressionsServiceSetup = ReturnType; export type ExpressionsServiceStart = ReturnType; @@ -117,6 +118,26 @@ export class ExpressionsService { context?: ExtraContext ): Promise => this.executor.run(ast, input, context); + /** + * Starts expression execution and immediately returns `ExecutionContract` + * instance that tracks the progress of the execution and can be used to + * interact with the execution. + */ + public readonly execute = < + Input = unknown, + Output = unknown, + ExtraContext extends Record = Record + >( + ast: string | ExpressionAstExpression, + // This any is for legacy reasons. + input: Input = { type: 'null' } as any, + context?: ExtraContext + ): ExecutionContract => { + const execution = this.executor.createExecution(ast, context); + execution.start(input); + return execution.contract; + }; + public setup() { const { executor, renderers, registerFunction, run } = this; @@ -144,7 +165,7 @@ export class ExpressionsService { } public start() { - const { executor, renderers, run } = this; + const { execute, executor, renderers, run } = this; const getFunction = executor.getFunction.bind(executor); const getFunctions = executor.getFunctions.bind(executor); @@ -154,6 +175,7 @@ export class ExpressionsService { const getTypes = executor.getTypes.bind(executor); return { + execute, getFunction, getFunctions, getRenderer, diff --git a/src/plugins/expressions/public/execute.test.ts b/src/plugins/expressions/public/execute.test.ts deleted file mode 100644 index 2f2a303bad4c4..0000000000000 --- a/src/plugins/expressions/public/execute.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { execute, ExpressionDataHandler } from './execute'; -import { ExpressionAstExpression, parseExpression } from '../common'; - -jest.mock('./services', () => ({ - getInterpreter: () => { - return { - interpretAst: async (expression: ExpressionAstExpression) => { - return {}; - }, - }; - }, - getNotifications: jest.fn(() => { - return { - toasts: { - addError: jest.fn(() => {}), - }, - }; - }), -})); - -describe('execute helper function', () => { - it('returns ExpressionDataHandler instance', () => { - const response = execute(''); - expect(response).toBeInstanceOf(ExpressionDataHandler); - }); -}); - -describe('ExpressionDataHandler', () => { - const expressionString = ''; - - describe('constructor', () => { - it('accepts expression string', () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); - expect(expressionDataHandler.getExpression()).toEqual(expressionString); - }); - - it('accepts expression AST', () => { - const expressionAST = parseExpression(expressionString) as ExpressionAstExpression; - const expressionDataHandler = new ExpressionDataHandler(expressionAST, {}); - expect(expressionDataHandler.getExpression()).toEqual(expressionString); - expect(expressionDataHandler.getAst()).toEqual(expressionAST); - }); - - it('allows passing in context', () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, { - context: { test: 'hello' }, - }); - expect(expressionDataHandler.getExpression()).toEqual(expressionString); - }); - - it('allows passing in search context', () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, { - searchContext: { filters: [] }, - }); - expect(expressionDataHandler.getExpression()).toEqual(expressionString); - }); - }); - - describe('getData()', () => { - it('returns a promise', () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); - expect(expressionDataHandler.getData()).toBeInstanceOf(Promise); - }); - - it('promise resolves with data', async () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); - expect(await expressionDataHandler.getData()).toEqual({}); - }); - }); - - it('cancel() aborts request', () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); - expressionDataHandler.cancel(); - }); - - it('inspect() returns correct inspector adapters', () => { - const expressionDataHandler = new ExpressionDataHandler(expressionString, {}); - expect(expressionDataHandler.inspect()).toHaveProperty('requests'); - expect(expressionDataHandler.inspect()).toHaveProperty('data'); - }); -}); diff --git a/src/plugins/expressions/public/execute.ts b/src/plugins/expressions/public/execute.ts deleted file mode 100644 index c07fb9ad0549c..0000000000000 --- a/src/plugins/expressions/public/execute.ts +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { DataAdapter, RequestAdapter, Adapters } from '../../inspector/public'; -import { getInterpreter } from './services'; -import { IExpressionLoaderParams } from './types'; -import { - ExpressionAstExpression, - parseExpression, - formatExpression, - ExpressionValue, -} from '../common'; - -/** - * The search context describes a specific context (filters, time range and query) - * that will be applied to the expression for execution. Not every expression will - * be effected by that. You have to use special functions - * that will pick up this search context and forward it to following functions that - * understand it. - */ - -export class ExpressionDataHandler { - private abortController: AbortController; - private expression: string; - private ast: ExpressionAstExpression; - - private inspectorAdapters: Adapters; - private promise: Promise; - - public isPending: boolean = true; - constructor(expression: string | ExpressionAstExpression, params: IExpressionLoaderParams) { - if (typeof expression === 'string') { - this.expression = expression; - this.ast = parseExpression(expression); - } else { - this.ast = expression; - this.expression = formatExpression(this.ast); - } - - this.abortController = new AbortController(); - this.inspectorAdapters = params.inspectorAdapters || this.getActiveInspectorAdapters(); - - const defaultInput = { type: 'null' }; - const interpreter = getInterpreter(); - this.promise = interpreter - .interpretAst(this.ast, params.context || defaultInput, { - search: params.searchContext, - inspectorAdapters: this.inspectorAdapters, - abortSignal: this.abortController.signal, - variables: params.variables, - }) - .then( - (v: ExpressionValue) => { - this.isPending = false; - return v; - }, - () => { - this.isPending = false; - } - ) as Promise; - } - - cancel = () => { - this.abortController.abort(); - }; - - getData = async () => { - try { - return await this.promise; - } catch (e) { - return { - type: 'error', - error: { - type: e.type, - message: e.message, - stack: e.stack, - }, - }; - } - }; - - getExpression = () => { - return this.expression; - }; - - getAst = () => { - return this.ast; - }; - - inspect = () => { - return this.inspectorAdapters; - }; - - /** - * Returns an object of all inspectors for this vis object. - * This must only be called after this.type has properly be initialized, - * since we need to read out data from the the vis type to check which - * inspectors are available. - */ - private getActiveInspectorAdapters = (): Adapters => { - const adapters: Adapters = {}; - - // Add the requests inspector adapters if the vis type explicitly requested it via - // inspectorAdapters.requests: true in its definition or if it's using the courier - // request handler, since that will automatically log its requests. - adapters.requests = new RequestAdapter(); - - // Add the data inspector adapter if the vis type requested it or if the - // vis is using courier, since we know that courier supports logging - // its data. - adapters.data = new DataAdapter(); - - return adapters; - }; -} - -export function execute( - expression: string | ExpressionAstExpression, - params: IExpressionLoaderParams = {} -): ExpressionDataHandler { - return new ExpressionDataHandler(expression, params); -} diff --git a/src/plugins/expressions/public/index.ts b/src/plugins/expressions/public/index.ts index 5f64c11f4efe6..06dd951cd5410 100644 --- a/src/plugins/expressions/public/index.ts +++ b/src/plugins/expressions/public/index.ts @@ -37,7 +37,6 @@ export { ReactExpressionRendererProps, ReactExpressionRendererType, } from './react_expression_renderer'; -export { ExpressionDataHandler } from './execute'; export { ExpressionRenderHandler } from './render'; export { AnyExpressionFunctionDefinition, @@ -48,6 +47,7 @@ export { DatatableColumnType, DatatableRow, Execution, + ExecutionContract, ExecutionContainer, ExecutionContext, ExecutionParams, diff --git a/src/plugins/expressions/public/loader.test.ts b/src/plugins/expressions/public/loader.test.ts index 480434244d6f5..e07a22a5e1d60 100644 --- a/src/plugins/expressions/public/loader.test.ts +++ b/src/plugins/expressions/public/loader.test.ts @@ -19,10 +19,12 @@ import { first, skip, toArray } from 'rxjs/operators'; import { loader, ExpressionLoader } from './loader'; -import { ExpressionDataHandler } from './execute'; import { Observable } from 'rxjs'; import { ExpressionAstExpression, parseExpression, IInterpreterRenderHandlers } from '../common'; +// eslint-disable-next-line +const { __getLastExecution } = require('./services'); + const element: HTMLElement = null as any; jest.mock('./services', () => { @@ -33,7 +35,13 @@ jest.mock('./services', () => { }, }, }; - return { + + // eslint-disable-next-line + const service = new (require('../common/service/expressions_services').ExpressionsService as any)(); + + const moduleMock = { + __execution: undefined, + __getLastExecution: () => moduleMock.__execution, getInterpreter: () => { return { interpretAst: async (expression: ExpressionAstExpression) => { @@ -51,17 +59,19 @@ jest.mock('./services', () => { }, }; }), + getExpressionsService: () => service, }; -}); -jest.mock('./execute', () => { - const actual = jest.requireActual('./execute'); - return { - ExpressionDataHandler: jest - .fn() - .mockImplementation((...args) => new actual.ExpressionDataHandler(...args)), - execute: jest.fn().mockReturnValue(actual.execute), + const execute = service.execute; + service.execute = (...args: any) => { + const execution = execute(...args); + jest.spyOn(execution, 'getData'); + jest.spyOn(execution, 'cancel'); + moduleMock.__execution = execution; + return execution; }; + + return moduleMock; }); describe('execute helper function', () => { @@ -97,9 +107,9 @@ describe('ExpressionLoader', () => { }); it('emits on $data when data is available', async () => { - const expressionLoader = new ExpressionLoader(element, expressionString, {}); + const expressionLoader = new ExpressionLoader(element, 'var foo', { variables: { foo: 123 } }); const response = await expressionLoader.data$.pipe(first()).toPromise(); - expect(response).toEqual({ type: 'render', as: 'test' }); + expect(response).toBe(123); }); it('emits on loading$ on initial load and on updates', async () => { @@ -128,94 +138,13 @@ describe('ExpressionLoader', () => { }); it('cancels the previous request when the expression is updated', () => { - const cancelMock = jest.fn(); - - (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({ - getData: () => true, - cancel: cancelMock, - isPending: () => true, - inspect: () => {}, - })); - - const expressionLoader = new ExpressionLoader(element, expressionString, {}); - expressionLoader.update('new', {}); - - expect(cancelMock).toHaveBeenCalledTimes(1); - }); - - it('does not send an observable message if a request was aborted', () => { - const cancelMock = jest.fn(); - - const getData = jest - .fn() - .mockResolvedValueOnce({ - type: 'error', - error: { - name: 'AbortError', - }, - }) - .mockResolvedValueOnce({ - type: 'real', - }); - - (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({ - getData, - cancel: cancelMock, - isPending: () => true, - inspect: () => {}, - })); - - (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({ - getData, - cancel: cancelMock, - isPending: () => true, - inspect: () => {}, - })); - - const expressionLoader = new ExpressionLoader(element, expressionString, {}); - - expect.assertions(2); - expressionLoader.data$.subscribe({ - next(data) { - expect(data).toEqual({ - type: 'real', - }); - }, - error() { - expect(false).toEqual('Should not be called'); - }, - }); - - expressionLoader.update('new expression', {}); - - expect(getData).toHaveBeenCalledTimes(2); - }); - - it('sends an observable error if the data fetching failed', () => { - const cancelMock = jest.fn(); - - const getData = jest.fn().mockResolvedValue('rejected'); - - (ExpressionDataHandler as jest.Mock).mockImplementationOnce(() => ({ - getData, - cancel: cancelMock, - isPending: () => true, - inspect: () => {}, - })); - - const expressionLoader = new ExpressionLoader(element, expressionString, {}); - - expect.assertions(2); - expressionLoader.data$.subscribe({ - next(data) { - expect(data).toEqual('Should not be called'); - }, - error(error) { - expect(error.message).toEqual('Could not fetch data'); - }, - }); + const expressionLoader = new ExpressionLoader(element, 'var foo', {}); + const execution = __getLastExecution(); + jest.spyOn(execution, 'cancel'); - expect(getData).toHaveBeenCalledTimes(1); + expect(execution.cancel).toHaveBeenCalledTimes(0); + expressionLoader.update('var bar', {}); + expect(execution.cancel).toHaveBeenCalledTimes(1); }); it('inspect() returns correct inspector adapters', () => { diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts index 320a8469fe9e3..4600922e076fa 100644 --- a/src/plugins/expressions/public/loader.ts +++ b/src/plugins/expressions/public/loader.ts @@ -20,11 +20,11 @@ import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { Adapters, InspectorSession } from '../../inspector/public'; -import { ExpressionDataHandler } from './execute'; import { ExpressionRenderHandler } from './render'; import { IExpressionLoaderParams } from './types'; import { ExpressionAstExpression } from '../common'; -import { getInspector } from './services'; +import { getInspector, getExpressionsService } from './services'; +import { ExecutionContract } from '../common/execution/execution_contract'; type Data = any; @@ -35,7 +35,7 @@ export class ExpressionLoader { events$: ExpressionRenderHandler['events$']; loading$: Observable; - private dataHandler: ExpressionDataHandler | undefined; + private execution: ExecutionContract | undefined; private renderHandler: ExpressionRenderHandler; private dataSubject: Subject; private loadingSubject: Subject; @@ -93,26 +93,26 @@ export class ExpressionLoader { this.dataSubject.complete(); this.loadingSubject.complete(); this.renderHandler.destroy(); - if (this.dataHandler) { - this.dataHandler.cancel(); + if (this.execution) { + this.execution.cancel(); } } cancel() { - if (this.dataHandler) { - this.dataHandler.cancel(); + if (this.execution) { + this.execution.cancel(); } } getExpression(): string | undefined { - if (this.dataHandler) { - return this.dataHandler.getExpression(); + if (this.execution) { + return this.execution.getExpression(); } } getAst(): ExpressionAstExpression | undefined { - if (this.dataHandler) { - return this.dataHandler.getAst(); + if (this.execution) { + return this.execution.getAst(); } } @@ -130,9 +130,7 @@ export class ExpressionLoader { } inspect(): Adapters | undefined { - if (this.dataHandler) { - return this.dataHandler.inspect(); - } + return this.execution ? (this.execution.inspect() as Adapters) : undefined; } update(expression?: string | ExpressionAstExpression, params?: IExpressionLoaderParams): void { @@ -150,15 +148,19 @@ export class ExpressionLoader { expression: string | ExpressionAstExpression, params: IExpressionLoaderParams ): Promise => { - if (this.dataHandler && this.dataHandler.isPending) { - this.dataHandler.cancel(); + if (this.execution && this.execution.isPending) { + this.execution.cancel(); } this.setParams(params); - this.dataHandler = new ExpressionDataHandler(expression, params); - if (!params.inspectorAdapters) params.inspectorAdapters = this.dataHandler.inspect(); - const prevDataHandler = this.dataHandler; + this.execution = getExpressionsService().execute(expression, params.context, { + search: params.searchContext, + variables: params.variables || {}, + inspectorAdapters: params.inspectorAdapters, + }); + if (!params.inspectorAdapters) params.inspectorAdapters = this.execution.inspect() as Adapters; + const prevDataHandler = this.execution; const data = await prevDataHandler.getData(); - if (this.dataHandler !== prevDataHandler) { + if (this.execution !== prevDataHandler) { return; } this.dataSubject.next(data); diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx index 70760ada83955..40ae698bc95eb 100644 --- a/src/plugins/expressions/public/mocks.tsx +++ b/src/plugins/expressions/public/mocks.tsx @@ -65,7 +65,6 @@ const createSetupContract = (): Setup => { const createStartContract = (): Start => { return { execute: jest.fn(), - ExpressionDataHandler: jest.fn(), ExpressionLoader: jest.fn(), ExpressionRenderHandler: jest.fn(), getFunction: jest.fn(), diff --git a/src/plugins/expressions/public/plugin.test.ts b/src/plugins/expressions/public/plugin.test.ts index 5437a7d21f338..fdfd583eac9de 100644 --- a/src/plugins/expressions/public/plugin.test.ts +++ b/src/plugins/expressions/public/plugin.test.ts @@ -65,6 +65,15 @@ describe('ExpressionsPublicPlugin', () => { } `); }); + + test('"kibana" function return value of type "kibana_context"', async () => { + const { doStart } = await expressionsPluginMock.createPlugin(); + const start = await doStart(); + const execution = start.execute('kibana'); + const result = await execution.getData(); + + expect((result as any).type).toBe('kibana_context'); + }); }); }); }); diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts index 6799b1590f252..aac429b365c48 100644 --- a/src/plugins/expressions/public/plugin.ts +++ b/src/plugins/expressions/public/plugin.ts @@ -36,11 +36,11 @@ import { setInterpreter, setRenderersRegistry, setNotifications, + setExpressionsService, } from './services'; import { kibanaContext as kibanaContextFunction } from './expression_functions/kibana_context'; import { ReactExpressionRenderer } from './react_expression_renderer'; import { ExpressionLoader, loader } from './loader'; -import { ExpressionDataHandler, execute } from './execute'; import { render, ExpressionRenderHandler } from './render'; export interface ExpressionsSetupDeps { @@ -92,8 +92,6 @@ export interface ExpressionsSetup extends ExpressionsServiceSetup { } export interface ExpressionsStart extends ExpressionsServiceStart { - execute: typeof execute; - ExpressionDataHandler: typeof ExpressionDataHandler; ExpressionLoader: typeof ExpressionLoader; ExpressionRenderHandler: typeof ExpressionRenderHandler; loader: typeof loader; @@ -118,6 +116,7 @@ export class ExpressionsPublicPlugin executor.registerFunction(kibanaContextFunction()); setRenderersRegistry(renderers); + setExpressionsService(this.expressions); const expressionsSetup = expressions.setup(); @@ -180,8 +179,6 @@ export class ExpressionsPublicPlugin return { ...expressionsStart, - execute, - ExpressionDataHandler, ExpressionLoader, ExpressionRenderHandler, loader, diff --git a/src/plugins/expressions/public/services.ts b/src/plugins/expressions/public/services.ts index 75ec4826ea45a..4fdff9b151ac2 100644 --- a/src/plugins/expressions/public/services.ts +++ b/src/plugins/expressions/public/services.ts @@ -22,6 +22,7 @@ import { createKibanaUtilsCore, createGetterSetter } from '../../kibana_utils/pu import { ExpressionInterpreter } from './types'; import { Start as IInspector } from '../../inspector/public'; import { ExpressionsSetup } from './plugin'; +import { ExpressionsService } from '../common'; export const { getCoreStart, setCoreStart, savedObjects } = createKibanaUtilsCore(); @@ -37,3 +38,7 @@ export const [getNotifications, setNotifications] = createGetterSetter('Renderers registry'); + +export const [getExpressionsService, setExpressionsService] = createGetterSetter< + ExpressionsService +>('ExpressionsService'); diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx index 41e466fddd11e..a50248a5b6fa3 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/app/components/main.tsx @@ -22,7 +22,7 @@ import { EuiPage, EuiPageBody, EuiPageContent, EuiPageContentHeader } from '@ela import { first } from 'rxjs/operators'; import { IInterpreterRenderHandlers, ExpressionValue } from 'src/plugins/expressions'; import { RequestAdapter, DataAdapter } from '../../../../../../../../src/plugins/inspector'; -import { Adapters, ExpressionRenderHandler, ExpressionDataHandler } from '../../types'; +import { Adapters, ExpressionRenderHandler } from '../../types'; import { getExpressions } from '../../services'; declare global { @@ -31,7 +31,7 @@ declare global { expressions: string, context?: ExpressionValue, initialContext?: ExpressionValue - ) => ReturnType; + ) => any; renderPipelineResponse: (context?: ExpressionValue) => Promise; } } @@ -61,12 +61,9 @@ class Main extends React.Component<{}, State> { data: new DataAdapter(), }; return getExpressions() - .execute(expression, { + .execute(expression, context || { type: 'null' }, { inspectorAdapters: adapters, - context, - // TODO: naming / typing is confusing and doesn't match here - // searchContext is also a way to set initialContext and Context can't be set to SearchContext - searchContext: initialContext as any, + search: initialContext as any, }) .getData(); }; diff --git a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts index 6e0a93e4a3cb1..123baa1183c48 100644 --- a/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts +++ b/test/interpreter_functional/plugins/kbn_tp_run_pipeline/public/np_ready/types.ts @@ -17,12 +17,7 @@ * under the License. */ -import { - ExpressionsStart, - ExpressionRenderHandler, - ExpressionDataHandler, -} from 'src/plugins/expressions/public'; - +import { ExpressionsStart, ExpressionRenderHandler } from 'src/plugins/expressions/public'; import { Adapters } from 'src/plugins/inspector/public'; -export { ExpressionsStart, ExpressionRenderHandler, ExpressionDataHandler, Adapters }; +export { ExpressionsStart, ExpressionRenderHandler, Adapters }; diff --git a/test/interpreter_functional/test_suites/run_pipeline/basic.ts b/test/interpreter_functional/test_suites/run_pipeline/basic.ts index 77853b0bcd6a4..a2172dd2da1ba 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/basic.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/basic.ts @@ -22,7 +22,7 @@ import { ExpectExpression, expectExpressionProvider } from './helpers'; import { FtrProviderContext } from '../../../functional/ftr_provider_context'; // this file showcases how to use testing utilities defined in helpers.ts together with the kbn_tp_run_pipeline -// test plugin to write autmated tests for interprete +// test plugin to write automated tests for interpreter export default function({ getService, updateBaselines, diff --git a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts index 015c311c30aef..00693845bb266 100644 --- a/test/interpreter_functional/test_suites/run_pipeline/helpers.ts +++ b/test/interpreter_functional/test_suites/run_pipeline/helpers.ts @@ -20,10 +20,8 @@ import expect from '@kbn/expect'; import { ExpressionValue } from 'src/plugins/expressions'; import { FtrProviderContext } from '../../../functional/ftr_provider_context'; -import { ExpressionDataHandler } from '../../plugins/kbn_tp_run_pipeline/public/np_ready/types'; -type UnWrapPromise = T extends Promise ? U : T; -export type ExpressionResult = UnWrapPromise>; +export type ExpressionResult = any; export type ExpectExpression = ( name: string, @@ -112,7 +110,7 @@ export function expectExpressionProvider({ if (!_currentContext.type) _currentContext.type = 'null'; return window .runPipeline(_expression, _currentContext, _initialContext) - .then(expressionResult => { + .then((expressionResult: any) => { done(expressionResult); return expressionResult; }); From d21d33d0b68bea3c45e49491fd4ee5671a00b3c7 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Mon, 17 Feb 2020 15:25:17 +0100 Subject: [PATCH 33/65] =?UTF-8?q?fix:=20=F0=9F=90=9B=20don't=20cast=20to?= =?UTF-8?q?=20any=20(#57799)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EUI 19.0 was merged into Kibana which supports React elements as context menu items, so we don't need to cast to any here anymore. --- .../public/context_menu/build_eui_context_menu_panels.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx index 7b80a8ea830c0..3dce2c1f4c257 100644 --- a/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx +++ b/src/plugins/ui_actions/public/context_menu/build_eui_context_menu_panels.tsx @@ -101,10 +101,9 @@ function convertPanelActionToContextMenuItem({ }): EuiContextMenuPanelItemDescriptor { const menuPanelItem: EuiContextMenuPanelItemDescriptor = { name: action.MenuItem - ? // Cast to `any` because `name` typed to string. - (React.createElement(uiToReactComponent(action.MenuItem), { + ? React.createElement(uiToReactComponent(action.MenuItem), { context: actionContext, - }) as any) + }) : action.getDisplayName(actionContext), icon: action.getIconType(actionContext), panel: _.get(action, 'childContextMenuPanel.id'), From c31c7ee4c7b20eee566fd3d915620a0ce86416d1 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 17 Feb 2020 15:06:42 +0000 Subject: [PATCH 34/65] [ML] Adding get buckets endpoint wrapper (#57752) * [ML] Adding results buckets endpoint wrapper * removing unnecessary spread operator * correcting timstamp format in api doc Co-authored-by: Elastic Machine --- .../ml/server/client/elasticsearch_ml.js | 25 ++++++ .../ml/server/routes/anomaly_detectors.ts | 76 +++++++++++++++---- .../ml/server/routes/data_frame_analytics.ts | 20 ++--- 3 files changed, 98 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js b/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js index cf13d329182ba..9317d3c6c3e07 100644 --- a/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js +++ b/x-pack/legacy/plugins/ml/server/client/elasticsearch_ml.js @@ -450,6 +450,31 @@ export const elasticsearchJsPlugin = (Client, config, components) => { method: 'POST', }); + ml.buckets = ca({ + urls: [ + { + fmt: '/_ml/anomaly_detectors/<%=jobId%>/results/buckets', + req: { + jobId: { + type: 'string', + }, + }, + }, + { + fmt: '/_ml/anomaly_detectors/<%=jobId%>/results/buckets/<%=timestamp%>', + req: { + jobId: { + type: 'string', + }, + timestamp: { + type: 'string', + }, + }, + }, + ], + method: 'POST', + }); + ml.overallBuckets = ca({ url: { fmt: '/_ml/anomaly_detectors/<%=jobId%>/results/overall_buckets', diff --git a/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts b/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts index 64af1f67bce29..927646e4f0acc 100644 --- a/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts +++ b/x-pack/legacy/plugins/ml/server/routes/anomaly_detectors.ts @@ -36,7 +36,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { try { const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobs'); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -67,7 +67,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { const { jobId } = request.params; const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobs', { jobId }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -94,7 +94,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { try { const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobStats'); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -125,7 +125,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { const { jobId } = request.params; const results = await context.ml!.mlClient.callAsCurrentUser('ml.jobStats', { jobId }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -160,7 +160,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { body: request.body, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -195,7 +195,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { body: request.body, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -228,7 +228,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { jobId, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -265,7 +265,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { } const results = await context.ml!.mlClient.callAsCurrentUser('ml.closeJob', options); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -302,7 +302,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { } const results = await context.ml!.mlClient.callAsCurrentUser('ml.deleteJob', options); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -332,7 +332,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { body: request.body, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -368,7 +368,57 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { duration, }); return response.ok({ - body: { ...results }, + body: results, + }); + } catch (e) { + return response.customError(wrapError(e)); + } + }) + ); + + /** + * @apiGroup AnomalyDetectors + * + * @api {post} /api/ml/anomaly_detectors/:jobId/results/buckets Obtain bucket scores for the specified job ID + * @apiName GetOverallBuckets + * @apiDescription The get buckets API presents a chronological view of the records, grouped by bucket. + * + * @apiParam {String} jobId Job ID. + * @apiParam {String} timestamp. + * + * @apiSuccess {Number} count + * @apiSuccess {Object[]} buckets + */ + router.post( + { + path: '/api/ml/anomaly_detectors/{jobId}/results/buckets/{timestamp?}', + validate: { + params: schema.object({ + jobId: schema.string(), + timestamp: schema.maybe(schema.string()), + }), + body: schema.object({ + anomaly_score: schema.maybe(schema.number()), + desc: schema.maybe(schema.boolean()), + end: schema.maybe(schema.string()), + exclude_interim: schema.maybe(schema.boolean()), + expand: schema.maybe(schema.boolean()), + 'page.from': schema.maybe(schema.number()), + 'page.size': schema.maybe(schema.number()), + sort: schema.maybe(schema.string()), + start: schema.maybe(schema.string()), + }), + }, + }, + licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => { + try { + const results = await context.ml!.mlClient.callAsCurrentUser('ml.buckets', { + jobId: request.params.jobId, + timestamp: request.params.timestamp, + ...request.body, + }); + return response.ok({ + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -413,7 +463,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { end: request.body.end, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -449,7 +499,7 @@ export function jobRoutes({ xpackMainPlugin, router }: RouteInitialization) { }; const results = await context.ml!.mlClient.callAsCurrentUser('ml.categories', options); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); diff --git a/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts b/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts index f134820adbb48..6541fa541a59f 100644 --- a/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts +++ b/x-pack/legacy/plugins/ml/server/routes/data_frame_analytics.ts @@ -40,7 +40,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti try { const results = await context.ml!.mlClient.callAsCurrentUser('ml.getDataFrameAnalytics'); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -71,7 +71,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti analyticsId, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -97,7 +97,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti 'ml.getDataFrameAnalyticsStats' ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -131,7 +131,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti } ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -170,7 +170,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti } ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -201,7 +201,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti } ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -241,7 +241,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti } ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -277,7 +277,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti } ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -310,7 +310,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti analyticsId, }); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); @@ -353,7 +353,7 @@ export function dataFrameAnalyticsRoutes({ xpackMainPlugin, router }: RouteIniti options ); return response.ok({ - body: { ...results }, + body: results, }); } catch (e) { return response.customError(wrapError(e)); From 3c51fb0314799365bfe641adc7423d34e81f0d3c Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 18 Feb 2020 07:16:40 +0000 Subject: [PATCH 35/65] [ML] File data viz fix index pattern warning after index change (#57807) --- .../file_based/components/import_view/import_view.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js index bb95d3e420d2a..beb5918e277ae 100644 --- a/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js +++ b/x-pack/legacy/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js @@ -325,9 +325,17 @@ export class ImportView extends Component { onIndexChange = e => { const name = e.target.value; + const { indexNames, indexPattern, indexPatternNames } = this.state; + this.setState({ index: name, - indexNameError: isIndexNameValid(name, this.state.indexNames), + indexNameError: isIndexNameValid(name, indexNames), + // if index pattern has been altered, check that it still matches the inputted index + ...(indexPattern === '' + ? {} + : { + indexPatternNameError: isIndexPatternNameValid(indexPattern, indexPatternNames, name), + }), }); }; From 446fda62f20af07d15f9a39cceebeac9f406ffcd Mon Sep 17 00:00:00 2001 From: Victor Martinez Date: Tue, 18 Feb 2020 08:25:14 +0000 Subject: [PATCH 36/65] [jenkins] Notify GH Checks for the apm-ui e2e pipeline (#52900) --- .ci/end2end.groovy | 27 +++++++++++++++++-- .../plugins/apm/cypress/ci/kibana.dev.yml | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.ci/end2end.groovy b/.ci/end2end.groovy index 5cf6efe324ac3..38fed4aca19dc 100644 --- a/.ci/end2end.groovy +++ b/.ci/end2end.groovy @@ -25,7 +25,7 @@ pipeline { durabilityHint('PERFORMANCE_OPTIMIZED') } triggers { - issueCommentTrigger('(?i).*jenkins\\W+run\\W+(?:the\\W+)?e2e(?:\\W+please)?.*') + issueCommentTrigger('(?i)(retest|.*jenkins\\W+run\\W+(?:the\\W+)?e2e?.*)') } parameters { booleanParam(name: 'FORCE', defaultValue: false, description: 'Whether to force the run.') @@ -60,8 +60,14 @@ pipeline { } } steps { + notifyStatus('Starting services', 'PENDING') dir("${APM_ITS}"){ - sh './scripts/compose.py start master --no-kibana --no-xpack-secure' + sh './scripts/compose.py start master --no-kibana' + } + } + post { + unsuccessful { + notifyStatus('Environmental issue', 'FAILURE') } } } @@ -77,10 +83,16 @@ pipeline { JENKINS_NODE_COOKIE = 'dontKillMe' } steps { + notifyStatus('Preparing kibana', 'PENDING') dir("${BASE_DIR}"){ sh script: "${CYPRESS_DIR}/ci/prepare-kibana.sh" } } + post { + unsuccessful { + notifyStatus('Kibana warm up failed', 'FAILURE') + } + } } stage('Smoke Tests'){ options { skipDefaultCheckout() } @@ -91,6 +103,7 @@ pipeline { } } steps{ + notifyStatus('Running smoke tests', 'PENDING') dir("${BASE_DIR}"){ sh ''' jobs -l @@ -112,6 +125,12 @@ pipeline { archiveArtifacts(allowEmptyArchive: false, artifacts: 'apm-its.log') } } + unsuccessful { + notifyStatus('Test failures', 'FAILURE') + } + success { + notifyStatus('Tests passed', 'SUCCESS') + } } } } @@ -123,3 +142,7 @@ pipeline { } } } + +def notifyStatus(String description, String status) { + withGithubNotify.notify('end2end-for-apm-ui', description, status, getBlueoceanDisplayURL()) +} diff --git a/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml b/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml index 3082391f23a15..db57db9a1abe9 100644 --- a/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml +++ b/x-pack/legacy/plugins/apm/cypress/ci/kibana.dev.yml @@ -2,3 +2,6 @@ # Disabled plugins ######################## logging.verbose: true +elasticsearch.username: "kibana_system_user" +elasticsearch.password: "changeme" +xpack.security.encryptionKey: "something_at_least_32_characters" From 66e685b4a80e671e0ce840bc7e98b1c944ecdd61 Mon Sep 17 00:00:00 2001 From: MadameSheema Date: Tue, 18 Feb 2020 10:03:43 +0100 Subject: [PATCH 37/65] refactors 'flyout-button' tests (#57572) Co-authored-by: Elastic Machine --- .../fields_browser/fields_browser.spec.ts | 8 ++--- .../smoke_tests/inspect/inspect.spec.ts | 2 +- .../timeline/data_providers.spec.ts | 3 +- .../timeline/flyout_button.spec.ts | 30 +++++++------------ .../plugins/siem/cypress/screens/siem_main.ts | 9 ++++++ .../siem/cypress/screens/timeline/main.ts | 5 ++++ .../plugins/siem/cypress/tasks/siem_main.ts | 20 +++++++++++++ .../siem/cypress/tasks/timeline/main.ts | 5 ---- 8 files changed, 50 insertions(+), 32 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts create mode 100644 x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts index 2889d78891a06..6e8ef93a54016 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/fields_browser/fields_browser.spec.ts @@ -22,11 +22,9 @@ import { FIELDS_BROWSER_HEADER_HOST_GEO_CONTINENT_NAME_HEADER, } from '../../../screens/timeline/fields_browser'; -import { - openTimeline, - populateTimeline, - openTimelineFieldsBrowser, -} from '../../../tasks/timeline/main'; +import { populateTimeline, openTimelineFieldsBrowser } from '../../../tasks/timeline/main'; + +import { openTimeline } from '../../../tasks/siem_main'; import { clearFieldsBrowser, diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts index e7411aba11af5..1555470f5eee7 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/inspect/inspect.spec.ts @@ -12,10 +12,10 @@ import { } from '../../../screens/inspect'; import { executeTimelineKQL, - openTimeline, openTimelineSettings, openTimelineInspectButton, } from '../../../tasks/timeline/main'; +import { openTimeline } from '../../../tasks/siem_main'; import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login'; import { closesModal, openStatsAndTables } from '../../../tasks/inspect'; diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts index 3d251c1c6bcac..c3fedfb06939b 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/data_providers.spec.ts @@ -13,7 +13,8 @@ import { } from '../../../tasks/hosts/all_hosts'; import { HOSTS_NAMES } from '../../../screens/hosts/all_hosts'; import { DEFAULT_TIMEOUT, loginAndWaitForPage } from '../../../tasks/login'; -import { openTimeline, createNewTimeline } from '../../../tasks/timeline/main'; +import { createNewTimeline } from '../../../tasks/timeline/main'; +import { openTimeline } from '../../../tasks/siem_main'; import { TIMELINE_DATA_PROVIDERS_EMPTY, TIMELINE_DATA_PROVIDERS, diff --git a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts index 63fe56371a4cd..b7faaaac1c06c 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/smoke_tests/timeline/flyout_button.spec.ts @@ -4,44 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ +import { HOSTS_PAGE } from '../../../urls/navigation'; +import { waitForAllHostsToBeLoaded, dragFirstHostToTimeline } from '../../../tasks/hosts/all_hosts'; +import { loginAndWaitForPage } from '../../../tasks/login'; +import { openTimelineIfClosed, openTimeline } from '../../../tasks/siem_main'; import { TIMELINE_FLYOUT_BODY, TIMELINE_NOT_READY_TO_DROP_BUTTON, -} from '../../lib/timeline/selectors'; -import { ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS } from '../../lib/hosts/selectors'; -import { HOSTS_PAGE } from '../../lib/urls'; -import { waitForAllHostsWidget } from '../../lib/hosts/helpers'; -import { loginAndWaitForPage } from '../../lib/util/helpers'; -import { drag } from '../../lib/drag_n_drop/helpers'; -import { createNewTimeline, toggleTimelineVisibility } from '../../lib/timeline/helpers'; +} from '../../../screens/timeline/main'; +import { createNewTimeline } from '../../../tasks/timeline/main'; describe('timeline flyout button', () => { before(() => { loginAndWaitForPage(HOSTS_PAGE); + waitForAllHostsToBeLoaded(); }); afterEach(() => { - cy.get('[data-test-subj="kibanaChrome"]').then($page => { - if ($page.find('[data-test-subj="flyoutOverlay"]').length === 1) { - toggleTimelineVisibility(); - } - }); - + openTimelineIfClosed(); createNewTimeline(); }); it('toggles open the timeline', () => { - toggleTimelineVisibility(); - + openTimeline(); cy.get(TIMELINE_FLYOUT_BODY).should('have.css', 'visibility', 'visible'); }); it('sets the flyout button background to euiColorSuccess with a 10% alpha channel when the user starts dragging a host, but is not hovering over the flyout button', () => { - waitForAllHostsWidget(); - - cy.get(ALL_HOSTS_WIDGET_DRAGGABLE_HOSTS) - .first() - .then(host => drag(host)); + dragFirstHostToTimeline(); cy.get(TIMELINE_NOT_READY_TO_DROP_BUTTON).should( 'have.css', diff --git a/x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts b/x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts new file mode 100644 index 0000000000000..d4eeeb036ee95 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/screens/siem_main.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +export const MAIN_PAGE = '[data-test-subj="kibanaChrome"]'; + +export const TIMELINE_TOGGLE_BUTTON = '[data-test-subj="flyoutOverlay"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts index 60c9c2ab44372..4c722ffa5f215 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline/main.ts @@ -32,3 +32,8 @@ export const TIMELINE_DATA_PROVIDERS_EMPTY = export const TIMELINE_DROPPED_DATA_PROVIDERS = '[data-test-subj="dataProviders"] [data-test-subj="providerContainer"]'; + +export const TIMELINE_FLYOUT_BODY = '[data-test-subj="eui-flyout-body"]'; + +export const TIMELINE_NOT_READY_TO_DROP_BUTTON = + '[data-test-subj="flyout-button-not-ready-to-drop"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts new file mode 100644 index 0000000000000..8501bb3d94e26 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/tasks/siem_main.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MAIN_PAGE, TIMELINE_TOGGLE_BUTTON } from '../screens/siem_main'; +import { DEFAULT_TIMEOUT } from '../tasks/login'; + +export const openTimelineIfClosed = () => { + cy.get(MAIN_PAGE).then($page => { + if ($page.find(TIMELINE_TOGGLE_BUTTON).length === 1) { + openTimeline(); + } + }); +}; + +export const openTimeline = () => { + cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click(); +}; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts index 068b6dd9f8bd4..f347c072a3584 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/timeline/main.ts @@ -7,7 +7,6 @@ import { DEFAULT_TIMEOUT } from '../../integration/lib/util/helpers'; import { - TIMELINE_TOGGLE_BUTTON, SEARCH_OR_FILTER_CONTAINER, TIMELINE_FIELDS_BUTTON, SERVER_SIDE_EVENT_COUNT, @@ -19,10 +18,6 @@ import { export const hostExistsQuery = 'host.name: *'; -export const openTimeline = () => { - cy.get(TIMELINE_TOGGLE_BUTTON, { timeout: DEFAULT_TIMEOUT }).click(); -}; - export const populateTimeline = () => { cy.get(`${SEARCH_OR_FILTER_CONTAINER} input`).type(`${hostExistsQuery} {enter}`); cy.get(SERVER_SIDE_EVENT_COUNT, { timeout: DEFAULT_TIMEOUT }) From 5b7734cd4d97b03b487b257c3c68ad5169b76b2f Mon Sep 17 00:00:00 2001 From: Maryia Lapata Date: Tue, 18 Feb 2020 12:54:30 +0300 Subject: [PATCH 38/65] Apply sub url tracking utils to visualize and discover (#57307) * Apply sub url tracking utils to visualize and discover * Update query karma mock * Remove unnecessary chrome legacy calls * Fix typo * Add setActiveUrl * Add unit test for setActiveUrl * Refactoring Co-authored-by: Elastic Machine --- src/legacy/core_plugins/kibana/index.js | 2 + .../kibana/public/discover/plugin.ts | 54 +++++++++++++- .../public/visualize/kibana_services.ts | 3 +- .../kibana/public/visualize/legacy.ts | 9 +-- .../kibana/public/visualize/legacy_imports.ts | 7 -- .../public/visualize/np_ready/application.ts | 3 +- .../visualize/np_ready/editor/editor.js | 7 +- .../np_ready/listing/visualize_listing.js | 17 ++--- .../kibana/public/visualize/plugin.ts | 70 +++++++++++++++---- .../new_platform/new_platform.karma_mock.js | 50 +++++++++++-- .../url/kbn_url_tracker.test.ts | 6 ++ .../state_management/url/kbn_url_tracker.ts | 26 ++++--- 12 files changed, 189 insertions(+), 65 deletions(-) diff --git a/src/legacy/core_plugins/kibana/index.js b/src/legacy/core_plugins/kibana/index.js index ea81193c1dd0a..8e6bae0b588bc 100644 --- a/src/legacy/core_plugins/kibana/index.js +++ b/src/legacy/core_plugins/kibana/index.js @@ -77,6 +77,7 @@ export default function(kibana) { order: -1003, url: `${kbnBaseUrl}#/discover`, euiIconType: 'discoverApp', + disableSubUrlTracking: true, category: DEFAULT_APP_CATEGORIES.analyze, }, { @@ -87,6 +88,7 @@ export default function(kibana) { order: -1002, url: `${kbnBaseUrl}#/visualize`, euiIconType: 'visualizeApp', + disableSubUrlTracking: true, category: DEFAULT_APP_CATEGORIES.analyze, }, { diff --git a/src/legacy/core_plugins/kibana/public/discover/plugin.ts b/src/legacy/core_plugins/kibana/public/discover/plugin.ts index 565382313e369..e8ded9d99f892 100644 --- a/src/legacy/core_plugins/kibana/public/discover/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/discover/plugin.ts @@ -16,11 +16,17 @@ * specific language governing permissions and limitations * under the License. */ + +import { BehaviorSubject } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { AppMountParameters, CoreSetup, CoreStart, Plugin } from 'kibana/public'; import angular, { auto } from 'angular'; import { UiActionsSetup, UiActionsStart } from 'src/plugins/ui_actions/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { + DataPublicPluginStart, + DataPublicPluginSetup, + getQueryStateContainer, +} from '../../../../../plugins/data/public'; import { registerFeature } from './np_ready/register_feature'; import './kibana_services'; import { IEmbeddableStart, IEmbeddableSetup } from '../../../../../plugins/embeddable/public'; @@ -30,7 +36,10 @@ import { NavigationPublicPluginStart as NavigationStart } from '../../../../../p import { ChartsPluginStart } from '../../../../../plugins/charts/public'; import { buildServices } from './build_services'; import { SharePluginStart } from '../../../../../plugins/share/public'; -import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; +import { + KibanaLegacySetup, + AngularRenderedAppUpdater, +} from '../../../../../plugins/kibana_legacy/public'; import { DocViewsRegistry } from './np_ready/doc_views/doc_views_registry'; import { DocViewInput, DocViewInputFn } from './np_ready/doc_views/doc_views_types'; import { DocViewTable } from './np_ready/components/table/table'; @@ -40,6 +49,7 @@ import { VisualizationsStart, VisualizationsSetup, } from '../../../visualizations/public/np_ready/public'; +import { createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public'; /** * These are the interfaces with your public contracts. You should export these @@ -56,6 +66,7 @@ export interface DiscoverSetupPlugins { kibanaLegacy: KibanaLegacySetup; home: HomePublicPluginSetup; visualizations: VisualizationsSetup; + data: DataPublicPluginSetup; } export interface DiscoverStartPlugins { uiActions: UiActionsStart; @@ -81,6 +92,9 @@ export class DiscoverPlugin implements Plugin { private docViewsRegistry: DocViewsRegistry | null = null; private embeddableInjector: auto.IInjectorService | null = null; private getEmbeddableInjector: (() => Promise) | null = null; + private appStateUpdater = new BehaviorSubject(() => ({})); + private stopUrlTracking: (() => void) | undefined = undefined; + /** * why are those functions public? they are needed for some mocha tests * can be removed once all is Jest @@ -89,6 +103,27 @@ export class DiscoverPlugin implements Plugin { public initializeServices?: () => Promise<{ core: CoreStart; plugins: DiscoverStartPlugins }>; setup(core: CoreSetup, plugins: DiscoverSetupPlugins): DiscoverSetup { + const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer( + plugins.data.query + ); + const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({ + baseUrl: core.http.basePath.prepend('/app/kibana'), + defaultSubUrl: '#/discover', + storageKey: 'lastUrl:discover', + navLinkUpdater$: this.appStateUpdater, + toastNotifications: core.notifications.toasts, + stateParams: [ + { + kbnUrlKey: '_g', + stateUpdate$: querySyncStateContainer.state$, + }, + ], + }); + this.stopUrlTracking = () => { + stopQuerySyncStateContainer(); + stopUrlTracker(); + }; + this.getEmbeddableInjector = this.getInjector.bind(this); this.docViewsRegistry = new DocViewsRegistry(this.getEmbeddableInjector); this.docViewsRegistry.addDocView({ @@ -108,6 +143,8 @@ export class DiscoverPlugin implements Plugin { plugins.kibanaLegacy.registerLegacyApp({ id: 'discover', title: 'Discover', + updater$: this.appStateUpdater.asObservable(), + navLinkId: 'kibana:discover', order: -1004, euiIconType: 'discoverApp', mount: async (params: AppMountParameters) => { @@ -117,11 +154,16 @@ export class DiscoverPlugin implements Plugin { if (!this.initializeInnerAngular) { throw Error('Discover plugin method initializeInnerAngular is undefined'); } + appMounted(); await this.initializeServices(); await this.initializeInnerAngular(); const { renderApp } = await import('./np_ready/application'); - return renderApp(innerAngularName, params.element); + const unmount = await renderApp(innerAngularName, params.element); + return () => { + unmount(); + appUnMounted(); + }; }, }); registerFeature(plugins.home); @@ -160,6 +202,12 @@ export class DiscoverPlugin implements Plugin { this.registerEmbeddable(core, plugins); } + stop() { + if (this.stopUrlTracking) { + this.stopUrlTracking(); + } + } + /** * register embeddable with a slimmer embeddable version of inner angular */ diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index 6082fb8428ac3..096877d5824c4 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -35,7 +35,6 @@ import { DataPublicPluginStart, IndexPatternsContract } from '../../../../../plu import { VisualizationsStart } from '../../../visualizations/public'; import { SavedVisualizations } from './np_ready/types'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; -import { Chrome } from './legacy_imports'; import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public'; export interface VisualizeKibanaServices { @@ -47,7 +46,6 @@ export interface VisualizeKibanaServices { embeddable: IEmbeddableStart; getBasePath: () => string; indexPatterns: IndexPatternsContract; - legacyChrome: Chrome; localStorage: Storage; navigation: NavigationStart; toastNotifications: ToastsStart; @@ -61,6 +59,7 @@ export interface VisualizeKibanaServices { visualizations: VisualizationsStart; usageCollection?: UsageCollectionSetup; I18nContext: I18nStart['Context']; + setActiveUrl: (newUrl: string) => void; } let services: VisualizeKibanaServices | null = null; diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts index bc2d700f6c6a1..fbbc7ab944daf 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy.ts @@ -18,19 +18,14 @@ */ import { PluginInitializerContext } from 'kibana/public'; -import { legacyChrome, npSetup, npStart } from './legacy_imports'; +import { npSetup, npStart } from 'ui/new_platform'; import { start as visualizations } from '../../../visualizations/public/np_ready/public/legacy'; import { plugin } from './index'; const instance = plugin({ env: npSetup.plugins.kibanaLegacy.env, } as PluginInitializerContext); -instance.setup(npSetup.core, { - ...npSetup.plugins, - __LEGACY: { - legacyChrome, - }, -}); +instance.setup(npSetup.core, npSetup.plugins); instance.start(npStart.core, { ...npStart.plugins, visualizations, diff --git a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts index ac9fc227406ff..92433799ba420 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/legacy_imports.ts @@ -24,11 +24,6 @@ * directly where they are needed. */ -import chrome from 'ui/chrome'; - -export const legacyChrome = chrome; -export { Chrome } from 'ui/chrome'; - // @ts-ignore export { AppState, AppStateProvider } from 'ui/state_management/app_state'; export { State } from 'ui/state_management/state'; @@ -39,8 +34,6 @@ export { StateManagementConfigProvider } from 'ui/state_management/config_provid export { stateMonitorFactory } from 'ui/state_management/state_monitor_factory'; export { PersistedState } from 'ui/persisted_state'; -export { npSetup, npStart } from 'ui/new_platform'; - export { subscribeWithScope } from 'ui/utils/subscribe_with_scope'; // @ts-ignore export { EventsProvider } from 'ui/events'; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts index 3d5fd6605f56b..bd7b478f827a6 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/application.ts @@ -45,7 +45,7 @@ import { VisualizeKibanaServices } from '../kibana_services'; let angularModuleInstance: IModule | null = null; -export const renderApp = async ( +export const renderApp = ( element: HTMLElement, appBasePath: string, deps: VisualizeKibanaServices @@ -58,7 +58,6 @@ export const renderApp = async ( { core: deps.core, env: deps.pluginInitializerContext.env }, true ); - // custom routing stuff initVisualizeApp(angularModuleInstance, deps); } const $injector = mountVisualizeApp(appBasePath, element); diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js index 657104344662f..409d4b41fbe69 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/editor/editor.js @@ -90,13 +90,13 @@ function VisualizeAppController( }, }, toastNotifications, - legacyChrome, chrome, getBasePath, core: { docLinks }, savedQueryService, uiSettings, I18nContext, + setActiveUrl, } = getServices(); const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager); @@ -580,10 +580,7 @@ function VisualizeAppController( }); // Manually insert a new url so the back button will open the saved visualization. $window.history.pushState({}, '', savedVisualizationParsedUrl.getRootRelativePath()); - // Since we aren't reloading the page, only inserting a new browser history item, we need to manually update - // the last url for this app, so directly clicking on the Visualize tab will also bring the user to the saved - // url, not the unsaved one. - legacyChrome.trackSubUrlForApp('kibana:visualize', savedVisualizationParsedUrl); + setActiveUrl(savedVisualizationParsedUrl.appPath); const lastDashboardAbsoluteUrl = chrome.navLinks.get('kibana:dashboard').url; const dashboardParsedUrl = absoluteToParsedUrl( diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js index cae1e40cd445a..c0cc499b598f0 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/listing/visualize_listing.js @@ -36,7 +36,6 @@ export function VisualizeListingController($injector, $scope, createNewVis) { const { addBasePath, chrome, - legacyChrome, savedObjectsClient, savedVisualizations, data: { @@ -100,17 +99,13 @@ export function VisualizeListingController($injector, $scope, createNewVis) { selectedItems.map(item => { return savedObjectsClient.delete(item.savedObjectType, item.id); }) - ) - .then(() => { - legacyChrome.untrackNavLinksForDeletedSavedObjects(selectedItems.map(item => item.id)); - }) - .catch(error => { - toastNotifications.addError(error, { - title: i18n.translate('kbn.visualize.visualizeListingDeleteErrorTitle', { - defaultMessage: 'Error deleting visualization', - }), - }); + ).catch(error => { + toastNotifications.addError(error, { + title: i18n.translate('kbn.visualize.visualizeListingDeleteErrorTitle', { + defaultMessage: 'Error deleting visualization', + }), }); + }); }; chrome.setBreadcrumbs([ diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 16715677d1e20..22804685db3cc 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -17,6 +17,7 @@ * under the License. */ +import { BehaviorSubject } from 'rxjs'; import { i18n } from '@kbn/i18n'; import { @@ -28,12 +29,19 @@ import { SavedObjectsClientContract, } from 'kibana/public'; -import { Storage } from '../../../../../plugins/kibana_utils/public'; -import { DataPublicPluginStart } from '../../../../../plugins/data/public'; +import { Storage, createKbnUrlTracker } from '../../../../../plugins/kibana_utils/public'; +import { + DataPublicPluginStart, + DataPublicPluginSetup, + getQueryStateContainer, +} from '../../../../../plugins/data/public'; import { IEmbeddableStart } from '../../../../../plugins/embeddable/public'; import { NavigationPublicPluginStart as NavigationStart } from '../../../../../plugins/navigation/public'; import { SharePluginStart } from '../../../../../plugins/share/public'; -import { KibanaLegacySetup } from '../../../../../plugins/kibana_legacy/public'; +import { + KibanaLegacySetup, + AngularRenderedAppUpdater, +} from '../../../../../plugins/kibana_legacy/public'; import { VisualizationsStart } from '../../../visualizations/public'; import { VisualizeConstants } from './np_ready/visualize_constants'; import { setServices, VisualizeKibanaServices } from './kibana_services'; @@ -42,7 +50,6 @@ import { HomePublicPluginSetup, } from '../../../../../plugins/home/public'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; -import { Chrome } from './legacy_imports'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; @@ -53,12 +60,10 @@ export interface VisualizePluginStartDependencies { } export interface VisualizePluginSetupDependencies { - __LEGACY: { - legacyChrome: Chrome; - }; home: HomePublicPluginSetup; kibanaLegacy: KibanaLegacySetup; usageCollection?: UsageCollectionSetup; + data: DataPublicPluginSetup; } export class VisualizePlugin implements Plugin { @@ -70,46 +75,72 @@ export class VisualizePlugin implements Plugin { share: SharePluginStart; visualizations: VisualizationsStart; } | null = null; + private appStateUpdater = new BehaviorSubject(() => ({})); + private stopUrlTracking: (() => void) | undefined = undefined; constructor(private initializerContext: PluginInitializerContext) {} public async setup( core: CoreSetup, - { home, kibanaLegacy, __LEGACY, usageCollection }: VisualizePluginSetupDependencies + { home, kibanaLegacy, usageCollection, data }: VisualizePluginSetupDependencies ) { + const { querySyncStateContainer, stop: stopQuerySyncStateContainer } = getQueryStateContainer( + data.query + ); + const { appMounted, appUnMounted, stop: stopUrlTracker, setActiveUrl } = createKbnUrlTracker({ + baseUrl: core.http.basePath.prepend('/app/kibana'), + defaultSubUrl: '#/visualize', + storageKey: 'lastUrl:visualize', + navLinkUpdater$: this.appStateUpdater, + toastNotifications: core.notifications.toasts, + stateParams: [ + { + kbnUrlKey: '_g', + stateUpdate$: querySyncStateContainer.state$, + }, + ], + }); + this.stopUrlTracking = () => { + stopQuerySyncStateContainer(); + stopUrlTracker(); + }; + kibanaLegacy.registerLegacyApp({ id: 'visualize', title: 'Visualize', + updater$: this.appStateUpdater.asObservable(), + navLinkId: 'kibana:visualize', mount: async (params: AppMountParameters) => { const [coreStart] = await core.getStartServices(); + if (this.startDependencies === null) { throw new Error('not started yet'); } + appMounted(); const { savedObjectsClient, embeddable, navigation, visualizations, - data, + data: dataStart, share, } = this.startDependencies; const deps: VisualizeKibanaServices = { - ...__LEGACY, pluginInitializerContext: this.initializerContext, addBasePath: coreStart.http.basePath.prepend, core: coreStart, chrome: coreStart.chrome, - data, + data: dataStart, embeddable, getBasePath: core.http.basePath.get, - indexPatterns: data.indexPatterns, + indexPatterns: dataStart.indexPatterns, localStorage: new Storage(localStorage), navigation, savedObjectsClient, savedVisualizations: visualizations.getSavedVisualizationsLoader(), - savedQueryService: data.query.savedQueries, + savedQueryService: dataStart.query.savedQueries, share, toastNotifications: coreStart.notifications.toasts, uiSettings: coreStart.uiSettings, @@ -118,11 +149,16 @@ export class VisualizePlugin implements Plugin { visualizations, usageCollection, I18nContext: coreStart.i18n.Context, + setActiveUrl, }; setServices(deps); const { renderApp } = await import('./np_ready/application'); - return renderApp(params.element, params.appBasePath, deps); + const unmount = renderApp(params.element, params.appBasePath, deps); + return () => { + unmount(); + appUnMounted(); + }; }, }); @@ -153,4 +189,10 @@ export class VisualizePlugin implements Plugin { visualizations, }; } + + stop() { + if (this.stopUrlTracking) { + this.stopUrlTracking(); + } + } } diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index 4e52f6f6bafec..38b3434ef9c48 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -61,6 +61,10 @@ const mockCore = { }, }; +let refreshInterval = undefined; +let isTimeRangeSelectorEnabled = true; +let isAutoRefreshSelectorEnabled = true; + export const npSetup = { core: mockCore, plugins: { @@ -101,7 +105,14 @@ export const npSetup = { }, query: { filterManager: { + getFetches$: sinon.fake(), + getFilters: sinon.fake(), + getAppFilters: sinon.fake(), getGlobalFilters: sinon.fake(), + removeFilter: sinon.fake(), + addFilters: sinon.fake(), + setFilters: sinon.fake(), + removeAll: sinon.fake(), getUpdates$: mockObservable, }, timefilter: { @@ -110,6 +121,41 @@ export const npSetup = { getRefreshInterval: sinon.fake(), getTimeUpdate$: mockObservable, getRefreshIntervalUpdate$: mockObservable, + getFetch$: mockObservable, + getAutoRefreshFetch$: mockObservable, + getEnabledUpdated$: mockObservable, + getTimeUpdate$: mockObservable, + getRefreshIntervalUpdate$: mockObservable, + isTimeRangeSelectorEnabled: () => { + return isTimeRangeSelectorEnabled; + }, + isAutoRefreshSelectorEnabled: () => { + return isAutoRefreshSelectorEnabled; + }, + disableAutoRefreshSelector: () => { + isAutoRefreshSelectorEnabled = false; + }, + enableAutoRefreshSelector: () => { + isAutoRefreshSelectorEnabled = true; + }, + getRefreshInterval: () => { + return refreshInterval; + }, + setRefreshInterval: interval => { + refreshInterval = interval; + }, + enableTimeRangeSelector: () => { + isTimeRangeSelectorEnabled = true; + }, + disableTimeRangeSelector: () => { + isTimeRangeSelectorEnabled = false; + }, + getTime: sinon.fake(), + setTime: sinon.fake(), + getActiveBounds: sinon.fake(), + getBounds: sinon.fake(), + calculateBounds: sinon.fake(), + createFilter: sinon.fake(), }, history: sinon.fake(), }, @@ -183,10 +229,6 @@ export const npSetup = { }, }; -let refreshInterval = undefined; -let isTimeRangeSelectorEnabled = true; -let isAutoRefreshSelectorEnabled = true; - export const npStart = { core: { chrome: { diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts index 4b17d8517328b..4cf74d991ceb9 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.test.ts @@ -181,4 +181,10 @@ describe('kbnUrlTracker', () => { `"/app/test#/start?state1=(key1:abc)&state2=(key2:def)"` ); }); + + test('set url to storage when setActiveUrl was called', () => { + createTracker(); + urlTracker.setActiveUrl('/deep/path/4'); + expect(storage.getItem('storageKey')).toEqual('#/deep/path/4'); + }); }); diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts index 6f3f64ea7b941..2edd135c184ec 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_tracker.ts @@ -36,6 +36,7 @@ export interface KbnUrlTracker { * Unregistering the url tracker. This won't reset the current state of the nav link */ stop: () => void; + setActiveUrl: (newUrl: string) => void; } /** @@ -130,20 +131,24 @@ export function createKbnUrlTracker({ } } + function setActiveUrl(newUrl: string) { + const urlWithHashes = baseUrl + '#' + newUrl; + let urlWithStates = ''; + try { + urlWithStates = unhashUrl(urlWithHashes); + } catch (e) { + toastNotifications.addDanger(e.message); + } + + activeUrl = getActiveSubUrl(urlWithStates || urlWithHashes); + storageInstance.setItem(storageKey, activeUrl); + } + function onMountApp() { unsubscribe(); // track current hash when within app unsubscribeURLHistory = historyInstance.listen(location => { - const urlWithHashes = baseUrl + '#' + location.pathname + location.search; - let urlWithStates = ''; - try { - urlWithStates = unhashUrl(urlWithHashes); - } catch (e) { - toastNotifications.addDanger(e.message); - } - - activeUrl = getActiveSubUrl(urlWithStates || urlWithHashes); - storageInstance.setItem(storageKey, activeUrl); + setActiveUrl(location.pathname + location.search); }); } @@ -188,5 +193,6 @@ export function createKbnUrlTracker({ stop() { unsubscribe(); }, + setActiveUrl, }; } From 8bc3fa40425778a8e056eb4d561ff28f3e870c22 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Tue, 18 Feb 2020 13:45:25 +0100 Subject: [PATCH 39/65] Bugfix: Navigation from unsaved dashboard to recently used fails (#57795) Co-authored-by: Elastic Machine --- .../np_ready/dashboard_state_manager.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts index fa5354a17b6d9..fe7beafcad18c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/np_ready/dashboard_state_manager.ts @@ -165,7 +165,7 @@ export class DashboardStateManager { // make sure url ('_a') matches initial state this.kbnUrlStateStorage.set(this.STATE_STORAGE_KEY, initialState, { replace: true }); - // setup state syncing utils. state container will be synched with url into `this.STATE_STORAGE_KEY` query param + // setup state syncing utils. state container will be synced with url into `this.STATE_STORAGE_KEY` query param this.stateSyncRef = syncState({ storageKey: this.STATE_STORAGE_KEY, stateContainer: { @@ -173,10 +173,20 @@ export class DashboardStateManager { set: (state: DashboardAppState | null) => { // sync state required state container to be able to handle null // overriding set() so it could handle null coming from url - this.stateContainer.set({ - ...this.stateDefaults, - ...state, - }); + if (state) { + this.stateContainer.set({ + ...this.stateDefaults, + ...state, + }); + } else { + // Do nothing in case when state from url is empty, + // this fixes: https://github.com/elastic/kibana/issues/57789 + // There are not much cases when state in url could become empty: + // 1. User manually removed `_a` from the url + // 2. Browser is navigating away from the page and most likely there is no `_a` in the url. + // In this case we don't want to do any state updates + // and just allow $scope.$on('destroy') fire later and clean up everything + } }, }, stateStorage: this.kbnUrlStateStorage, From 23306d80973878ecb22f1d3cb9d0a71b8e2ae6c1 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 18 Feb 2020 14:24:22 +0100 Subject: [PATCH 40/65] [Discover] Migrate context AppState / GlobalState to use new app state helpers (#57078) * Remove globalState, migrate to the new helpers * Remove appState, migrate to the new helpers * Add tests --- .../discover/np_ready/angular/context.js | 73 +++-- .../np_ready/angular/context/api/context.ts | 2 +- .../np_ready/angular/context/query/actions.js | 4 +- .../np_ready/angular/context_state.test.ts | 193 ++++++++++++ .../np_ready/angular/context_state.ts | 275 ++++++++++++++++++ 5 files changed, 506 insertions(+), 41 deletions(-) create mode 100644 src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts create mode 100644 src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js index a370c66ae330b..038f783a0daf1 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context.js @@ -19,13 +19,11 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; -import { getAngularModule, getServices, subscribeWithScope } from '../../kibana_services'; - +import { getAngularModule, getServices } from '../../kibana_services'; import './context_app'; +import { getState } from './context_state'; import contextAppRouteTemplate from './context.html'; import { getRootBreadcrumbs } from '../helpers/breadcrumbs'; -import { FilterStateManager } from '../../../../../data/public'; -const { chrome } = getServices(); const k7Breadcrumbs = $route => { const { indexPattern } = $route.current.locals; @@ -68,53 +66,50 @@ getAngularModule().config($routeProvider => { }); }); -function ContextAppRouteController( - $routeParams, - $scope, - AppState, - config, - $route, - getAppState, - globalState -) { +function ContextAppRouteController($routeParams, $scope, config, $route) { const filterManager = getServices().filterManager; - const filterStateManager = new FilterStateManager(globalState, getAppState, filterManager); const indexPattern = $route.current.locals.indexPattern.ip; + const { + startSync: startStateSync, + stopSync: stopStateSync, + appState, + getFilters, + setFilters, + setAppState, + } = getState({ + defaultStepSize: config.get('context:defaultSize'), + timeFieldName: indexPattern.timeFieldName, + storeInSessionStorage: config.get('state:storeInSessionStorage'), + }); + this.state = { ...appState.getState() }; + this.anchorId = $routeParams.id; + this.indexPattern = indexPattern; + this.discoverUrl = getServices().chrome.navLinks.get('kibana:discover').url; + filterManager.setFilters(_.cloneDeep(getFilters())); + startStateSync(); - this.state = new AppState(createDefaultAppState(config, indexPattern)); - this.state.save(true); - + // take care of parameter changes in UI $scope.$watchGroup( [ 'contextAppRoute.state.columns', 'contextAppRoute.state.predecessorCount', 'contextAppRoute.state.successorCount', ], - () => this.state.save(true) + newValues => { + const [columns, predecessorCount, successorCount] = newValues; + if (Array.isArray(columns) && predecessorCount >= 0 && successorCount >= 0) { + setAppState({ columns, predecessorCount, successorCount }); + } + } ); - - const updateSubsciption = subscribeWithScope($scope, filterManager.getUpdates$(), { - next: () => { - this.filters = _.cloneDeep(filterManager.getFilters()); - }, + // take care of parameter filter changes + const filterObservable = filterManager.getUpdates$().subscribe(() => { + setFilters(filterManager); + $route.reload(); }); $scope.$on('$destroy', () => { - filterStateManager.destroy(); - updateSubsciption.unsubscribe(); + stopStateSync(); + filterObservable.unsubscribe(); }); - this.anchorId = $routeParams.id; - this.indexPattern = indexPattern; - this.discoverUrl = chrome.navLinks.get('kibana:discover').url; - this.filters = _.cloneDeep(filterManager.getFilters()); -} - -function createDefaultAppState(config, indexPattern) { - return { - columns: ['_source'], - filters: [], - predecessorCount: parseInt(config.get('context:defaultSize'), 10), - sort: [indexPattern.timeFieldName, 'desc'], - successorCount: parseInt(config.get('context:defaultSize'), 10), - }; } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts index a9c6918adbfde..b91ef5a6b79fb 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/api/context.ts @@ -67,7 +67,7 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) { size: number, filters: Filter[] ) { - if (typeof anchor !== 'object' || anchor === null) { + if (typeof anchor !== 'object' || anchor === null || !size) { return []; } const indexPattern = await indexPatterns.get(indexPatternId); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js index 966ecffda7755..1cebb88cbda5a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/query/actions.js @@ -88,9 +88,11 @@ export function QueryActionsProvider(Promise) { const fetchSurroundingRows = (type, state) => { const { - queryParameters: { indexPatternId, filters, sort, tieBreakerField }, + queryParameters: { indexPatternId, sort, tieBreakerField }, rows: { anchor }, } = state; + const filters = getServices().filterManager.getFilters(); + const count = type === 'successors' ? state.queryParameters.successorCount diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts new file mode 100644 index 0000000000000..1fa71ed11643a --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.test.ts @@ -0,0 +1,193 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getState } from './context_state'; +import { createBrowserHistory, History } from 'history'; +import { FilterManager, Filter } from '../../../../../../../plugins/data/public'; +import { coreMock } from '../../../../../../../core/public/mocks'; +const setupMock = coreMock.createSetup(); + +describe('Test Discover Context State', () => { + let history: History; + let state: any; + const getCurrentUrl = () => history.createHref(history.location); + beforeEach(async () => { + history = createBrowserHistory(); + history.push('/'); + state = await getState({ + defaultStepSize: '4', + timeFieldName: 'time', + history, + }); + state.startSync(); + }); + afterEach(() => { + state.stopSync(); + }); + test('getState function default return', () => { + expect(state.appState.getState()).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "_source", + ], + "filters": Array [], + "predecessorCount": 4, + "sort": Array [ + "time", + "desc", + ], + "successorCount": 4, + } + `); + expect(state.globalState.getState()).toMatchInlineSnapshot(`null`); + expect(state.startSync).toBeDefined(); + expect(state.stopSync).toBeDefined(); + expect(state.getFilters()).toStrictEqual([]); + }); + test('getState -> setAppState syncing to url', async () => { + state.setAppState({ predecessorCount: 10 }); + state.flushToUrl(); + expect(getCurrentUrl()).toMatchInlineSnapshot( + `"/#?_a=(columns:!(_source),filters:!(),predecessorCount:10,sort:!(time,desc),successorCount:4)"` + ); + }); + test('getState -> url to appState syncing', async () => { + history.push( + '/#?_a=(columns:!(_source),predecessorCount:1,sort:!(time,desc),successorCount:1)' + ); + expect(state.appState.getState()).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "_source", + ], + "predecessorCount": 1, + "sort": Array [ + "time", + "desc", + ], + "successorCount": 1, + } + `); + }); + test('getState -> url to appState syncing with return to a url without state', async () => { + history.push( + '/#?_a=(columns:!(_source),predecessorCount:1,sort:!(time,desc),successorCount:1)' + ); + expect(state.appState.getState()).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "_source", + ], + "predecessorCount": 1, + "sort": Array [ + "time", + "desc", + ], + "successorCount": 1, + } + `); + history.push('/'); + expect(state.appState.getState()).toMatchInlineSnapshot(` + Object { + "columns": Array [ + "_source", + ], + "predecessorCount": 1, + "sort": Array [ + "time", + "desc", + ], + "successorCount": 1, + } + `); + }); + + test('getState -> filters', async () => { + const filterManager = new FilterManager(setupMock.uiSettings); + const filterGlobal = { + query: { match: { extension: { query: 'jpg', type: 'phrase' } } }, + meta: { index: 'logstash-*', negate: false, disabled: false, alias: null }, + } as Filter; + filterManager.setGlobalFilters([filterGlobal]); + const filterApp = { + query: { match: { extension: { query: 'png', type: 'phrase' } } }, + meta: { index: 'logstash-*', negate: true, disabled: false, alias: null }, + } as Filter; + filterManager.setAppFilters([filterApp]); + state.setFilters(filterManager); + expect(state.getFilters()).toMatchInlineSnapshot(` + Array [ + Object { + "$state": Object { + "store": "globalState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "logstash-*", + "key": "extension", + "negate": false, + "params": Object { + "query": "jpg", + }, + "type": "phrase", + "value": [Function], + }, + "query": Object { + "match": Object { + "extension": Object { + "query": "jpg", + "type": "phrase", + }, + }, + }, + }, + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "logstash-*", + "key": "extension", + "negate": true, + "params": Object { + "query": "png", + }, + "type": "phrase", + "value": [Function], + }, + "query": Object { + "match": Object { + "extension": Object { + "query": "png", + "type": "phrase", + }, + }, + }, + }, + ] + `); + state.flushToUrl(); + expect(getCurrentUrl()).toMatchInlineSnapshot( + `"/#?_g=(filters:!(('$state':(store:globalState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!f,params:(query:jpg),type:phrase),query:(match:(extension:(query:jpg,type:phrase))))))&_a=(columns:!(_source),filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:'logstash-*',key:extension,negate:!t,params:(query:png),type:phrase),query:(match:(extension:(query:png,type:phrase))))),predecessorCount:4,sort:!(time,desc),successorCount:4)"` + ); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts new file mode 100644 index 0000000000000..8fb6140d55e31 --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context_state.ts @@ -0,0 +1,275 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import _ from 'lodash'; +import { createBrowserHistory, History } from 'history'; +import { + createStateContainer, + createKbnUrlStateStorage, + syncStates, + BaseStateContainer, +} from '../../../../../../../plugins/kibana_utils/public'; +import { esFilters, FilterManager, Filter } from '../../../../../../../plugins/data/public'; + +interface AppState { + /** + * Columns displayed in the table, cannot be changed by UI, just in discover's main app + */ + columns: string[]; + /** + * Array of filters + */ + filters: Filter[]; + /** + * Number of records to be fetched before anchor records (newer records) + */ + predecessorCount: number; + /** + * Sorting of the records to be fetched, assumed to be a legacy parameter + */ + sort: string[]; + /** + * Number of records to be fetched after the anchor records (older records) + */ + successorCount: number; +} + +interface GlobalState { + /** + * Array of filters + */ + filters: Filter[]; +} + +interface GetStateParams { + /** + * Number of records to be fetched when 'Load' link/button is clicked + */ + defaultStepSize: string; + /** + * The timefield used for sorting + */ + timeFieldName: string; + /** + * Determins the use of long vs. short/hashed urls + */ + storeInSessionStorage?: boolean; + /** + * Browser history used for testing + */ + history?: History; +} + +interface GetStateReturn { + /** + * Global state, the _g part of the URL + */ + globalState: BaseStateContainer; + /** + * App state, the _a part of the URL + */ + appState: BaseStateContainer; + /** + * Start sync between state and URL + */ + startSync: () => void; + /** + * Stop sync between state and URL + */ + stopSync: () => void; + /** + * Set app state to with a partial new app state + */ + setAppState: (newState: Partial) => void; + /** + * Get all filters, global and app state + */ + getFilters: () => Filter[]; + /** + * Set global state and app state filters by the given FilterManager instance + * @param filterManager + */ + setFilters: (filterManager: FilterManager) => void; + /** + * sync state to URL, used for testing + */ + flushToUrl: () => void; +} +const GLOBAL_STATE_URL_KEY = '_g'; +const APP_STATE_URL_KEY = '_a'; + +/** + * Builds and returns appState and globalState containers + * provides helper functions to start/stop syncing with URL + */ +export function getState({ + defaultStepSize, + timeFieldName, + storeInSessionStorage = false, + history, +}: GetStateParams): GetStateReturn { + const stateStorage = createKbnUrlStateStorage({ + useHash: storeInSessionStorage, + history: history ? history : createBrowserHistory(), + }); + + const globalStateInitial = stateStorage.get(GLOBAL_STATE_URL_KEY) as GlobalState; + const globalStateContainer = createStateContainer(globalStateInitial); + + const appStateFromUrl = stateStorage.get(APP_STATE_URL_KEY) as AppState; + const appStateInitial = createInitialAppState(defaultStepSize, timeFieldName, appStateFromUrl); + const appStateContainer = createStateContainer(appStateInitial); + + const { start, stop } = syncStates([ + { + storageKey: GLOBAL_STATE_URL_KEY, + stateContainer: { + ...globalStateContainer, + ...{ + set: (value: GlobalState | null) => { + if (value) { + globalStateContainer.set(value); + } + }, + }, + }, + stateStorage, + }, + { + storageKey: APP_STATE_URL_KEY, + stateContainer: { + ...appStateContainer, + ...{ + set: (value: AppState | null) => { + if (value) { + appStateContainer.set(value); + } + }, + }, + }, + stateStorage, + }, + ]); + + return { + globalState: globalStateContainer, + appState: appStateContainer, + startSync: start, + stopSync: stop, + setAppState: (newState: Partial) => { + const oldState = appStateContainer.getState(); + const mergedState = { ...oldState, ...newState }; + + if (!isEqualState(oldState, mergedState)) { + appStateContainer.set(mergedState); + } + }, + getFilters: () => [ + ...getFilters(globalStateContainer.getState()), + ...getFilters(appStateContainer.getState()), + ], + setFilters: (filterManager: FilterManager) => { + // global state filters + const globalFilters = filterManager.getGlobalFilters(); + const globalFilterChanged = !isEqualFilters( + globalFilters, + getFilters(globalStateContainer.getState()) + ); + if (globalFilterChanged) { + globalStateContainer.set({ filters: globalFilters }); + } + // app state filters + const appFilters = filterManager.getAppFilters(); + const appFilterChanged = !isEqualFilters( + appFilters, + getFilters(appStateContainer.getState()) + ); + if (appFilterChanged) { + appStateContainer.set({ ...appStateContainer.getState(), ...{ filters: appFilters } }); + } + }, + // helper function just needed for testing + flushToUrl: () => stateStorage.flush(), + }; +} + +/** + * Helper function to compare 2 different filter states + */ +export function isEqualFilters(filtersA: Filter[], filtersB: Filter[]) { + if (!filtersA && !filtersB) { + return true; + } else if (!filtersA || !filtersB) { + return false; + } + return esFilters.compareFilters(filtersA, filtersB, esFilters.COMPARE_ALL_OPTIONS); +} + +/** + * Helper function to compare 2 different states, is needed since comparing filters + * works differently, doesn't work with _.isEqual + */ +function isEqualState(stateA: AppState | GlobalState, stateB: AppState | GlobalState) { + if (!stateA && !stateB) { + return true; + } else if (!stateA || !stateB) { + return false; + } + const { filters: stateAFilters = [], ...stateAPartial } = stateA; + const { filters: stateBFilters = [], ...stateBPartial } = stateB; + return ( + _.isEqual(stateAPartial, stateBPartial) && + esFilters.compareFilters(stateAFilters, stateBFilters, esFilters.COMPARE_ALL_OPTIONS) + ); +} + +/** + * Helper function to return array of filter object of a given state + */ +function getFilters(state: AppState | GlobalState): Filter[] { + if (!state || !Array.isArray(state.filters)) { + return []; + } + return state.filters; +} + +/** + * Helper function to return the initial app state, which is a merged object of url state and + * default state. The default size is the default number of successor/predecessor records to fetch + */ +function createInitialAppState( + defaultSize: string, + timeFieldName: string, + urlState: AppState +): AppState { + const defaultState = { + columns: ['_source'], + filters: [], + predecessorCount: parseInt(defaultSize, 10), + sort: [timeFieldName, 'desc'], + successorCount: parseInt(defaultSize, 10), + }; + if (typeof urlState !== 'object') { + return defaultState; + } + + return { + ...defaultState, + ...urlState, + }; +} From ad5daba2eaf6800a0c67d658f62877706033d797 Mon Sep 17 00:00:00 2001 From: patrykkopycinski Date: Tue, 18 Feb 2020 14:53:31 +0100 Subject: [PATCH 41/65] [SIEM] Replace AutoSizer with use-resize-observer (#56588) --- renovate.json5 | 8 + .../auto_sizer/__examples__/index.stories.tsx | 27 - .../public/components/auto_sizer/index.tsx | 182 ---- .../components/charts/areachart.test.tsx | 4 +- .../public/components/charts/areachart.tsx | 36 +- .../components/charts/barchart.test.tsx | 6 +- .../public/components/charts/barchart.tsx | 39 +- .../events_viewer/events_viewer.test.tsx | 5 + .../events_viewer/events_viewer.tsx | 212 +++-- .../components/events_viewer/index.test.tsx | 5 + .../__snapshots__/timeline.test.tsx.snap | 793 +++++++++++++++++- .../components/timeline/timeline.test.tsx | 5 + .../public/components/timeline/timeline.tsx | 192 ++--- .../plugins/siem/public/pages/home/index.tsx | 171 ++-- .../pages/hosts/details/details_tabs.test.tsx | 5 + x-pack/package.json | 2 + yarn.lock | 14 + 17 files changed, 1144 insertions(+), 562 deletions(-) delete mode 100644 x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx diff --git a/renovate.json5 b/renovate.json5 index 642c4a98b5799..58a64a5d0f967 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -913,6 +913,14 @@ '@types/tslib', ], }, + { + groupSlug: 'use-resize-observer', + groupName: 'use-resize-observer related packages', + packageNames: [ + 'use-resize-observer', + '@types/use-resize-observer', + ], + }, { groupSlug: 'uuid', groupName: 'uuid related packages', diff --git a/x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx b/x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx deleted file mode 100644 index 414cea0d3f40d..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/auto_sizer/__examples__/index.stories.tsx +++ /dev/null @@ -1,27 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { storiesOf } from '@storybook/react'; -import React from 'react'; -import { AutoSizer } from '..'; - -storiesOf('components/AutoSizer', module).add('example', () => ( -
- - {({ measureRef, content }) => ( -
-
- {'width: '} - {content.width} -
-
- {'height: '} - {content.height} -
-
- )} -
-
-)); diff --git a/x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx b/x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx deleted file mode 100644 index 8b3a85b28b8fe..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/auto_sizer/index.tsx +++ /dev/null @@ -1,182 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import isEqual from 'lodash/fp/isEqual'; -import React from 'react'; -import ResizeObserver from 'resize-observer-polyfill'; - -interface Measurement { - width?: number; - height?: number; -} - -interface Measurements { - bounds: Measurement; - content: Measurement; - windowMeasurement: Measurement; -} - -interface AutoSizerProps { - detectAnyWindowResize?: boolean; - bounds?: boolean; - content?: boolean; - onResize?: (size: Measurements) => void; - children: ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - args: { measureRef: (instance: HTMLElement | null) => any } & Measurements - ) => React.ReactNode; -} - -interface AutoSizerState { - boundsMeasurement: Measurement; - contentMeasurement: Measurement; - windowMeasurement: Measurement; -} - -/** A hard-fork of the `infra` `AutoSizer` ಠ_ಠ */ -export class AutoSizer extends React.PureComponent { - public element: HTMLElement | null = null; - public resizeObserver: ResizeObserver | null = null; - public windowWidth: number = -1; - - public readonly state = { - boundsMeasurement: { - height: void 0, - width: void 0, - }, - contentMeasurement: { - height: void 0, - width: void 0, - }, - windowMeasurement: { - height: void 0, - width: void 0, - }, - }; - - constructor(props: AutoSizerProps) { - super(props); - if (this.props.detectAnyWindowResize) { - window.addEventListener('resize', this.updateMeasurement); - } - this.resizeObserver = new ResizeObserver(entries => { - entries.forEach(entry => { - if (entry.target === this.element) { - this.measure(entry); - } - }); - }); - } - - public componentWillUnmount() { - if (this.resizeObserver) { - this.resizeObserver.disconnect(); - this.resizeObserver = null; - } - if (this.props.detectAnyWindowResize) { - window.removeEventListener('resize', this.updateMeasurement); - } - } - - public measure = (entry: ResizeObserverEntry | null) => { - if (!this.element) { - return; - } - - const { content = true, bounds = false } = this.props; - const { - boundsMeasurement: previousBoundsMeasurement, - contentMeasurement: previousContentMeasurement, - windowMeasurement: previousWindowMeasurement, - } = this.state; - - const boundsRect = bounds ? this.element.getBoundingClientRect() : null; - const boundsMeasurement = boundsRect - ? { - height: this.element.getBoundingClientRect().height, - width: this.element.getBoundingClientRect().width, - } - : previousBoundsMeasurement; - const windowMeasurement: Measurement = { - width: window.innerWidth, - height: window.innerHeight, - }; - - if ( - this.props.detectAnyWindowResize && - boundsMeasurement && - boundsMeasurement.width && - this.windowWidth !== -1 && - this.windowWidth > window.innerWidth - ) { - const gap = this.windowWidth - window.innerWidth; - boundsMeasurement.width = boundsMeasurement.width - gap; - } - this.windowWidth = window.innerWidth; - const contentRect = content && entry ? entry.contentRect : null; - const contentMeasurement = - contentRect && entry - ? { - height: entry.contentRect.height, - width: entry.contentRect.width, - } - : previousContentMeasurement; - - if ( - isEqual(boundsMeasurement, previousBoundsMeasurement) && - isEqual(contentMeasurement, previousContentMeasurement) && - isEqual(windowMeasurement, previousWindowMeasurement) - ) { - return; - } - - requestAnimationFrame(() => { - if (!this.resizeObserver) { - return; - } - - this.setState({ boundsMeasurement, contentMeasurement, windowMeasurement }); - - if (this.props.onResize) { - this.props.onResize({ - bounds: boundsMeasurement, - content: contentMeasurement, - windowMeasurement, - }); - } - }); - }; - - public render() { - const { children } = this.props; - const { boundsMeasurement, contentMeasurement, windowMeasurement } = this.state; - - return children({ - bounds: boundsMeasurement, - content: contentMeasurement, - windowMeasurement, - measureRef: this.storeRef, - }); - } - - private updateMeasurement = () => { - window.setTimeout(() => { - this.measure(null); - }, 0); - }; - - private storeRef = (element: HTMLElement | null) => { - if (this.element && this.resizeObserver) { - this.resizeObserver.unobserve(this.element); - } - - if (element && this.resizeObserver) { - this.resizeObserver.observe(element); - } - - this.element = element; - }; -} diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx index 342d7d35f9cb7..27f0222b96b77 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.test.tsx @@ -331,7 +331,7 @@ describe('AreaChart', () => { }); it(`should render area chart`, () => { - expect(shallowWrapper.find('AutoSizer')).toHaveLength(1); + expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1); expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0); }); }); @@ -344,7 +344,7 @@ describe('AreaChart', () => { }); it(`should render a chart place holder`, () => { - expect(shallowWrapper.find('AutoSizer')).toHaveLength(0); + expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0); expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1); }); } diff --git a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx index 57f78080abc60..fd05b80e41235 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/areachart.tsx @@ -16,7 +16,8 @@ import { RecursivePartial, } from '@elastic/charts'; import { getOr, get, isNull, isNumber } from 'lodash/fp'; -import { AutoSizer } from '../auto_sizer'; +import useResizeObserver from 'use-resize-observer'; + import { ChartPlaceHolder } from './chart_place_holder'; import { useTimeZone } from '../../lib/kibana'; import { @@ -124,35 +125,24 @@ export const AreaChartBase = React.memo(AreaChartBaseComponent); AreaChartBase.displayName = 'AreaChartBase'; -export const AreaChartComponent = ({ - areaChart, - configs, -}: { +interface AreaChartComponentProps { areaChart: ChartSeriesData[] | null | undefined; configs?: ChartSeriesConfigs | undefined; -}) => { +} + +export const AreaChartComponent: React.FC = ({ areaChart, configs }) => { + const { ref: measureRef, width, height } = useResizeObserver({}); const customHeight = get('customHeight', configs); const customWidth = get('customWidth', configs); + const chartHeight = getChartHeight(customHeight, height); + const chartWidth = getChartWidth(customWidth, width); return checkIfAnyValidSeriesExist(areaChart) ? ( - - {({ measureRef, content: { height, width } }) => ( - - - - )} - + + + ) : ( - + ); }; diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx index d8e5079dd72a6..0b6635b04d380 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx @@ -278,7 +278,7 @@ describe.each(chartDataSets)('BarChart with valid data [%o]', data => { }); it(`should render chart`, () => { - expect(shallowWrapper.find('AutoSizer')).toHaveLength(1); + expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(1); expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0); }); }); @@ -290,8 +290,8 @@ describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => { shallowWrapper = shallow(); }); - it(`should render chart holder`, () => { - expect(shallowWrapper.find('AutoSizer')).toHaveLength(0); + it(`should render a ChartPlaceHolder`, () => { + expect(shallowWrapper.find('WrappedByAutoSizer')).toHaveLength(0); expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(1); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx index d9dd302dae724..1355926d343df 100644 --- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx +++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx @@ -8,9 +8,9 @@ import React from 'react'; import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts'; import { getOr, get, isNumber } from 'lodash/fp'; import deepmerge from 'deepmerge'; +import useResizeObserver from 'use-resize-observer'; import { useTimeZone } from '../../lib/kibana'; -import { AutoSizer } from '../auto_sizer'; import { ChartPlaceHolder } from './chart_place_holder'; import { chartDefaultSettings, @@ -99,40 +99,25 @@ export const BarChartBase = React.memo(BarChartBaseComponent); BarChartBase.displayName = 'BarChartBase'; -export const BarChartComponent = ({ - barChart, - configs, -}: { +interface BarChartComponentProps { barChart: ChartSeriesData[] | null | undefined; configs?: ChartSeriesConfigs | undefined; -}) => { +} + +export const BarChartComponent: React.FC = ({ barChart, configs }) => { + const { ref: measureRef, width, height } = useResizeObserver({}); const customHeight = get('customHeight', configs); const customWidth = get('customWidth', configs); + const chartHeight = getChartHeight(customHeight, height); + const chartWidth = getChartWidth(customWidth, width); return checkIfAnyValidSeriesExist(barChart) ? ( - - {({ measureRef, content: { height, width } }) => ( - - - - )} - + + + ) : ( - + ); }; -BarChartComponent.displayName = 'BarChartComponent'; - export const BarChart = React.memo(BarChartComponent); - -BarChart.displayName = 'BarChart'; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx index 3cef3e98c2f0a..8c4228b597dbb 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { MockedProvider } from 'react-apollo/test-utils'; +import useResizeObserver from 'use-resize-observer'; import { mockIndexPattern, TestProviders } from '../../mock'; import { wait } from '../../lib/helpers'; @@ -27,6 +28,10 @@ mockUseFetchIndexPatterns.mockImplementation(() => [ }, ]); +const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; +jest.mock('use-resize-observer'); +mockUseResizeObserver.mockImplementation(() => ({})); + const from = 1566943856794; const to = 1566857456791; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 14473605a7c88..cbce1f635310a 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -9,13 +9,13 @@ import deepEqual from 'fast-deep-equal'; import { getOr, isEmpty, isEqual, union } from 'lodash/fp'; import React, { useMemo } from 'react'; import styled from 'styled-components'; +import useResizeObserver from 'use-resize-observer'; import { BrowserFields } from '../../containers/source'; import { TimelineQuery } from '../../containers/timeline'; import { Direction } from '../../graphql/types'; import { useKibana } from '../../lib/kibana'; import { KqlMode } from '../../store/timeline/model'; -import { AutoSizer } from '../auto_sizer'; import { HeaderSection } from '../header_section'; import { ColumnHeader } from '../timeline/body/column_headers/column_header'; import { defaultHeaders } from '../timeline/body/column_headers/default_headers'; @@ -95,6 +95,7 @@ const EventsViewerComponent: React.FC = ({ toggleColumn, utilityBar, }) => { + const { ref: measureRef, width = 0 } = useResizeObserver({}); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const kibana = useKibana(); const combinedQueries = combineQueries({ @@ -120,115 +121,108 @@ const EventsViewerComponent: React.FC = ({ return ( - - {({ measureRef, content: { width = 0 } }) => ( - <> - -
- - - {combinedQueries != null ? ( - - {({ - events, - getUpdatedAt, - inspect, - loading, - loadMore, - pageInfo, - refetch, - totalCount = 0, - }) => { - const totalCountMinusDeleted = - totalCount > 0 ? totalCount - deletedEventIds.length : 0; - - const subtitle = `${ - i18n.SHOWING - }: ${totalCountMinusDeleted.toLocaleString()} ${timelineTypeContext.unit?.( - totalCountMinusDeleted - ) ?? i18n.UNIT(totalCountMinusDeleted)}`; - - // TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt) - return ( - <> - + +
+ + + {combinedQueries != null ? ( + + {({ + events, + getUpdatedAt, + inspect, + loading, + loadMore, + pageInfo, + refetch, + totalCount = 0, + }) => { + const totalCountMinusDeleted = + totalCount > 0 ? totalCount - deletedEventIds.length : 0; + + const subtitle = `${ + i18n.SHOWING + }: ${totalCountMinusDeleted.toLocaleString()} ${timelineTypeContext.unit?.( + totalCountMinusDeleted + ) ?? i18n.UNIT(totalCountMinusDeleted)}`; + + // TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt) + return ( + <> + + {headerFilterGroup} + + + {utilityBar?.(refetch, totalCountMinusDeleted)} + +
+ + - {headerFilterGroup} - - - {utilityBar?.(refetch, totalCountMinusDeleted)} - -
- - - - !deletedEventIds.includes(e._id))} - id={id} - isEventViewer={true} - height={height} - sort={sort} - toggleColumn={toggleColumn} - /> - -
- -
- - ); - }} - - ) : null} - - )} - + inputId="global" + inspect={inspect} + loading={loading} + refetch={refetch} + /> + + !deletedEventIds.includes(e._id))} + id={id} + isEventViewer={true} + height={height} + sort={sort} + toggleColumn={toggleColumn} + /> + +
+ +
+ + ); + }} +
+ ) : null} + ); }; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx index ec8d329f1dfe3..2bedd1cb89b41 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.test.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { MockedProvider } from 'react-apollo/test-utils'; +import useResizeObserver from 'use-resize-observer'; import { wait } from '../../lib/helpers'; import { mockIndexPattern, TestProviders } from '../../mock'; @@ -26,6 +27,10 @@ mockUseFetchIndexPatterns.mockImplementation(() => [ }, ]); +const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; +jest.mock('use-resize-observer'); +mockUseResizeObserver.mockImplementation(() => ({})); + const from = 1566943856794; const to = 1566857456791; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap index 67d266d1cbf39..3fcd258b79147 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/timeline/__snapshots__/timeline.test.tsx.snap @@ -1,10 +1,793 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Timeline rendering renders correctly against snapshot 1`] = ` - - - + + + + + + + + `; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx index f7c0d0b475734..78899b7c5d628 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.test.tsx @@ -7,6 +7,7 @@ import { shallow } from 'enzyme'; import React from 'react'; import { MockedProvider } from 'react-apollo/test-utils'; +import useResizeObserver from 'use-resize-observer'; import { timelineQuery } from '../../containers/timeline/index.gql_query'; import { mockBrowserFields } from '../../containers/source/mock'; @@ -29,6 +30,10 @@ const testFlyoutHeight = 980; jest.mock('../../lib/kibana'); +const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; +jest.mock('use-resize-observer'); +mockUseResizeObserver.mockImplementation(() => ({})); + describe('Timeline', () => { const sort: Sort = { columnId: '@timestamp', diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx index 09457c8f0285a..4b7331ab14c7e 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx @@ -8,13 +8,13 @@ import { EuiFlexGroup } from '@elastic/eui'; import { getOr, isEmpty } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; +import useResizeObserver from 'use-resize-observer'; import { BrowserFields } from '../../containers/source'; import { TimelineQuery } from '../../containers/timeline'; import { Direction } from '../../graphql/types'; import { useKibana } from '../../lib/kibana'; import { KqlMode, EventType } from '../../store/timeline/model'; -import { AutoSizer } from '../auto_sizer'; import { ColumnHeader } from './body/column_headers/column_header'; import { defaultHeaders } from './body/column_headers/default_headers'; import { Sort } from './body/sort'; @@ -88,7 +88,7 @@ interface Props { } /** The parent Timeline component */ -export const TimelineComponent = ({ +export const TimelineComponent: React.FC = ({ browserFields, columns, dataProviders, @@ -118,7 +118,10 @@ export const TimelineComponent = ({ start, sort, toggleColumn, -}: Props) => { +}) => { + const { ref: measureRef, width = 0, height: timelineHeaderHeight = 0 } = useResizeObserver< + HTMLDivElement + >({}); const kibana = useKibana(); const combinedQueries = combineQueries({ config: esQuery.getEsQueryConfig(kibana.services.uiSettings), @@ -132,101 +135,98 @@ export const TimelineComponent = ({ end, }); const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; + return ( - - {({ measureRef, content: { height: timelineHeaderHeight = 0, width = 0 } }) => ( - + }> + + + + {combinedQueries != null ? ( + c.id)} + sourceId="default" + limit={itemsPerPage} + filterQuery={combinedQueries.filterQuery} + sortField={{ + sortFieldId: sort.columnId, + direction: sort.sortDirection as Direction, + }} > - - - - - {combinedQueries != null ? ( - c.id)} - sourceId="default" - limit={itemsPerPage} - filterQuery={combinedQueries.filterQuery} - sortField={{ - sortFieldId: sort.columnId, - direction: sort.sortDirection as Direction, - }} - > - {({ - events, - inspect, - loading, - totalCount, - pageInfo, - loadMore, - getUpdatedAt, - refetch, - }) => ( - - - -