From 96000fa425932618211b76643ef6914335beb28e Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Fri, 21 Feb 2020 15:31:57 +0100 Subject: [PATCH 1/7] [ML] Transforms: Adds clone feature to transforms list. (#57837) Adds a Clone action to the transform management list. The action opens the transform wizard with prepopulated configurations based on the selected transform. The fields for the transform name and target index will not be populated to avoid the obvious "already exists" warnings. --- .../plugins/transform/public/app/app.tsx | 5 + .../transform/public/app/common/index.ts | 3 + .../public/app/common/request.test.ts | 11 +- .../transform/public/app/common/request.ts | 6 + .../transform/public/app/constants/index.ts | 1 + .../transform/public/app/lib/kibana/common.ts | 34 ++- .../transform/public/app/lib/kibana/index.ts | 1 + .../public/app/lib/kibana/kibana_context.tsx | 18 +- .../public/app/lib/kibana/kibana_provider.tsx | 20 +- .../clone_transform_section.tsx | 194 ++++++++++++++++++ .../app/sections/clone_transform/index.ts | 7 + .../use_source_index_data.ts | 3 +- .../components/step_define/index.ts | 3 +- .../step_define/step_define_form.tsx | 72 ++++++- .../components/step_details/index.ts | 6 +- .../step_details/step_details_form.tsx | 16 ++ .../components/wizard/wizard.tsx | 26 ++- .../transform_list/action_clone.tsx | 60 ++++++ .../transform_list/actions.test.tsx | 3 +- .../components/transform_list/actions.tsx | 6 + .../app/services/navigation/breadcrumb.ts | 8 + .../public/app/services/text/text.ts | 3 + 22 files changed, 454 insertions(+), 52 deletions(-) create mode 100644 x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx create mode 100644 x-pack/legacy/plugins/transform/public/app/sections/clone_transform/index.ts create mode 100644 x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx diff --git a/x-pack/legacy/plugins/transform/public/app/app.tsx b/x-pack/legacy/plugins/transform/public/app/app.tsx index 825c1761bf619..0f21afbcccca8 100644 --- a/x-pack/legacy/plugins/transform/public/app/app.tsx +++ b/x-pack/legacy/plugins/transform/public/app/app.tsx @@ -16,6 +16,7 @@ import { getAppProviders } from './app_dependencies'; import { AuthorizationContext } from './lib/authorization'; import { AppDependencies } from '../shim'; +import { CloneTransformSection } from './sections/clone_transform'; import { CreateTransformSection } from './sections/create_transform'; import { TransformManagementSection } from './sections/transform_management'; @@ -39,6 +40,10 @@ export const App: FC = () => { return (
+ { + test('isMatchAllQuery()', () => { + expect(isMatchAllQuery(defaultQuery)).toBe(false); + expect(isMatchAllQuery(matchAllQuery)).toBe(true); + expect(isMatchAllQuery(simpleQuery)).toBe(false); + }); + test('isSimpleQuery()', () => { expect(isSimpleQuery(defaultQuery)).toBe(true); expect(isSimpleQuery(matchAllQuery)).toBe(false); diff --git a/x-pack/legacy/plugins/transform/public/app/common/request.ts b/x-pack/legacy/plugins/transform/public/app/common/request.ts index 5d508f3d245d3..3b740de177ef8 100644 --- a/x-pack/legacy/plugins/transform/public/app/common/request.ts +++ b/x-pack/legacy/plugins/transform/public/app/common/request.ts @@ -53,6 +53,12 @@ export function isSimpleQuery(arg: any): arg is SimpleQuery { return arg.query_string !== undefined; } +export const matchAllQuery = { match_all: {} }; +export function isMatchAllQuery(query: any): boolean { + return query.match_all !== undefined && Object.keys(query.match_all).length === 0; +} + +export const defaultQuery: PivotQuery = { query_string: { query: '*' } }; export function isDefaultQuery(query: PivotQuery): boolean { return isSimpleQuery(query) && query.query_string.query === '*'; } diff --git a/x-pack/legacy/plugins/transform/public/app/constants/index.ts b/x-pack/legacy/plugins/transform/public/app/constants/index.ts index 85ffc222f59a2..78b5f018dd782 100644 --- a/x-pack/legacy/plugins/transform/public/app/constants/index.ts +++ b/x-pack/legacy/plugins/transform/public/app/constants/index.ts @@ -8,6 +8,7 @@ export const CLIENT_BASE_PATH = '/management/elasticsearch/transform'; export enum SECTION_SLUG { HOME = 'transform_management', + CLONE_TRANSFORM = 'clone_transform', CREATE_TRANSFORM = 'create_transform', } diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts index 3e55d509a94ab..aba61766b5d2b 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts +++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts @@ -4,17 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, IUiSettingsClient } from 'src/core/public'; +import { SavedObjectsClientContract, SimpleSavedObject, IUiSettingsClient } from 'src/core/public'; import { IndexPattern, esQuery, IndexPatternsContract, } from '../../../../../../../../src/plugins/data/public'; +import { matchAllQuery } from '../../common'; + type IndexPatternId = string; type SavedSearchId = string; -let indexPatternCache = []; +let indexPatternCache: Array>> = []; let fullIndexPatterns; let currentIndexPattern = null; let currentSavedSearch = null; @@ -53,6 +55,10 @@ export function loadIndexPatterns( }); } +export function getIndexPatternIdByTitle(indexPatternTitle: string): string | undefined { + return indexPatternCache.find(d => d?.attributes?.title === indexPatternTitle)?.id; +} + type CombinedQuery = Record<'bool', any> | unknown; export function loadCurrentIndexPattern( @@ -69,12 +75,20 @@ export function loadCurrentSavedSearch(savedSearches: any, savedSearchId: SavedS return currentSavedSearch; } +function isIndexPattern(arg: any): arg is IndexPattern { + return arg !== undefined; +} // Helper for creating the items used for searching and job creation. export function createSearchItems( indexPattern: IndexPattern | undefined, savedSearch: any, config: IUiSettingsClient -) { +): { + indexPattern: IndexPattern; + savedSearch: any; + query: any; + combinedQuery: CombinedQuery; +} { // query is only used by the data visualizer as it needs // a lucene query_string. // Using a blank query will cause match_all:{} to be used @@ -86,17 +100,13 @@ export function createSearchItems( let combinedQuery: CombinedQuery = { bool: { - must: [ - { - match_all: {}, - }, - ], + must: [matchAllQuery], }, }; - if (indexPattern === undefined && savedSearch !== null && savedSearch.id !== undefined) { + if (!isIndexPattern(indexPattern) && savedSearch !== null && savedSearch.id !== undefined) { const searchSource = savedSearch.searchSource; - indexPattern = searchSource.getField('index'); + indexPattern = searchSource.getField('index') as IndexPattern; query = searchSource.getField('query'); const fs = searchSource.getField('filter'); @@ -107,6 +117,10 @@ export function createSearchItems( combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs); } + if (!isIndexPattern(indexPattern)) { + throw new Error('Index Pattern is not defined.'); + } + return { indexPattern, savedSearch, diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts index 82d5362e21c02..62107cb37ff2c 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts +++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export { getIndexPatternIdByTitle, loadIndexPatterns } from './common'; export { useKibanaContext, InitializedKibanaContextValue, diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx index 5b7702a0193ec..b0a0371d2de86 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx +++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx @@ -6,30 +6,26 @@ import React, { createContext, useContext, FC } from 'react'; +import { IUiSettingsClient } from 'kibana/public'; + import { SavedSearch } from '../../../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/types'; import { IndexPattern, IndexPatternsContract, } from '../../../../../../../../src/plugins/data/public'; -import { KibanaConfig } from '../../../../../../../../src/legacy/server/kbn_server'; - -// set() method is missing in original d.ts -interface KibanaConfigTypeFix extends KibanaConfig { - set(key: string, value: any): void; -} interface UninitializedKibanaContextValue { - initialized: boolean; + initialized: false; } export interface InitializedKibanaContextValue { combinedQuery: any; - currentIndexPattern: IndexPattern; - currentSavedSearch: SavedSearch; indexPatterns: IndexPatternsContract; - initialized: boolean; + initialized: true; kbnBaseUrl: string; - kibanaConfig: KibanaConfigTypeFix; + kibanaConfig: IUiSettingsClient; + currentIndexPattern: IndexPattern; + currentSavedSearch?: SavedSearch; } export type KibanaContextValue = UninitializedKibanaContextValue | InitializedKibanaContextValue; diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx index 0a9de49168ad4..d2cf5f2b32910 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx +++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx @@ -17,7 +17,7 @@ import { loadCurrentSavedSearch, } from './common'; -import { KibanaContext, KibanaContextValue } from './kibana_context'; +import { InitializedKibanaContextValue, KibanaContext, KibanaContextValue } from './kibana_context'; const indexPatterns = npStart.plugins.data.indexPatterns; const savedObjectsClient = npStart.core.savedObjects.client; @@ -52,20 +52,20 @@ export const KibanaProvider: FC = ({ savedObjectId, children }) => { const kibanaConfig = npStart.core.uiSettings; - const { indexPattern, savedSearch, combinedQuery } = createSearchItems( - fetchedIndexPattern, - fetchedSavedSearch, - kibanaConfig - ); - - const kibanaContext = { + const { + indexPattern: currentIndexPattern, + savedSearch: currentSavedSearch, combinedQuery, - currentIndexPattern: indexPattern, - currentSavedSearch: savedSearch, + } = createSearchItems(fetchedIndexPattern, fetchedSavedSearch, kibanaConfig); + + const kibanaContext: InitializedKibanaContextValue = { indexPatterns, initialized: true, kbnBaseUrl: npStart.core.injectedMetadata.getBasePath(), kibanaConfig, + combinedQuery, + currentIndexPattern, + currentSavedSearch, }; setContextValue(kibanaContext); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx new file mode 100644 index 0000000000000..de96a4de32962 --- /dev/null +++ b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx @@ -0,0 +1,194 @@ +/* + * 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, { useEffect, useState, FC } from 'react'; +import { RouteComponentProps } from 'react-router-dom'; + +import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; + +import { + EuiBetaBadge, + EuiButtonEmpty, + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiPageContent, + EuiPageContentBody, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; + +import { npStart } from 'ui/new_platform'; + +import { useApi } from '../../hooks/use_api'; + +import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants'; +import { TransformPivotConfig } from '../../common'; +import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation'; +import { documentationLinksService } from '../../services/documentation'; +import { PrivilegesWrapper } from '../../lib/authorization'; +import { + getIndexPatternIdByTitle, + loadIndexPatterns, + KibanaProvider, + RenderOnlyWithInitializedKibanaContext, +} from '../../lib/kibana'; + +import { Wizard } from '../create_transform/components/wizard'; + +const indexPatterns = npStart.plugins.data.indexPatterns; +const savedObjectsClient = npStart.core.savedObjects.client; + +interface GetTransformsResponseOk { + count: number; + transforms: TransformPivotConfig[]; +} + +interface GetTransformsResponseError { + error: { + msg: string; + path: string; + query: any; + statusCode: number; + response: string; + }; +} + +function isGetTransformsResponseError(arg: any): arg is GetTransformsResponseError { + return arg.error !== undefined; +} + +type GetTransformsResponse = GetTransformsResponseOk | GetTransformsResponseError; + +type Props = RouteComponentProps<{ transformId: string }>; +export const CloneTransformSection: FC = ({ match }) => { + // Set breadcrumb and page title + useEffect(() => { + breadcrumbService.setBreadcrumbs(BREADCRUMB_SECTION.CLONE_TRANSFORM); + docTitleService.setTitle('createTransform'); + }, []); + + const api = useApi(); + + const transformId = match.params.transformId; + + const [transformConfig, setTransformConfig] = useState(); + const [errorMessage, setErrorMessage] = useState(); + const [isInitialized, setIsInitialized] = useState(false); + const [savedObjectId, setSavedObjectId] = useState(undefined); + + const fetchTransformConfig = async () => { + try { + const transformConfigs: GetTransformsResponse = await api.getTransforms(transformId); + if (isGetTransformsResponseError(transformConfigs)) { + setTransformConfig(undefined); + setErrorMessage(transformConfigs.error.msg); + setIsInitialized(true); + return; + } + + await loadIndexPatterns(savedObjectsClient, indexPatterns); + const indexPatternTitle = Array.isArray(transformConfigs.transforms[0].source.index) + ? transformConfigs.transforms[0].source.index.join(',') + : transformConfigs.transforms[0].source.index; + const indexPatternId = getIndexPatternIdByTitle(indexPatternTitle); + + if (indexPatternId === undefined) { + throw new Error( + i18n.translate('xpack.transform.clone.errorPromptText', { + defaultMessage: 'Could not fetch the Kibana index pattern ID.', + }) + ); + } + + setSavedObjectId(indexPatternId); + + setTransformConfig(transformConfigs.transforms[0]); + setErrorMessage(undefined); + setIsInitialized(true); + } catch (e) { + setTransformConfig(undefined); + if (e.message !== undefined) { + setErrorMessage(e.message); + } else { + setErrorMessage(JSON.stringify(e, null, 2)); + } + setIsInitialized(true); + } + }; + + useEffect(() => { + fetchTransformConfig(); + // The effect should only be called once. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( + + + + + +

+ +   + +

+
+ + + + + +
+
+ + + {typeof errorMessage !== 'undefined' && ( + +
{JSON.stringify(errorMessage)}
+
+ )} + {savedObjectId !== undefined && isInitialized === true && transformConfig !== undefined && ( + + + + + + )} +
+
+
+ ); +}; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/index.ts b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/index.ts new file mode 100644 index 0000000000000..fef33d50130a7 --- /dev/null +++ b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/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 { CloneTransformSection } from './clone_transform_section'; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts index 3fcc3cc15803b..e5c6783db1022 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/use_source_index_data.ts @@ -17,6 +17,7 @@ import { getDefaultSelectableFields, getFlattenedFields, isDefaultQuery, + matchAllQuery, EsDoc, EsDocSource, EsFieldName, @@ -75,7 +76,7 @@ export const useSourceIndexData = ( index: indexPattern.title, size: SEARCH_SIZE, // Instead of using the default query (`*`), fall back to a more efficient `match_all` query. - body: { query: isDefaultQuery(query) ? { match_all: {} } : query }, + body: { query: isDefaultQuery(query) ? matchAllQuery : query }, }); if (isErrorResponse(resp)) { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts index 7c5b60715961b..881e8c6b26658 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/index.ts @@ -5,8 +5,9 @@ */ export { + applyTransformConfigToDefineState, + getDefaultStepDefineState, StepDefineExposedState, StepDefineForm, - getDefaultStepDefineState, } from './step_define_form'; export { StepDefineSummary } from './step_define_summary'; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index b8f63ef697e78..675386be8e2a5 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEqual } from 'lodash'; import React, { Fragment, FC, useEffect, useState } from 'react'; import { i18n } from '@kbn/i18n'; @@ -27,7 +28,8 @@ import { EuiSwitch, } from '@elastic/eui'; -import { dictionaryToArray } from '../../../../../../common/types/common'; +import { TransformPivotConfig } from '../../../../common'; +import { dictionaryToArray, Dictionary } from '../../../../../../common/types/common'; import { DropDown } from '../aggregation_dropdown'; import { AggListForm } from '../aggregation_list'; import { GroupByListForm } from '../group_by_list'; @@ -43,10 +45,12 @@ import { } from '../../../../lib/kibana'; import { - AggName, - DropDownLabel, getPivotQuery, getPreviewRequestBody, + isMatchAllQuery, + matchAllQuery, + AggName, + DropDownLabel, PivotAggDict, PivotAggsConfig, PivotAggsConfigDict, @@ -55,6 +59,7 @@ import { PivotGroupByConfigDict, PivotSupportedGroupByAggs, PIVOT_SUPPORTED_AGGS, + PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../common'; import { getPivotDropdownOptions } from './common'; @@ -89,6 +94,58 @@ export function getDefaultStepDefineState( valid: false, }; } + +export function applyTransformConfigToDefineState( + state: StepDefineExposedState, + transformConfig?: TransformPivotConfig +): StepDefineExposedState { + // apply the transform configuration to wizard DEFINE state + if (transformConfig !== undefined) { + // transform aggregations config to wizard state + state.aggList = Object.keys(transformConfig.pivot.aggregations).reduce((aggList, aggName) => { + const aggConfig = transformConfig.pivot.aggregations[aggName] as Dictionary; + const agg = Object.keys(aggConfig)[0]; + aggList[aggName] = { + ...aggConfig[agg], + agg: agg as PIVOT_SUPPORTED_AGGS, + aggName, + dropDownName: aggName, + } as PivotAggsConfig; + return aggList; + }, {} as PivotAggsConfigDict); + + // transform group by config to wizard state + state.groupByList = Object.keys(transformConfig.pivot.group_by).reduce( + (groupByList, groupByName) => { + const groupByConfig = transformConfig.pivot.group_by[groupByName] as Dictionary; + const groupBy = Object.keys(groupByConfig)[0]; + groupByList[groupByName] = { + agg: groupBy as PIVOT_SUPPORTED_GROUP_BY_AGGS, + aggName: groupByName, + dropDownName: groupByName, + ...groupByConfig[groupBy], + } as PivotGroupByConfig; + return groupByList; + }, + {} as PivotGroupByConfigDict + ); + + // only apply the query from the transform config to wizard state if it's not the default query + const query = transformConfig.source.query; + if (query !== undefined && !isEqual(query, matchAllQuery)) { + state.isAdvancedSourceEditorEnabled = true; + state.searchString = ''; + state.searchQuery = query; + state.sourceConfigUpdated = true; + } + + // applying a transform config to wizard state will always result in a valid configuration + state.valid = true; + } + + return state; +} + export function isAggNameConflict( aggName: AggName, aggList: PivotAggsConfigDict, @@ -208,10 +265,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange const searchHandler = (d: Record) => { const { filterQuery, queryString } = d; const newSearch = queryString === emptySearch ? defaultSearch : queryString; - const newSearchQuery = - filterQuery.match_all && Object.keys(filterQuery.match_all).length === 0 - ? defaultSearch - : filterQuery; + const newSearchQuery = isMatchAllQuery(filterQuery) ? defaultSearch : filterQuery; setSearchString(newSearch); setSearchQuery(newSearchQuery); }; @@ -363,10 +417,10 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange const aggConfigKeys = Object.keys(aggConfig); const agg = aggConfigKeys[0] as PivotSupportedGroupByAggs; newGroupByList[aggName] = { + ...aggConfig[agg], agg, aggName, dropDownName: '', - ...aggConfig[agg], }; }); } @@ -380,10 +434,10 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange const aggConfigKeys = Object.keys(aggConfig); const agg = aggConfigKeys[0] as PIVOT_SUPPORTED_AGGS; newAggList[aggName] = { + ...aggConfig[agg], agg, aggName, dropDownName: '', - ...aggConfig[agg], }; }); } diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts index e454ea32d76ed..5cbdf4500e3c3 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/index.ts @@ -4,5 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export { StepDetailsForm, getDefaultStepDetailsState } from './step_details_form'; +export { + applyTransformConfigToDetailsState, + getDefaultStepDetailsState, + StepDetailsForm, +} from './step_details_form'; export { StepDetailsSummary } from './step_details_summary'; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index a01481fde343c..220923f88ed36 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -49,6 +49,22 @@ export function getDefaultStepDetailsState(): StepDetailsExposedState { }; } +export function applyTransformConfigToDetailsState( + state: StepDetailsExposedState, + transformConfig?: TransformPivotConfig +): StepDetailsExposedState { + // apply the transform configuration to wizard DETAILS state + if (transformConfig !== undefined) { + const time = transformConfig.sync?.time; + if (time !== undefined) { + state.continuousModeDateField = time.field; + state.continuousModeDelay = time.delay; + state.isContinuousModeEnabled = true; + } + } + return state; +} + interface Props { overrides?: StepDetailsExposedState; onChange(s: StepDetailsExposedState): void; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx index 109cf81da6caa..f1861755d9742 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx @@ -12,16 +12,22 @@ import { EuiSteps, EuiStepStatus } from '@elastic/eui'; import { useKibanaContext } from '../../../../lib/kibana'; -import { getCreateRequestBody } from '../../../../common'; +import { getCreateRequestBody, TransformPivotConfig } from '../../../../common'; import { + applyTransformConfigToDefineState, + getDefaultStepDefineState, StepDefineExposedState, StepDefineForm, StepDefineSummary, - getDefaultStepDefineState, } from '../step_define'; import { getDefaultStepCreateState, StepCreateForm, StepCreateSummary } from '../step_create'; -import { getDefaultStepDetailsState, StepDetailsForm, StepDetailsSummary } from '../step_details'; +import { + applyTransformConfigToDetailsState, + getDefaultStepDetailsState, + StepDetailsForm, + StepDetailsSummary, +} from '../step_details'; import { WizardNav } from '../wizard_nav'; enum KBN_MANAGEMENT_PAGE_CLASSNAME { @@ -67,17 +73,25 @@ const StepDefine: FC = ({ ); }; -export const Wizard: FC = React.memo(() => { +interface WizardProps { + cloneConfig?: TransformPivotConfig; +} + +export const Wizard: FC = React.memo(({ cloneConfig }) => { const kibanaContext = useKibanaContext(); // The current WIZARD_STEP const [currentStep, setCurrentStep] = useState(WIZARD_STEPS.DEFINE); // The DEFINE state - const [stepDefineState, setStepDefineState] = useState(getDefaultStepDefineState(kibanaContext)); + const [stepDefineState, setStepDefineState] = useState( + applyTransformConfigToDefineState(getDefaultStepDefineState(kibanaContext), cloneConfig) + ); // The DETAILS state - const [stepDetailsState, setStepDetailsState] = useState(getDefaultStepDetailsState()); + const [stepDetailsState, setStepDetailsState] = useState( + applyTransformConfigToDetailsState(getDefaultStepDetailsState(), cloneConfig) + ); const stepDetails = currentStep === WIZARD_STEPS.DETAILS ? ( diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx new file mode 100644 index 0000000000000..40098ac7ef72a --- /dev/null +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_clone.tsx @@ -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 React, { FC, useContext } from 'react'; +import { useHistory } from 'react-router-dom'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui'; + +import { + createCapabilityFailureMessage, + AuthorizationContext, +} from '../../../../lib/authorization'; + +import { CLIENT_BASE_PATH, SECTION_SLUG } from '../../../../constants'; + +interface CloneActionProps { + itemId: string; +} + +export const CloneAction: FC = ({ itemId }) => { + const history = useHistory(); + + const { canCreateTransform } = useContext(AuthorizationContext).capabilities; + + const buttonCloneText = i18n.translate('xpack.transform.transformList.cloneActionName', { + defaultMessage: 'Clone', + }); + + function clickHandler() { + history.push(`${CLIENT_BASE_PATH}/${SECTION_SLUG.CLONE_TRANSFORM}/${itemId}`); + } + + const cloneButton = ( + + {buttonCloneText} + + ); + + if (!canCreateTransform) { + const content = createCapabilityFailureMessage('canStartStopTransform'); + + return ( + + {cloneButton} + + ); + } + + return <>{cloneButton}; +}; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx index 3d847890b2bd5..ef92a5e3859d7 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx @@ -12,9 +12,10 @@ describe('Transform: Transform List Actions', () => { test('getActions()', () => { const actions = getActions({ forceDisable: false }); - expect(actions).toHaveLength(2); + expect(actions).toHaveLength(3); expect(actions[0].isPrimary).toBeTruthy(); expect(typeof actions[0].render).toBe('function'); expect(typeof actions[1].render).toBe('function'); + expect(typeof actions[2].render).toBe('function'); }); }); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx index 1773405e36e39..3e3829973e328 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { TransformListRow, TRANSFORM_STATE } from '../../../../common'; +import { CloneAction } from './action_clone'; import { StartAction } from './action_start'; import { StopAction } from './action_stop'; import { DeleteAction } from './action_delete'; @@ -21,6 +22,11 @@ export const getActions = ({ forceDisable }: { forceDisable: boolean }) => { return ; }, }, + { + render: (item: TransformListRow) => { + return ; + }, + }, { render: (item: TransformListRow) => { return ; diff --git a/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts b/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts index 0e0b174f28f99..5a2f698b35154 100644 --- a/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts +++ b/x-pack/legacy/plugins/transform/public/app/services/navigation/breadcrumb.ts @@ -10,6 +10,7 @@ import { linkToHome } from './links'; export enum BREADCRUMB_SECTION { MANAGEMENT = 'management', HOME = 'home', + CLONE_TRANSFORM = 'cloneTransform', CREATE_TRANSFORM = 'createTransform', } @@ -27,6 +28,7 @@ class BreadcrumbService { private breadcrumbs: Breadcrumbs = { management: [], home: [], + cloneTransform: [], createTransform: [], }; @@ -42,6 +44,12 @@ class BreadcrumbService { href: linkToHome(), }, ]; + this.breadcrumbs.cloneTransform = [ + ...this.breadcrumbs.home, + { + text: textService.breadcrumbs.cloneTransform, + }, + ]; this.breadcrumbs.createTransform = [ ...this.breadcrumbs.home, { diff --git a/x-pack/legacy/plugins/transform/public/app/services/text/text.ts b/x-pack/legacy/plugins/transform/public/app/services/text/text.ts index df1b07e171c62..af4aea7e8db4e 100644 --- a/x-pack/legacy/plugins/transform/public/app/services/text/text.ts +++ b/x-pack/legacy/plugins/transform/public/app/services/text/text.ts @@ -14,6 +14,9 @@ class TextService { home: i18n.translate('xpack.transform.home.breadcrumbTitle', { defaultMessage: 'Transforms', }), + cloneTransform: i18n.translate('xpack.transform.cloneTransform.breadcrumbTitle', { + defaultMessage: 'Clone transform', + }), createTransform: i18n.translate('xpack.transform.createTransform.breadcrumbTitle', { defaultMessage: 'Create transform', }), From 68e79e45a5f49d749ea6d0ef1f039b914cbc6bf9 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Fri, 21 Feb 2020 15:35:20 +0100 Subject: [PATCH 2/7] Trigger context (#57870) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 annotate with comments Trigger and add createContext( * feat: 🎸 add TriggerContext type * feat: 🎸 improve trigger setup in embeddable plugin * feat: 🎸 export trigger vars and types from embeddable * feat: 🎸 add internal representation of Trigger * feat: 🎸 use new trigger interface to execute in vis embeddable * feat: 🎸 add TriggerContextMapping interface * feat: 🎸 improve trigger types * refactor: 💡 remove trigger ID getter * chore: 🤖 remove commented out line * chore: 🤖 re-export more trigger stuff * refactor: 💡 simplify Trigger interface * suggestions * Remove unused type Co-authored-by: Stacey Gammon --- .../public/embeddable/visualize_embeddable.ts | 14 ++-- src/plugins/embeddable/public/bootstrap.ts | 71 ++++++++--------- src/plugins/embeddable/public/index.ts | 26 ++++--- .../embeddable/public/lib/triggers/index.ts | 6 +- .../public/lib/triggers/triggers.ts | 65 ++++++++++++++++ src/plugins/ui_actions/public/index.ts | 3 +- .../public/service/ui_actions_service.test.ts | 5 +- .../public/service/ui_actions_service.ts | 65 ++++++---------- .../ui_actions/public/triggers/index.ts | 4 +- .../ui_actions/public/triggers/trigger.ts | 31 +++++++- .../public/triggers/trigger_contract.ts | 56 ++++++++++++++ .../public/triggers/trigger_internal.ts | 76 +++++++++++++++++++ src/plugins/ui_actions/public/types.ts | 10 ++- 13 files changed, 321 insertions(+), 111 deletions(-) create mode 100644 src/plugins/embeddable/public/lib/triggers/triggers.ts create mode 100644 src/plugins/ui_actions/public/triggers/trigger_contract.ts create mode 100644 src/plugins/ui_actions/public/triggers/trigger_internal.ts 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 4c6c12f825609..72a0ef72b5693 100644 --- a/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/legacy/core_plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -24,6 +24,7 @@ import * as Rx from 'rxjs'; import { buildPipeline } from 'ui/visualize/loader/pipeline_helpers'; import { npStart } from 'ui/new_platform'; import { IExpressionLoaderParams } from 'src/plugins/expressions/public'; +import { EmbeddableVisTriggerContext } from 'src/plugins/embeddable/public'; import { VISUALIZE_EMBEDDABLE_TYPE } from './constants'; import { IIndexPattern, @@ -39,8 +40,8 @@ import { EmbeddableOutput, Embeddable, Container, - VALUE_CLICK_TRIGGER, - SELECT_RANGE_TRIGGER, + selectRangeTrigger, + valueClickTrigger, } from '../../../../../plugins/embeddable/public'; import { dispatchRenderComplete } from '../../../../../plugins/kibana_utils/public'; import { SavedObject } from '../../../../../plugins/saved_objects/public'; @@ -301,13 +302,14 @@ export class VisualizeEmbeddable extends Embeddable { - const triggerContext: Trigger = { - id: CONTEXT_MENU_TRIGGER, - title: 'Context menu', - description: 'Triggered on top-right corner context-menu select.', - }; - const triggerFilter: Trigger = { - id: APPLY_FILTER_TRIGGER, - title: 'Filter click', - description: 'Triggered when user applies filter to an embeddable.', - }; - const triggerBadge: Trigger = { - id: PANEL_BADGE_TRIGGER, - title: 'Panel badges', - description: 'Actions appear in title bar when an embeddable loads in a panel', - }; - const selectRangeTrigger: Trigger = { - id: SELECT_RANGE_TRIGGER, - title: 'Select range', - description: 'Applies a range filter', - }; - const valueClickTrigger: Trigger = { - id: VALUE_CLICK_TRIGGER, - title: 'Value clicked', - description: 'Value was clicked', - }; + uiActions.registerTrigger(contextMenuTrigger); + uiActions.registerTrigger(applyFilterTrigger); + uiActions.registerTrigger(panelBadgeTrigger); + uiActions.registerTrigger(selectRangeTrigger); + uiActions.registerTrigger(valueClickTrigger); + const actionApplyFilter = createFilterAction(); - uiActions.registerTrigger(triggerContext); - uiActions.registerTrigger(triggerFilter); uiActions.registerAction(actionApplyFilter); - uiActions.registerTrigger(triggerBadge); - uiActions.registerTrigger(selectRangeTrigger); - uiActions.registerTrigger(valueClickTrigger); - // uiActions.attachAction(triggerFilter.id, actionApplyFilter.id); }; diff --git a/src/plugins/embeddable/public/index.ts b/src/plugins/embeddable/public/index.ts index b0e14a04a9944..2eafe16442e18 100644 --- a/src/plugins/embeddable/public/index.ts +++ b/src/plugins/embeddable/public/index.ts @@ -23,18 +23,17 @@ import { PluginInitializerContext } from 'src/core/public'; import { EmbeddablePublicPlugin } from './plugin'; export { + Adapters, ADD_PANEL_ACTION_ID, + AddPanelAction, APPLY_FILTER_ACTION, APPLY_FILTER_TRIGGER, - PANEL_BADGE_TRIGGER, - SELECT_RANGE_TRIGGER, - VALUE_CLICK_TRIGGER, - Adapters, - AddPanelAction, - CONTEXT_MENU_TRIGGER, + applyFilterTrigger, Container, ContainerInput, ContainerOutput, + CONTEXT_MENU_TRIGGER, + contextMenuTrigger, EDIT_PANEL_ACTION_ID, EditPanelAction, Embeddable, @@ -42,25 +41,32 @@ export { EmbeddableChildPanelProps, EmbeddableFactory, EmbeddableFactoryNotFoundError, + EmbeddableFactoryRenderer, EmbeddableInput, EmbeddableInstanceConfiguration, EmbeddableOutput, EmbeddablePanel, + EmbeddableRoot, + EmbeddableVisTriggerContext, ErrorEmbeddable, GetEmbeddableFactories, GetEmbeddableFactory, IContainer, IEmbeddable, + isErrorEmbeddable, + openAddPanelFlyout, OutputSpec, + PANEL_BADGE_TRIGGER, + panelBadgeTrigger, PanelNotFoundError, PanelState, PropertySpec, + SELECT_RANGE_TRIGGER, + selectRangeTrigger, + VALUE_CLICK_TRIGGER, + valueClickTrigger, ViewMode, - isErrorEmbeddable, - openAddPanelFlyout, withEmbeddableSubscription, - EmbeddableFactoryRenderer, - EmbeddableRoot, } from './lib'; export function plugin(initializerContext: PluginInitializerContext) { diff --git a/src/plugins/embeddable/public/lib/triggers/index.ts b/src/plugins/embeddable/public/lib/triggers/index.ts index 72565b3f527ad..4f981562a49ba 100644 --- a/src/plugins/embeddable/public/lib/triggers/index.ts +++ b/src/plugins/embeddable/public/lib/triggers/index.ts @@ -17,8 +17,4 @@ * under the License. */ -export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER'; -export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER'; -export const SELECT_RANGE_TRIGGER = 'SELECT_RANGE_TRIGGER'; -export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER'; -export const PANEL_BADGE_TRIGGER = 'PANEL_BADGE_TRIGGER'; +export * from './triggers'; diff --git a/src/plugins/embeddable/public/lib/triggers/triggers.ts b/src/plugins/embeddable/public/lib/triggers/triggers.ts new file mode 100644 index 0000000000000..491d9e730eb75 --- /dev/null +++ b/src/plugins/embeddable/public/lib/triggers/triggers.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 { Trigger } from '../../../../ui_actions/public'; +import { IEmbeddable } from '..'; + +export interface EmbeddableVisTriggerContext { + embeddable: IEmbeddable; + timeFieldName: string; + data: { + e: MouseEvent; + data: unknown; + }; +} + +export const SELECT_RANGE_TRIGGER = 'SELECT_RANGE_TRIGGER'; +export const selectRangeTrigger: Trigger<'SELECT_RANGE_TRIGGER'> = { + id: SELECT_RANGE_TRIGGER, + title: 'Select range', + description: 'Applies a range filter', +}; + +export const VALUE_CLICK_TRIGGER = 'VALUE_CLICK_TRIGGER'; +export const valueClickTrigger: Trigger<'VALUE_CLICK_TRIGGER'> = { + id: VALUE_CLICK_TRIGGER, + title: 'Value clicked', + description: 'Value was clicked', +}; + +export const CONTEXT_MENU_TRIGGER = 'CONTEXT_MENU_TRIGGER'; +export const contextMenuTrigger: Trigger<'CONTEXT_MENU_TRIGGER'> = { + id: CONTEXT_MENU_TRIGGER, + title: 'Context menu', + description: 'Triggered on top-right corner context-menu select.', +}; + +export const APPLY_FILTER_TRIGGER = 'FILTER_TRIGGER'; +export const applyFilterTrigger: Trigger<'FILTER_TRIGGER'> = { + id: APPLY_FILTER_TRIGGER, + title: 'Filter click', + description: 'Triggered when user applies filter to an embeddable.', +}; + +export const PANEL_BADGE_TRIGGER = 'PANEL_BADGE_TRIGGER'; +export const panelBadgeTrigger: Trigger<'PANEL_BADGE_TRIGGER'> = { + id: PANEL_BADGE_TRIGGER, + title: 'Panel badges', + description: 'Actions appear in title bar when an embeddable loads in a panel', +}; diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 83a08b11fa4c2..1ce48d5460b2e 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -29,7 +29,8 @@ export { UiActionsSetup, UiActionsStart } from './plugin'; export { UiActionsServiceParams, UiActionsService } from './service'; export { Action, createAction, IncompatibleActionError } from './actions'; export { buildContextMenuForActions } from './context_menu'; -export { Trigger } from './triggers'; +export { Trigger, TriggerContext } from './triggers'; +export { TriggerContextMapping } from './types'; /** * @deprecated 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 index 2bbe106c49a25..8963ba4ddb005 100644 --- a/src/plugins/ui_actions/public/service/ui_actions_service.test.ts +++ b/src/plugins/ui_actions/public/service/ui_actions_service.test.ts @@ -68,7 +68,7 @@ describe('UiActionsService', () => { const trigger = service.getTrigger('bar'); - expect(trigger).toEqual({ + expect(trigger).toMatchObject({ description: 'foo', id: 'bar', title: 'baz', @@ -345,8 +345,9 @@ describe('UiActionsService', () => { id: 'bar', title: 'baz', }); + const triggerContract = service.getTrigger('bar'); - expect(triggers.get('bar')).toEqual({ + expect(triggerContract).toMatchObject({ description: 'foo', id: 'bar', title: 'baz', diff --git a/src/plugins/ui_actions/public/service/ui_actions_service.ts b/src/plugins/ui_actions/public/service/ui_actions_service.ts index a62d2aa356435..ae409830bbb6e 100644 --- a/src/plugins/ui_actions/public/service/ui_actions_service.ts +++ b/src/plugins/ui_actions/public/service/ui_actions_service.ts @@ -17,10 +17,11 @@ * under the License. */ -import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry } from '../types'; +import { TriggerRegistry, ActionRegistry, TriggerToActionsRegistry, TriggerId } from '../types'; import { Action } from '../actions'; -import { Trigger } from '../triggers/trigger'; -import { buildContextMenuForActions, openContextMenu } from '../context_menu'; +import { Trigger, TriggerContext } from '../triggers/trigger'; +import { TriggerInternal } from '../triggers/trigger_internal'; +import { TriggerContract } from '../triggers/trigger_contract'; export interface UiActionsServiceParams { readonly triggers?: TriggerRegistry; @@ -52,18 +53,20 @@ export class UiActionsService { throw new Error(`Trigger [trigger.id = ${trigger.id}] already registered.`); } - this.triggers.set(trigger.id, trigger); + const triggerInternal = new TriggerInternal(this, trigger); + + this.triggers.set(trigger.id, triggerInternal); this.triggerToActions.set(trigger.id, []); }; - public readonly getTrigger = (id: string) => { - const trigger = this.triggers.get(id); + public readonly getTrigger = (triggerId: T): TriggerContract => { + const trigger = this.triggers.get(triggerId as string); if (!trigger) { - throw new Error(`Trigger [triggerId = ${id}] does not exist.`); + throw new Error(`Trigger [triggerId = ${triggerId}] does not exist.`); } - return trigger; + return trigger.contract; }; public readonly registerAction = (action: Action) => { @@ -128,41 +131,17 @@ export class UiActionsService { ); }; - 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); + /** + * @deprecated + * + * Use `plugins.uiActions.getTrigger(triggerId).exec(params)` instead. + */ + public readonly executeTriggerActions = async ( + triggerId: T, + context: TriggerContext + ) => { + const trigger = this.getTrigger(triggerId); + await trigger.exec(context); }; /** diff --git a/src/plugins/ui_actions/public/triggers/index.ts b/src/plugins/ui_actions/public/triggers/index.ts index a34c6eda61ba0..1ae2a19c4001f 100644 --- a/src/plugins/ui_actions/public/triggers/index.ts +++ b/src/plugins/ui_actions/public/triggers/index.ts @@ -17,4 +17,6 @@ * under the License. */ -export { Trigger } from './trigger'; +export * from './trigger'; +export * from './trigger_contract'; +export * from './trigger_internal'; diff --git a/src/plugins/ui_actions/public/triggers/trigger.ts b/src/plugins/ui_actions/public/triggers/trigger.ts index ba83f5619e250..2c019b09881d1 100644 --- a/src/plugins/ui_actions/public/triggers/trigger.ts +++ b/src/plugins/ui_actions/public/triggers/trigger.ts @@ -17,8 +17,35 @@ * under the License. */ -export interface Trigger { - id: string; +import { TriggerContextMapping, TriggerId } from '../types'; + +/** + * This is a convenience interface used to register a *trigger*. + * + * `Trigger` specifies a named anchor to which `Action` can be attached. When + * `Trigger` is being *called* it creates a `Context` object and passes it to + * the `execute` method of an `Action`. + * + * More than one action can be attached to a single trigger, in which case when + * trigger is *called* it first displays a context menu for user to pick a + * single action to execute. + */ +export interface Trigger { + /** + * Unique name of the trigger as identified in `ui_actions` plugin trigger + * registry, such as "SELECT_RANGE_TRIGGER" or "VALUE_CLICK_TRIGGER". + */ + id: ID; + + /** + * User friendly name of the trigger. + */ title?: string; + + /** + * A longer user friendly description of the trigger. + */ description?: string; } + +export type TriggerContext = T extends TriggerId ? TriggerContextMapping[T] : never; diff --git a/src/plugins/ui_actions/public/triggers/trigger_contract.ts b/src/plugins/ui_actions/public/triggers/trigger_contract.ts new file mode 100644 index 0000000000000..853b83dccabcc --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/trigger_contract.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 { TriggerContext } from './trigger'; +import { TriggerInternal } from './trigger_internal'; +import { TriggerId } from '../types'; + +/** + * This is a public representation of a trigger that is provided to other plugins. + */ +export class TriggerContract { + /** + * Unique name of the trigger as identified in `ui_actions` plugin trigger + * registry, such as "SELECT_RANGE_TRIGGER" or "VALUE_CLICK_TRIGGER". + */ + public readonly id: T; + + /** + * User friendly name of the trigger. + */ + public readonly title?: string; + + /** + * A longer user friendly description of the trigger. + */ + public readonly description?: string; + + constructor(private readonly internal: TriggerInternal) { + this.id = this.internal.trigger.id; + this.title = this.internal.trigger.title; + this.description = this.internal.trigger.description; + } + + /** + * Use this method to execute action attached to this trigger. + */ + public readonly exec = async (context: TriggerContext) => { + await this.internal.execute(context); + }; +} diff --git a/src/plugins/ui_actions/public/triggers/trigger_internal.ts b/src/plugins/ui_actions/public/triggers/trigger_internal.ts new file mode 100644 index 0000000000000..efcdc72ecad57 --- /dev/null +++ b/src/plugins/ui_actions/public/triggers/trigger_internal.ts @@ -0,0 +1,76 @@ +/* + * 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 { TriggerContext, Trigger } from './trigger'; +import { TriggerContract } from './trigger_contract'; +import { UiActionsService } from '../service'; +import { Action } from '../actions'; +import { buildContextMenuForActions, openContextMenu } from '../context_menu'; +import { TriggerId } from '../types'; + +/** + * Internal representation of a trigger kept for consumption only internally + * within `ui_actions` plugin. + */ +export class TriggerInternal { + public readonly contract = new TriggerContract(this); + + constructor(public readonly service: UiActionsService, public readonly trigger: Trigger) {} + + public async execute(context: TriggerContext) { + const triggerId = this.trigger.id; + const actions = await this.service.getTriggerCompatibleActions!(triggerId, context); + + 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], context); + return; + } + + await this.executeMultipleActions(actions, context); + } + + private async executeSingleAction(action: Action>, context: TriggerContext) { + const href = action.getHref && action.getHref(context); + + if (href) { + window.location.href = href; + return; + } + + await action.execute(context); + } + + private async executeMultipleActions( + actions: Array>>, + context: TriggerContext + ) { + const panel = await buildContextMenuForActions({ + actions, + actionContext: context, + closeMenu: () => session.close(), + }); + const session = openContextMenu([panel]); + } +} diff --git a/src/plugins/ui_actions/public/types.ts b/src/plugins/ui_actions/public/types.ts index 9bd6ffdef2af3..8daa893eb4347 100644 --- a/src/plugins/ui_actions/public/types.ts +++ b/src/plugins/ui_actions/public/types.ts @@ -18,8 +18,14 @@ */ import { Action } from './actions/action'; -import { Trigger } from './triggers/trigger'; +import { TriggerInternal } from './triggers/trigger_internal'; -export type TriggerRegistry = Map; +export type TriggerRegistry = Map>; export type ActionRegistry = Map; export type TriggerToActionsRegistry = Map; + +export type TriggerId = string; + +export interface TriggerContextMapping { + [key: string]: object; +} From c91b6ceebcec85cf0991da555ba94605e5fcb029 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Fri, 21 Feb 2020 07:27:40 -0800 Subject: [PATCH 3/7] Updating to @elastic/lodash@3.10.1-kibana4 (#54662) Co-authored-by: Elastic Machine --- package.json | 2 +- packages/kbn-interpreter/package.json | 2 +- packages/kbn-ui-framework/package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 8 ++++---- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index b11234b6312e9..cb87c48ab7f2a 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "leaflet.heat": "0.2.0", "less": "^2.7.3", "less-loader": "5.0.0", - "lodash": "npm:@elastic/lodash@3.10.1-kibana3", + "lodash": "npm:@elastic/lodash@3.10.1-kibana4", "lodash.clonedeep": "^4.5.0", "lru-cache": "4.1.5", "markdown-it": "^10.0.0", diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index d2f0b0c358284..5dede7fbf1aaa 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -11,7 +11,7 @@ "dependencies": { "@babel/runtime": "^7.5.5", "@kbn/i18n": "1.0.0", - "lodash": "npm:@elastic/lodash@3.10.1-kibana3", + "lodash": "npm:@elastic/lodash@3.10.1-kibana4", "lodash.clone": "^4.5.0", "uuid": "3.3.2" }, diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index fc245ca3fe921..0402a83d3d274 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -17,7 +17,7 @@ "dependencies": { "classnames": "2.2.6", "focus-trap-react": "^3.1.1", - "lodash": "npm:@elastic/lodash@3.10.1-kibana3", + "lodash": "npm:@elastic/lodash@3.10.1-kibana4", "prop-types": "15.6.0", "react": "^16.12.0", "react-ace": "^5.9.0", diff --git a/x-pack/package.json b/x-pack/package.json index 9d6b5d76a58e7..551e466893f93 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -262,7 +262,7 @@ "json-stable-stringify": "^1.0.1", "jsonwebtoken": "^8.5.1", "jsts": "^1.6.2", - "lodash": "npm:@elastic/lodash@3.10.1-kibana3", + "lodash": "npm:@elastic/lodash@3.10.1-kibana4", "lodash.keyby": "^4.6.0", "lodash.mean": "^4.1.0", "lodash.topath": "^4.5.2", diff --git a/yarn.lock b/yarn.lock index 6b3370407e3b2..2caaac974dfad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19812,10 +19812,10 @@ lodash@^3.10.1: resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= -"lodash@npm:@elastic/lodash@3.10.1-kibana3": - version "3.10.1-kibana3" - resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana3.tgz#c0e318245219eeeff535895c429e0cef5058a9ad" - integrity sha512-HMfwwT2yAkEQNzHSR1BxgE5YcDMUaZ/skhNyjy1nvM/A4m0Kh940hLZeCqKBCsSaUJz/8A/9cQGd9BaAOCIBLg== +"lodash@npm:@elastic/lodash@3.10.1-kibana4": + version "3.10.1-kibana4" + resolved "https://registry.yarnpkg.com/@elastic/lodash/-/lodash-3.10.1-kibana4.tgz#d491228fd659b4a1b0dfa08ba9c67a4979b9746d" + integrity sha512-geQqXd9ZedRCL+kq5cpeahYWYaYRV0BMXhCwzq4DpnGCVs430FTMS3Wcot3XChZZhCvkwHm15bpNjB312vPxaA== log-ok@^0.1.1: version "0.1.1" From 38988dae7118b0e71db6c3cf73d87ca9e816ee5c Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 21 Feb 2020 16:54:53 +0100 Subject: [PATCH 4/7] Do not refresh color scale on each lookup (#57792) --- .../public/components/__tests__/tag_cloud.js | 26 +++++++++---------- .../public/components/tag_cloud.js | 7 +++-- .../components/tag_cloud_visualization.js | 4 ++- .../new_platform/new_platform.karma_mock.js | 3 +++ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js index 136fe51674bf1..152efe5667f18 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/__tests__/tag_cloud.js @@ -30,10 +30,6 @@ import simpleloadPng from './simpleload.png'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { seedColors } from '../../../../../../plugins/charts/public/services/colors/seed_colors'; -const colors = { - seedColors, -}; - describe('tag cloud tests', function() { const minValue = 1; const maxValue = 9; @@ -102,6 +98,8 @@ describe('tag cloud tests', function() { let domNode; let tagCloud; + const colorScale = d3.scale.ordinal().range(seedColors); + function setupDOM() { domNode = document.createElement('div'); domNode.style.top = '0'; @@ -132,7 +130,7 @@ describe('tag cloud tests', function() { )}`, function() { beforeEach(async function() { setupDOM(); - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(test.data); tagCloud.setOptions(test.options); await fromNode(cb => tagCloud.once('renderComplete', cb)); @@ -164,7 +162,7 @@ describe('tag cloud tests', function() { //TagCloud takes at least 600ms to complete (due to d3 animation) //renderComplete should only notify at the last one - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); @@ -196,7 +194,7 @@ describe('tag cloud tests', function() { describe('should use the latest state before notifying (when modifying options multiple times)', function() { beforeEach(async function() { setupDOM(); - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); tagCloud.setOptions(logScaleTest.options); @@ -223,7 +221,7 @@ describe('tag cloud tests', function() { describe('should use the latest state before notifying (when modifying data multiple times)', function() { beforeEach(async function() { setupDOM(); - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); tagCloud.setData(trimDataTest.data); @@ -253,7 +251,7 @@ describe('tag cloud tests', function() { counter = 0; setupDOM(); return new Promise((resolve, reject) => { - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); @@ -299,7 +297,7 @@ describe('tag cloud tests', function() { describe('should show correct data when state-updates are interleaved with resize event', function() { beforeEach(async function() { setupDOM(); - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(logScaleTest.data); tagCloud.setOptions(logScaleTest.options); @@ -337,7 +335,7 @@ describe('tag cloud tests', function() { setupDOM(); domNode.style.width = '1px'; domNode.style.height = '1px'; - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); await fromNode(cb => tagCloud.once('renderComplete', cb)); @@ -363,7 +361,7 @@ describe('tag cloud tests', function() { domNode.style.width = '1px'; domNode.style.height = '1px'; - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); await fromNode(cb => tagCloud.once('renderComplete', cb)); @@ -388,7 +386,7 @@ describe('tag cloud tests', function() { describe(`tags should no longer fit after making container smaller`, function() { beforeEach(async function() { setupDOM(); - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); await fromNode(cb => tagCloud.once('renderComplete', cb)); @@ -420,7 +418,7 @@ describe('tag cloud tests', function() { }); it('should render simple image', async function() { - tagCloud = new TagCloud(domNode, colors); + tagCloud = new TagCloud(domNode, colorScale); tagCloud.setData(baseTest.data); tagCloud.setOptions(baseTest.options); diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js index f5084fd92cfee..fae7cdf797958 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud.js @@ -37,7 +37,7 @@ const D3_SCALING_FUNCTIONS = { }; export class TagCloud extends EventEmitter { - constructor(domNode, colors) { + constructor(domNode, colorScale) { super(); //DOM @@ -54,7 +54,6 @@ export class TagCloud extends EventEmitter { this._spiral = 'archimedean'; //layout shape this._timeInterval = 1000; //time allowed for layout algorithm this._padding = 5; - this._seedColors = colors.seedColors; //OPTIONS this._orientation = 'single'; @@ -67,6 +66,7 @@ export class TagCloud extends EventEmitter { this._words = null; //UTIL + this._colorScale = colorScale; this._setTimeoutId = null; this._pendingJob = null; this._layoutIsUpdating = null; @@ -371,8 +371,7 @@ export class TagCloud extends EventEmitter { } getFill(tag) { - const colorScale = d3.scale.ordinal().range(this._seedColors); - return colorScale(tag.text); + return this._colorScale(tag.text); } } diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js index 5528278adf4eb..114643c9a74e0 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_visualization.js @@ -28,10 +28,12 @@ import { getFormat } from '../legacy_imports'; import { Label } from './label'; import { TagCloud } from './tag_cloud'; import { FeedbackMessage } from './feedback_message'; +import d3 from 'd3'; const MAX_TAG_COUNT = 200; export function createTagCloudVisualization({ colors }) { + const colorScale = d3.scale.ordinal().range(colors.seedColors); return class TagCloudVisualization { constructor(node, vis) { this._containerNode = node; @@ -48,7 +50,7 @@ export function createTagCloudVisualization({ colors }) { this._vis = vis; this._truncated = false; - this._tagCloud = new TagCloud(cloudContainer, colors); + this._tagCloud = new TagCloud(cloudContainer, colorScale); this._tagCloud.on('select', event => { if (!this._visParams.bucket) { return; 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 38b3434ef9c48..cf8537ba7ab3e 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 @@ -218,6 +218,9 @@ export const npSetup = { chartsTheme$: mockObservable, useChartsTheme: sinon.fake(), }, + colors: { + seedColors: ['white', 'black'], + }, }, management: { sections: { From 8686fc99dc841f0c3463cb80da53c83f0d24763c Mon Sep 17 00:00:00 2001 From: Spencer Date: Fri, 21 Feb 2020 09:14:28 -0700 Subject: [PATCH 5/7] [kbn/optimizer] include bootstrap cache key in optimizer cache key (#58176) * [kbn/optimizer] include bootstrap cache key in optimizer cache key * remove cache buster * update cache keys tests * move mocks --- packages/kbn-optimizer/src/index.ts | 1 - .../src/optimizer/cache_keys.test.ts | 52 ++++++++++++++----- .../kbn-optimizer/src/optimizer/cache_keys.ts | 43 ++++++++++++--- 3 files changed, 75 insertions(+), 21 deletions(-) diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 9798391d47da4..48777f1d54aaf 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -17,7 +17,6 @@ * under the License. */ -// cache buster - https://github.com/elastic/kibana/issues/58077 - 1 export { OptimizerConfig } from './optimizer'; export * from './run_optimizer'; export * from './log_optimizer_state'; diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts index 44234acd897dc..2337017f54ed8 100644 --- a/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts +++ b/packages/kbn-optimizer/src/optimizer/cache_keys.test.ts @@ -17,14 +17,35 @@ * under the License. */ +import Path from 'path'; + import jestDiff from 'jest-diff'; import { REPO_ROOT, createAbsolutePathSerializer } from '@kbn/dev-utils'; import { reformatJestDiff, getOptimizerCacheKey, diffCacheKey } from './cache_keys'; import { OptimizerConfig } from './optimizer_config'; -jest.mock('./get_changes.ts'); +jest.mock('./get_changes.ts', () => ({ + getChanges: async () => + new Map([ + ['/foo/bar/a', 'modified'], + ['/foo/bar/b', 'modified'], + ['/foo/bar/c', 'deleted'], + ]), +})); + +jest.mock('./get_mtimes.ts', () => ({ + getMtimes: async (paths: string[]) => new Map(paths.map(path => [path, 12345])), +})); + jest.mock('execa'); + +jest.mock('fs', () => { + const realFs = jest.requireActual('fs'); + jest.spyOn(realFs, 'readFile'); + return realFs; +}); + expect.addSnapshotSerializer(createAbsolutePathSerializer()); jest.requireMock('execa').mockImplementation(async (cmd: string, args: string[], opts: object) => { @@ -46,28 +67,35 @@ jest.requireMock('execa').mockImplementation(async (cmd: string, args: string[], }; }); -jest.requireMock('./get_changes.ts').getChanges.mockImplementation( - async () => - new Map([ - ['/foo/bar/a', 'modified'], - ['/foo/bar/b', 'modified'], - ['/foo/bar/c', 'deleted'], - ]) -); - describe('getOptimizerCacheKey()', () => { - it('uses latest commit and changes files to create unique value', async () => { + it('uses latest commit, bootstrap cache, and changed files to create unique value', async () => { + jest + .requireMock('fs') + .readFile.mockImplementation( + (path: string, enc: string, cb: (err: null, file: string) => void) => { + expect(path).toBe( + Path.resolve(REPO_ROOT, 'packages/kbn-optimizer/target/.bootstrap-cache') + ); + expect(enc).toBe('utf8'); + cb(null, ''); + } + ); + const config = OptimizerConfig.create({ repoRoot: REPO_ROOT, }); await expect(getOptimizerCacheKey(config)).resolves.toMatchInlineSnapshot(` Object { + "bootstrap": "", "deletedPaths": Array [ "/foo/bar/c", ], "lastCommit": "", - "modifiedPaths": Object {}, + "modifiedTimes": Object { + "/foo/bar/a": 12345, + "/foo/bar/b": 12345, + }, "workerConfig": Object { "browserslistEnv": "dev", "cache": true, diff --git a/packages/kbn-optimizer/src/optimizer/cache_keys.ts b/packages/kbn-optimizer/src/optimizer/cache_keys.ts index 3529ffa587f16..af6a8a648d29c 100644 --- a/packages/kbn-optimizer/src/optimizer/cache_keys.ts +++ b/packages/kbn-optimizer/src/optimizer/cache_keys.ts @@ -18,6 +18,8 @@ */ import Path from 'path'; +import Fs from 'fs'; +import { promisify } from 'util'; import Chalk from 'chalk'; import execa from 'execa'; @@ -116,9 +118,10 @@ export function reformatJestDiff(diff: string | null) { export interface OptimizerCacheKey { readonly lastCommit: string | undefined; + readonly bootstrap: string | undefined; readonly workerConfig: WorkerConfig; readonly deletedPaths: string[]; - readonly modifiedPaths: Record; + readonly modifiedTimes: Record; } async function getLastCommit() { @@ -133,21 +136,45 @@ async function getLastCommit() { return stdout.trim() || undefined; } +async function getBootstrapCacheKey() { + try { + return await promisify(Fs.readFile)( + Path.resolve(OPTIMIZER_DIR, 'target/.bootstrap-cache'), + 'utf8' + ); + } catch (error) { + if (error?.code !== 'ENOENT') { + throw error; + } + return undefined; + } +} + export async function getOptimizerCacheKey(config: OptimizerConfig) { - const changes = Array.from((await getChanges(OPTIMIZER_DIR)).entries()); + const [changes, lastCommit, bootstrap] = await Promise.all([ + getChanges(OPTIMIZER_DIR), + getLastCommit(), + getBootstrapCacheKey(), + ] as const); + + const deletedPaths: string[] = []; + const modifiedPaths: string[] = []; + for (const [path, type] of changes) { + (type === 'deleted' ? deletedPaths : modifiedPaths).push(path); + } const cacheKeys: OptimizerCacheKey = { - lastCommit: await getLastCommit(), workerConfig: config.getWorkerConfig('â™»'), - deletedPaths: changes.filter(e => e[1] === 'deleted').map(e => e[0]), - modifiedPaths: {} as Record, + lastCommit, + bootstrap, + deletedPaths, + modifiedTimes: {} as Record, }; - const modified = changes.filter(e => e[1] === 'modified').map(e => e[0]); - const mtimes = await getMtimes(modified); + const mtimes = await getMtimes(modifiedPaths); for (const [path, mtime] of Array.from(mtimes.entries()).sort(ascending(e => e[0]))) { if (typeof mtime === 'number') { - cacheKeys.modifiedPaths[path] = mtime; + cacheKeys.modifiedTimes[path] = mtime; } } From fb04b7afb4cd644e68fa4bb3a5f1da60f799ae35 Mon Sep 17 00:00:00 2001 From: Charlie Pichette <56399229+charlie-pichette@users.noreply.github.com> Date: Fri, 21 Feb 2020 11:44:57 -0500 Subject: [PATCH 6/7] [Endpoint] Refactor Management List Tests (#58148) * endpoint-161-refactor-management-list-test * fix location of es archive file --- .../functional/apps/endpoint/management.ts | 68 +++++++++++++++---- .../endpoint/metadata/api_feature/data.json | 6 +- .../functional/page_objects/endpoint_page.ts | 27 +++++++- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/x-pack/test/functional/apps/endpoint/management.ts b/x-pack/test/functional/apps/endpoint/management.ts index 500185182f0d8..4925fa7678ab0 100644 --- a/x-pack/test/functional/apps/endpoint/management.ts +++ b/x-pack/test/functional/apps/endpoint/management.ts @@ -25,19 +25,61 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('displays table data', async () => { - const data = await pageObjects.endpoint.getManagementTableData(); - [ - 'Hostnamecadmann-4.example.com', - 'PolicyPolicy Name', - 'Policy StatusPolicy Status', - 'Alerts0', - 'Operating Systemwindows 10.0', - 'IP Address10.192.213.130, 10.70.28.129', - 'Sensor Versionversion', - 'Last Activexxxx', - ].forEach((cellValue, index) => { - expect(data[1][index]).to.equal(cellValue); - }); + const expectedData = [ + [ + 'Hostname', + 'Policy', + 'Policy Status', + 'Alerts', + 'Operating System', + 'IP Address', + 'Sensor Version', + 'Last Active', + ], + [ + 'cadmann-4.example.com', + 'Policy Name', + 'Policy Status', + '0', + 'windows 10.0', + '10.192.213.130, 10.70.28.129', + 'version', + 'xxxx', + ], + [ + 'thurlow-9.example.com', + 'Policy Name', + 'Policy Status', + '0', + 'windows 10.0', + '10.46.229.234', + 'version', + 'xxxx', + ], + [ + 'rezzani-7.example.com', + 'Policy Name', + 'Policy Status', + '0', + 'windows 10.0', + '10.101.149.26, 2606:a000:ffc0:39:11ef:37b9:3371:578c', + 'version', + 'xxxx', + ], + ]; + const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable'); + expect(tableData).to.eql(expectedData); + }); + + it('displays no items found', async () => { + // clear out the data and reload the page + await esArchiver.unload('endpoint/metadata/api_feature'); + await pageObjects.common.navigateToUrlWithBrowserHistory('endpoint', '/management'); + // get the table data and verify no entries appear + const tableData = await pageObjects.endpoint.getEndpointAppTableData('managementListTable'); + expect(tableData[1][0]).to.equal('No items found'); + // reload the data so the other tests continue to pass + await esArchiver.load('endpoint/metadata/api_feature'); }); after(async () => { diff --git a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json index 87720b068f0e8..6a7911b5be61f 100644 --- a/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json +++ b/x-pack/test/functional/es_archives/endpoint/metadata/api_feature/data.json @@ -110,7 +110,7 @@ "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", "ip": [ "10.101.149.26", - "10.12.85.216" + "2606:a000:ffc0:39:11ef:37b9:3371:578c" ], "mac": [ "e2-6d-f9-0-46-2e" @@ -238,7 +238,7 @@ "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", "ip": [ "10.101.149.26", - "10.12.85.216" + "2606:a000:ffc0:39:11ef:37b9:3371:578c" ], "mac": [ "e2-6d-f9-0-46-2e" @@ -365,7 +365,7 @@ "id": "fc0ff548-feba-41b6-8367-65e8790d0eaf", "ip": [ "10.101.149.26", - "10.12.85.216" + "2606:a000:ffc0:39:11ef:37b9:3371:578c" ], "mac": [ "e2-6d-f9-0-46-2e" diff --git a/x-pack/test/functional/page_objects/endpoint_page.ts b/x-pack/test/functional/page_objects/endpoint_page.ts index 54f537dd0e8c3..185b95b00527d 100644 --- a/x-pack/test/functional/page_objects/endpoint_page.ts +++ b/x-pack/test/functional/page_objects/endpoint_page.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import { WebElementWrapper } from 'test/functional/services/lib/web_element_wrapper'; import { FtrProviderContext } from '../ftr_provider_context'; export function EndpointPageProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); - const table = getService('table'); return { /** @@ -34,8 +34,29 @@ export function EndpointPageProvider({ getService }: FtrProviderContext) { return await testSubjects.getVisibleText('welcomeTitle'); }, - async getManagementTableData() { - return await table.getDataFromTestSubj('managementListTable'); + /** + * Finds a table and returns the data in a nested array with row 0 is the headers if they exist. + * It uses euiTableCellContent to avoid poluting the array data with the euiTableRowCell__mobileHeader data. + * @param dataTestSubj + * @returns Promise + */ + async getEndpointAppTableData(dataTestSubj: string) { + await testSubjects.exists(dataTestSubj); + const hostTable: WebElementWrapper = await testSubjects.find(dataTestSubj); + const $ = await hostTable.parseDomContent(); + return $('tr') + .toArray() + .map(row => + $(row) + .find('.euiTableCellContent') + .toArray() + .map(cell => + $(cell) + .text() + .replace(/ /g, '') + .trim() + ) + ); }, }; } From 3d9eb2a679f6316548f156ed5c9e705ea45a7bf6 Mon Sep 17 00:00:00 2001 From: Dima Arnautov Date: Fri, 21 Feb 2020 17:45:42 +0100 Subject: [PATCH 7/7] [ML] Transform: Support multi-line JSON notation in advanced editor (#58015) * [ML] use xJsonMode * [ML] remove commented code * [ML] move use_x_json_mode hook, disable json formatting * [ML] mocks for shared_imports * [ML] ts-ignore worker import --- .../public/__mocks__/shared_imports.ts | 10 +++++++++ .../public/app/hooks/use_x_json_mode.ts | 20 ++++++++++++++++++ .../source_index_preview.test.tsx | 2 ++ .../step_create/step_create_form.test.tsx | 2 ++ .../step_define/pivot_preview.test.tsx | 2 ++ .../step_define/step_define_form.test.tsx | 2 ++ .../step_define/step_define_form.tsx | 21 +++++++++++-------- .../step_define/step_define_summary.test.tsx | 2 ++ .../create_transform_button.test.tsx | 2 ++ .../transform_list/action_delete.test.tsx | 2 ++ .../transform_list/action_start.test.tsx | 2 ++ .../transform_list/action_stop.test.tsx | 2 ++ .../transform_list/actions.test.tsx | 2 ++ .../transform_list/columns.test.tsx | 2 ++ .../transform_list/expanded_row.test.tsx | 2 ++ .../transform_list/transform_list.test.tsx | 2 ++ .../transform_management_section.test.tsx | 2 ++ .../transform/public/shared_imports.ts | 6 ++++++ .../ace/modes/x_json/worker/index.ts | 1 + 19 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts create mode 100644 x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.ts diff --git a/x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts new file mode 100644 index 0000000000000..b55a4cd5c7bd6 --- /dev/null +++ b/x-pack/legacy/plugins/transform/public/__mocks__/shared_imports.ts @@ -0,0 +1,10 @@ +/* + * 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 XJsonMode() {} +export function setDependencyCache() {} +export { mlInMemoryTableBasicFactory } from '../../../ml/public/application/components/ml_in_memory_table'; +export const SORT_DIRECTION = { ASC: 'asc' }; diff --git a/x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.ts b/x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.ts new file mode 100644 index 0000000000000..1017ce198ff29 --- /dev/null +++ b/x-pack/legacy/plugins/transform/public/app/hooks/use_x_json_mode.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 { useState } from 'react'; +import { collapseLiteralStrings, expandLiteralStrings, XJsonMode } from '../../shared_imports'; + +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/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx index f326199271592..d7f1d9d099cc3 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx @@ -20,6 +20,8 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('../../../../../shared_imports'); + describe('Transform: ', () => { test('Minimal initialization', () => { const props = { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx index 055f0613e4e44..a0c91c070844b 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx @@ -19,6 +19,8 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('../../../../../shared_imports'); + describe('Transform: ', () => { test('Minimal initialization', () => { const props = { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx index 4ff1190415dba..a2aa056c1634d 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx @@ -27,6 +27,8 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('../../../../../shared_imports'); + describe('Transform: ', () => { test('Minimal initialization', () => { const groupBy: PivotGroupByConfig = { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx index 48df371e87664..0311b26304c30 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx @@ -25,6 +25,8 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('../../../../../shared_imports'); + describe('Transform: ', () => { test('Minimal initialization', () => { // Using a wrapping
element because shallow() would fail diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index 675386be8e2a5..1499f99f82824 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -28,6 +28,7 @@ import { EuiSwitch, } from '@elastic/eui'; +import { useXJsonMode, xJsonMode } from '../../../../hooks/use_x_json_mode'; import { TransformPivotConfig } from '../../../../common'; import { dictionaryToArray, Dictionary } from '../../../../../../common/types/common'; import { DropDown } from '../aggregation_dropdown'; @@ -383,7 +384,13 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange const [advancedEditorConfigLastApplied, setAdvancedEditorConfigLastApplied] = useState( stringifiedPivotConfig ); - const [advancedEditorConfig, setAdvancedEditorConfig] = useState(stringifiedPivotConfig); + + const { + convertToJson, + setXJson: setAdvancedEditorConfig, + xJson: advancedEditorConfig, + } = useXJsonMode(stringifiedPivotConfig); + // source config const stringifiedSourceConfig = JSON.stringify(previewRequest.source.query, null, 2); const [ @@ -407,7 +414,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange }; const applyAdvancedPivotEditorChanges = () => { - const pivotConfig = JSON.parse(advancedEditorConfig); + const pivotConfig = JSON.parse(convertToJson(advancedEditorConfig)); const newGroupByList: PivotGroupByConfigDict = {}; if (pivotConfig !== undefined && pivotConfig.group_by !== undefined) { @@ -442,10 +449,8 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange }); } setAggList(newAggList); - const prettyPivotConfig = JSON.stringify(pivotConfig, null, 2); - setAdvancedEditorConfig(prettyPivotConfig); - setAdvancedEditorConfigLastApplied(prettyPivotConfig); + setAdvancedEditorConfigLastApplied(advancedEditorConfig); setAdvancedPivotEditorApplyButtonEnabled(false); }; @@ -513,13 +518,11 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange pivotAggsArr ); - const stringifiedPivotConfigUpdate = JSON.stringify(previewRequestUpdate.pivot, null, 2); const stringifiedSourceConfigUpdate = JSON.stringify( previewRequestUpdate.source.query, null, 2 ); - setAdvancedEditorConfig(stringifiedPivotConfigUpdate); setAdvancedEditorSourceConfig(stringifiedSourceConfigUpdate); onChange({ @@ -784,7 +787,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange > { @@ -799,7 +802,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange // Try to parse the string passed on from the editor. // If parsing fails, the "Apply"-Button will be disabled try { - JSON.parse(d); + JSON.parse(convertToJson(d)); setAdvancedPivotEditorApplyButtonEnabled(true); } catch (e) { setAdvancedPivotEditorApplyButtonEnabled(false); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx index 2d9895e8ddcf1..aae366e6008d5 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx @@ -26,6 +26,8 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('../../../../../shared_imports'); + describe('Transform: ', () => { test('Minimal initialization', () => { const groupBy: PivotGroupByConfig = { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx index 673e60de54572..288630333615a 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/create_transform_button/create_transform_button.test.tsx @@ -11,6 +11,8 @@ import { CreateTransformButton } from './create_transform_button'; jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List ', () => { test('Minimal initialization', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx index 979da13b1f83a..4795a2eb7d7bc 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_delete.test.tsx @@ -17,6 +17,8 @@ import transformListRow from '../../../../common/__mocks__/transform_list_row.js jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List Actions ', () => { test('Minimal initialization', () => { const Providers = getAppProviders(createPublicShim()); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx index 71a2eff39506d..5f4d4a71c71eb 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_start.test.tsx @@ -17,6 +17,8 @@ import transformListRow from '../../../../common/__mocks__/transform_list_row.js jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List Actions ', () => { test('Minimal initialization', () => { const Providers = getAppProviders(createPublicShim()); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx index c3b67f7661a1a..f6bb1c8b60667 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/action_stop.test.tsx @@ -17,6 +17,8 @@ import transformListRow from '../../../../common/__mocks__/transform_list_row.js jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List Actions ', () => { test('Minimal initialization', () => { const Providers = getAppProviders(createPublicShim()); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx index ef92a5e3859d7..12e1ba5528c43 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/actions.test.tsx @@ -8,6 +8,8 @@ import { getActions } from './actions'; jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List Actions', () => { test('getActions()', () => { const actions = getActions({ forceDisable: false }); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx index f16130bfe618b..42f04ed101ad6 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.test.tsx @@ -8,6 +8,8 @@ import { getColumns } from './columns'; jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Job List Columns', () => { test('getColumns()', () => { const columns = getColumns([], () => {}, []); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx index 4f992707cbf1a..7fcaf5e6048f6 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/expanded_row.test.tsx @@ -13,6 +13,8 @@ import { ExpandedRow } from './expanded_row'; import transformListRow from '../../../../common/__mocks__/transform_list_row.json'; +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List ', () => { // Set timezone to US/Eastern for consistent test results. beforeEach(() => { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx index 303de6b86ac53..e1a19ddd3c742 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.test.tsx @@ -12,6 +12,8 @@ import { TransformList } from './transform_list'; jest.mock('ui/new_platform'); +jest.mock('../../../../../shared_imports'); + describe('Transform: Transform List ', () => { test('Minimal initialization', () => { const wrapper = shallow( diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx index c94f5c1d57d49..f68670f0b38b2 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.test.tsx @@ -14,6 +14,8 @@ jest.mock('ui/timefilter', () => { return {}; }); +jest.mock('../../../shared_imports'); + describe('Transform: ', () => { test('Minimal initialization', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/transform/public/shared_imports.ts b/x-pack/legacy/plugins/transform/public/shared_imports.ts index 74e0c9a3878db..248eb00c67dff 100644 --- a/x-pack/legacy/plugins/transform/public/shared_imports.ts +++ b/x-pack/legacy/plugins/transform/public/shared_imports.ts @@ -4,6 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ +export { XJsonMode } from '../../../../plugins/es_ui_shared/console_lang/ace/modes/x_json'; +export { + collapseLiteralStrings, + expandLiteralStrings, +} from '../../../../../src/plugins/es_ui_shared/console_lang/lib'; + export { SendRequestConfig, SendRequestResponse, diff --git a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts index af50562bd3242..0e40fd335dd31 100644 --- a/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts +++ b/x-pack/plugins/es_ui_shared/console_lang/ace/modes/x_json/worker/index.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +// @ts-ignore import src from '!!raw-loader!./worker.js'; export const workerModule = {