diff --git a/package.json b/package.json index 9fa1a1382a496..c9acdab0e3dee 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "@elastic/ecs": "^8.11.1", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.9.1-canary.1", "@elastic/ems-client": "8.5.1", - "@elastic/eui": "93.1.1", + "@elastic/eui": "93.2.0", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap index 87f13bee8f7c9..8656c7684ce37 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap +++ b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap @@ -303,6 +303,7 @@ exports[`#start() returns \`Context\` component 1`] = ` "euiSelectableTemplateSitewide.noResults": "No results available", "euiSelectableTemplateSitewide.onFocusBadgeGoTo": "Go to", "euiSelectableTemplateSitewide.searchPlaceholder": "Search for anything...", + "euiSideNav.mobileToggleAriaLabel": "Toggle navigation", "euiSkeletonLoading.loadedAriaText": [Function], "euiSkeletonLoading.loadingAriaText": [Function], "euiStat.loadingText": "Statistic is loading", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx index 58f057e2fedcc..21069027c6a50 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx +++ b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx @@ -1544,6 +1544,9 @@ export const getEuiContextMapping = (): EuiTokensObject => { defaultMessage: 'Search for anything...', } ), + 'euiSideNav.mobileToggleAriaLabel': i18n.translate('core.euiSideNav.mobileToggleAriaLabel', { + defaultMessage: 'Toggle navigation', + }), 'euiStat.loadingText': i18n.translate('core.euiStat.loadingText', { defaultMessage: 'Statistic is loading', }), diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 0561176f3a50c..bfa2953310e0c 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -536,6 +536,8 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D customUrls: `${MACHINE_LEARNING_DOCS}ml-configuring-url.html`, dataFrameAnalytics: `${MACHINE_LEARNING_DOCS}ml-dfanalytics.html`, dFAPrepareData: `${MACHINE_LEARNING_DOCS}ml-dfa-overview.html#prepare-transform-data`, + dFAStartJob: `${ELASTICSEARCH_DOCS}start-dfanalytics.html`, + dFACreateJob: `${ELASTICSEARCH_DOCS}put-dfanalytics.html`, featureImportance: `${MACHINE_LEARNING_DOCS}ml-feature-importance.html`, outlierDetectionRoc: `${MACHINE_LEARNING_DOCS}ml-dfa-finding-outliers.html#ml-dfanalytics-roc`, regressionEvaluation: `${MACHINE_LEARNING_DOCS}ml-dfa-regression.html#ml-dfanalytics-regression-evaluation`, diff --git a/packages/shared-ux/page/solution_nav/src/__snapshots__/solution_nav.test.tsx.snap b/packages/shared-ux/page/solution_nav/src/__snapshots__/solution_nav.test.tsx.snap index ab65858bff1ef..01ea4307e9138 100644 --- a/packages/shared-ux/page/solution_nav/src/__snapshots__/solution_nav.test.tsx.snap +++ b/packages/shared-ux/page/solution_nav/src/__snapshots__/solution_nav.test.tsx.snap @@ -36,7 +36,7 @@ exports[`SolutionNav accepts EuiSideNavProps 1`] = ` color="transparent" paddingSize="s" > - - - - - - - - - - = ({ className="kbnSolutionNav__flyout" hideCloseButton={!canBeCollapsed} > -
+ {titleText} {sideNavContent} -
+ )} {canBeCollapsed && ( diff --git a/packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx b/packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx index 2e4879a4093cb..7689a739ebbe9 100644 --- a/packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx +++ b/packages/shared-ux/page/solution_nav/src/with_solution_nav.tsx @@ -76,6 +76,7 @@ export const withSolutionNav =

(WrappedComponent: Compo ...props.pageSideBarProps, minWidth: isSidebarShrunk ? euiTheme.size.xxl : undefined, className: sideBarClasses, + hasEmbellish: !isSidebarShrunk, }; return ( diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 319d66567336b..e21de7b953d0a 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -85,7 +85,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.1': ['Elastic License 2.0'], - '@elastic/eui@93.1.1': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@93.2.0': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary '@bufbuild/protobuf@1.2.1': ['Apache-2.0'], // license (Apache-2.0 AND BSD-3-Clause) diff --git a/src/plugins/controls/kibana.jsonc b/src/plugins/controls/kibana.jsonc index 14718f533a8f6..bd65ecc2d0b6f 100644 --- a/src/plugins/controls/kibana.jsonc +++ b/src/plugins/controls/kibana.jsonc @@ -17,6 +17,7 @@ "unifiedSearch", "uiActions" ], - "extraPublicDirs": ["common"] + "extraPublicDirs": ["common"], + "requiredBundles": ["kibanaUtils"] } } diff --git a/src/plugins/controls/public/control_group/component/control_group_component.tsx b/src/plugins/controls/public/control_group/component/control_group_component.tsx index e77e88f6e900e..20d599aed0eb4 100644 --- a/src/plugins/controls/public/control_group/component/control_group_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_group_component.tsx @@ -8,34 +8,44 @@ import '../control_group.scss'; -import { - arrayMove, - SortableContext, - rectSortingStrategy, - sortableKeyboardCoordinates, -} from '@dnd-kit/sortable'; +import classNames from 'classnames'; +import React, { useEffect, useMemo, useState } from 'react'; +import { TypedUseSelectorHook, useSelector } from 'react-redux'; + import { closestCenter, DndContext, DragEndEvent, DragOverlay, KeyboardSensor, + LayoutMeasuringStrategy, PointerSensor, useSensor, useSensors, - LayoutMeasuringStrategy, } from '@dnd-kit/core'; -import classNames from 'classnames'; -import React, { useMemo, useState } from 'react'; -import { TypedUseSelectorHook, useSelector } from 'react-redux'; -import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; - +import { + arrayMove, + rectSortingStrategy, + SortableContext, + sortableKeyboardCoordinates, +} from '@dnd-kit/sortable'; +import { + EuiButtonEmpty, + EuiButtonIcon, + EuiCheckbox, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiText, + EuiTourStep, +} from '@elastic/eui'; import { ViewMode } from '@kbn/embeddable-plugin/public'; -import { ControlGroupReduxState } from '../types'; import { ControlGroupStrings } from '../control_group_strings'; -import { ControlClone, SortableControl } from './control_group_sortable_item'; import { useControlGroupContainer } from '../embeddable/control_group_container'; +import { ControlGroupReduxState } from '../types'; +import { ControlClone, SortableControl } from './control_group_sortable_item'; const contextSelect = useSelector as TypedUseSelectorHook; @@ -47,6 +57,12 @@ export const ControlGroup = () => { const viewMode = contextSelect((state) => state.explicitInput.viewMode); const controlStyle = contextSelect((state) => state.explicitInput.controlStyle); const showAddButton = contextSelect((state) => state.componentState.showAddButton); + const controlWithInvalidSelectionsId = contextSelect( + (state) => state.componentState.controlWithInvalidSelectionsId + ); + const [tourStepOpen, setTourStepOpen] = useState(true); + const [suppressTourChecked, setSuppressTourChecked] = useState(false); + const [renderTourStep, setRenderTourStep] = useState(false); const isEditable = viewMode === ViewMode.EDIT; @@ -61,6 +77,87 @@ export const ControlGroup = () => { [panels] ); + useEffect(() => { + /** + * This forces the tour step to get unmounted so that it can attach to the new invalid + * control - otherwise, the anchor will remain attached to the old invalid control + */ + setRenderTourStep(false); + setTimeout(() => setRenderTourStep(true), 100); + }, [controlWithInvalidSelectionsId]); + + const tourStep = useMemo(() => { + if ( + !renderTourStep || + !controlGroup.canShowInvalidSelectionsWarning() || + !tourStepOpen || + !controlWithInvalidSelectionsId + ) { + return null; + } + const invalidControlType = panels[controlWithInvalidSelectionsId].type; + + return ( + {}} + panelPaddingSize="m" + anchorPosition="downCenter" + panelClassName="controlGroup--invalidSelectionsTour" + anchor={`#controlFrame--${controlWithInvalidSelectionsId}`} + title={ + + + + + {ControlGroupStrings.invalidControlWarning.getTourTitle()} + + } + content={ControlGroupStrings.invalidControlWarning.getTourContent(invalidControlType)} + footerAction={[ + setSuppressTourChecked(e.target.checked)} + label={ + + {ControlGroupStrings.invalidControlWarning.getSuppressTourLabel()} + + } + />, + { + setTourStepOpen(false); + if (suppressTourChecked) { + controlGroup.suppressInvalidSelectionsWarning(); + } + }} + > + {ControlGroupStrings.invalidControlWarning.getDismissButton()} + , + ]} + /> + ); + }, [ + panels, + controlGroup, + tourStepOpen, + renderTourStep, + suppressTourChecked, + controlWithInvalidSelectionsId, + ]); + const [draggingId, setDraggingId] = useState(null); const draggingIndex = useMemo( () => (draggingId ? idsInOrder.indexOf(draggingId) : -1), @@ -117,6 +214,7 @@ export const ControlGroup = () => { alignItems="center" data-test-subj="controls-group" > + {tourStep} setDraggingId(active.id)} diff --git a/src/plugins/controls/public/control_group/control_group.scss b/src/plugins/controls/public/control_group/control_group.scss index d1fb6a8d495de..4ad7753a9cb53 100644 --- a/src/plugins/controls/public/control_group/control_group.scss +++ b/src/plugins/controls/public/control_group/control_group.scss @@ -199,3 +199,12 @@ $controlMinWidth: $euiSize * 14; top: (-$euiSizeXS) !important; } } + +.controlGroup--invalidSelectionsTour { + .controlGroup--suppressTourCheckbox { + height: 22px; + &Label { + font-weight: $euiFontWeightMedium; + } + } +} \ No newline at end of file diff --git a/src/plugins/controls/public/control_group/control_group_strings.ts b/src/plugins/controls/public/control_group/control_group_strings.ts index 996d86f7d676d..7b1b3ff3c1169 100644 --- a/src/plugins/controls/public/control_group/control_group_strings.ts +++ b/src/plugins/controls/public/control_group/control_group_strings.ts @@ -10,6 +10,42 @@ import { i18n } from '@kbn/i18n'; import { RANGE_SLIDER_CONTROL } from '../range_slider'; export const ControlGroupStrings = { + invalidControlWarning: { + getTourTitle: () => + i18n.translate('controls.controlGroup.invalidControlWarning.tourStepTitle.default', { + defaultMessage: 'Invalid selections are no longer ignored', + }), + getTourContent: (controlType: string) => { + switch (controlType) { + case RANGE_SLIDER_CONTROL: { + return i18n.translate( + 'controls.controlGroup.invalidControlWarning.tourStepContent.rangeSlider', + { + defaultMessage: 'The selected range is returning no results. Try changing the range.', + } + ); + } + default: { + return i18n.translate( + 'controls.controlGroup.invalidControlWarning.tourStepContent.default', + { + defaultMessage: + 'Some selections are returning no results. Try changing the selections.', + } + ); + } + } + }, + + getDismissButton: () => + i18n.translate('controls.controlGroup.invalidControlWarning.dismissButtonLabel', { + defaultMessage: 'Dismiss', + }), + getSuppressTourLabel: () => + i18n.translate('controls.controlGroup.invalidControlWarning.suppressTourLabel', { + defaultMessage: "Don't show again", + }), + }, manageControl: { getFlyoutCreateTitle: () => i18n.translate('controls.controlGroup.manageControl.createFlyoutTitle', { @@ -258,8 +294,7 @@ export const ControlGroupStrings = { }), getValidateSelectionsSubTitle: () => i18n.translate('controls.controlGroup.management.validate.subtitle', { - defaultMessage: - 'Automatically ignore any control selection that would result in no data.', + defaultMessage: 'Highlight control selections that result in no data.', }), }, controlChaining: { diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx index dcb74fd606154..0663d3a3d9c61 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx @@ -5,6 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import { compareFilters, COMPARE_ALL_OPTIONS, Filter, uniqFilters } from '@kbn/es-query'; import { isEqual, pick } from 'lodash'; import React, { createContext, useContext } from 'react'; @@ -24,6 +25,7 @@ import { persistableControlGroupInputKeys, } from '../../../common'; import { pluginServices } from '../../services'; +import { ControlsStorageService } from '../../services/storage/types'; import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; import { ControlGroup } from '../component/control_group_component'; import { openAddDataControlFlyout } from '../editor/open_add_data_control_flyout'; @@ -86,11 +88,15 @@ export class ControlGroupContainer extends Container< private initialized$ = new BehaviorSubject(false); + private storageService: ControlsStorageService; + private subscriptions: Subscription = new Subscription(); private domNode?: HTMLElement; private recalculateFilters$: Subject; private relevantDataViewId?: string; private lastUsedDataViewId?: string; + private invalidSelectionsState: { [childId: string]: boolean }; + public diffingSubscription: Subscription = new Subscription(); // state management @@ -126,6 +132,8 @@ export class ControlGroupContainer extends Container< ControlGroupChainingSystems[initialInput.chainingSystem]?.getContainerSettings(initialInput) ); + ({ storage: this.storageService } = pluginServices.getServices()); + this.recalculateFilters$ = new Subject(); this.onFiltersPublished$ = new Subject(); this.onControlRemoved$ = new Subject(); @@ -153,6 +161,10 @@ export class ControlGroupContainer extends Container< this.store = reduxEmbeddableTools.store; + this.invalidSelectionsState = this.getChildIds().reduce((prev, id) => { + return { ...prev, [id]: false }; + }, {}); + // when all children are ready setup subscriptions this.untilAllChildrenReady().then(() => { this.recalculateDataViews(); @@ -164,6 +176,32 @@ export class ControlGroupContainer extends Container< this.fieldFilterPredicate = fieldFilterPredicate; } + public canShowInvalidSelectionsWarning = () => + this.storageService.getShowInvalidSelectionWarning() ?? true; + + public suppressInvalidSelectionsWarning = () => { + this.storageService.setShowInvalidSelectionWarning(false); + }; + + public reportInvalidSelections = ({ + id, + hasInvalidSelections, + }: { + id: string; + hasInvalidSelections: boolean; + }) => { + this.invalidSelectionsState = { ...this.invalidSelectionsState, [id]: hasInvalidSelections }; + + const childrenWithInvalidSelections = cachedChildEmbeddableOrder( + this.getInput().panels + ).idsInOrder.filter((childId) => { + return this.invalidSelectionsState[childId]; + }); + this.dispatch.setControlWithInvalidSelectionsId( + childrenWithInvalidSelections.length > 0 ? childrenWithInvalidSelections[0] : undefined + ); + }; + private setupSubscriptions = () => { /** * refresh control order cache and make all panels refreshInputFromParent whenever panel orders change @@ -201,7 +239,9 @@ export class ControlGroupContainer extends Container< * debounce output recalculation */ this.subscriptions.add( - this.recalculateFilters$.pipe(debounceTime(10)).subscribe(() => this.recalculateFilters()) + this.recalculateFilters$.pipe(debounceTime(10)).subscribe(() => { + this.recalculateFilters(); + }) ); }; @@ -211,9 +251,14 @@ export class ControlGroupContainer extends Container< } = this.getState(); if (!persistableControlGroupInputIsEqual(this.getPersistableInput(), lastSavedInput)) { this.updateInput(lastSavedInput); + this.reload(); // this forces the children to update their inputs + perform validation as necessary } } + public reload() { + super.reload(); + } + public getPersistableInput: () => PersistableControlGroupInput & { id: string } = () => { const input = this.getInput(); return pick(input, [...persistableControlGroupInputKeys, 'id']); @@ -284,13 +329,14 @@ export class ControlGroupContainer extends Container< private recalculateFilters = () => { const allFilters: Filter[] = []; let timeslice; - Object.values(this.children).map((child) => { + Object.values(this.children).map((child: ControlEmbeddable) => { const childOutput = child.getOutput() as ControlOutput; allFilters.push(...(childOutput?.filters ?? [])); if (childOutput.timeslice) { timeslice = childOutput.timeslice; } }); + // if filters are different, publish them if ( !compareFilters(this.output.filters ?? [], allFilters ?? [], COMPARE_ALL_OPTIONS) || diff --git a/src/plugins/controls/public/control_group/state/control_group_reducers.ts b/src/plugins/controls/public/control_group/state/control_group_reducers.ts index 617a708fb0fe2..313b21af5745b 100644 --- a/src/plugins/controls/public/control_group/state/control_group_reducers.ts +++ b/src/plugins/controls/public/control_group/state/control_group_reducers.ts @@ -19,6 +19,12 @@ export const controlGroupReducers = { ) => { state.componentState.lastSavedInput = action.payload; }, + setControlWithInvalidSelectionsId: ( + state: WritableDraft, + action: PayloadAction + ) => { + state.componentState.controlWithInvalidSelectionsId = action.payload; + }, setControlStyle: ( state: WritableDraft, action: PayloadAction diff --git a/src/plugins/controls/public/control_group/types.ts b/src/plugins/controls/public/control_group/types.ts index 699b003097fe1..c48aee3f1ace9 100644 --- a/src/plugins/controls/public/control_group/types.ts +++ b/src/plugins/controls/public/control_group/types.ts @@ -42,6 +42,7 @@ export interface ControlGroupSettings { export type ControlGroupComponentState = ControlGroupSettings & { lastSavedInput: PersistableControlGroupInput; + controlWithInvalidSelectionsId?: string; }; export { diff --git a/src/plugins/controls/public/options_list/components/options_list.scss b/src/plugins/controls/public/options_list/components/options_list.scss index 4dca925e952ce..fc1cdf68e3fec 100644 --- a/src/plugins/controls/public/options_list/components/options_list.scss +++ b/src/plugins/controls/public/options_list/components/options_list.scss @@ -15,14 +15,16 @@ font-weight: $euiFontWeightRegular; } - .optionsList__filterValid { + .optionsList__selections { + overflow: hidden !important; + } + + .optionsList__filter { font-weight: $euiFontWeightMedium; } .optionsList__filterInvalid { - color: $euiTextSubduedColor; - text-decoration: line-through; - font-weight: $euiFontWeightRegular; + color: $euiColorWarningText; } .optionsList__negateLabel { @@ -74,12 +76,11 @@ } .optionsList-control-ignored-selection-title { - padding-left: $euiSizeS; + padding-left: $euiSizeM; } .optionsList__selectionInvalid { - text-decoration: line-through; - color: $euiTextSubduedColor; + color: $euiColorWarningText; } .optionslist--loadingMoreGroupLabel { diff --git a/src/plugins/controls/public/options_list/components/options_list_control.test.tsx b/src/plugins/controls/public/options_list/components/options_list_control.test.tsx index 5bfe3edd16c57..65b94fc240eac 100644 --- a/src/plugins/controls/public/options_list/components/options_list_control.test.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_control.test.tsx @@ -8,9 +8,7 @@ import React from 'react'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; - +import { render } from '@testing-library/react'; import { OptionsListEmbeddableContext } from '../embeddable/options_list_embeddable'; import { OptionsListComponentState, OptionsListReduxState } from '../types'; import { ControlOutput, OptionsListEmbeddableInput } from '../..'; @@ -37,7 +35,7 @@ describe('Options list control', () => { output: options?.output ?? {}, } as Partial); - return mountWithIntl( + return render( @@ -48,15 +46,15 @@ describe('Options list control', () => { const control = await mountComponent({ explicitInput: { id: 'testExists', exclude: false, existsSelected: true }, }); - const existsOption = findTestSubject(control, 'optionsList-control-testExists'); - expect(existsOption.text()).toBe('Exists'); + const existsOption = control.getByTestId('optionsList-control-testExists'); + expect(existsOption).toHaveTextContent('Exists'); }); test('if exclude = true and existsSelected = true, then the option should read "Does not exist"', async () => { const control = await mountComponent({ explicitInput: { id: 'testDoesNotExist', exclude: true, existsSelected: true }, }); - const existsOption = findTestSubject(control, 'optionsList-control-testDoesNotExist'); - expect(existsOption.text()).toBe('DOES NOT Exist'); + const existsOption = control.getByTestId('optionsList-control-testDoesNotExist'); + expect(existsOption).toHaveTextContent('DOES NOT Exist'); }); }); diff --git a/src/plugins/controls/public/options_list/components/options_list_control.tsx b/src/plugins/controls/public/options_list/components/options_list_control.tsx index 5a358b29c6196..930348d9731fd 100644 --- a/src/plugins/controls/public/options_list/components/options_list_control.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_control.tsx @@ -11,7 +11,15 @@ import classNames from 'classnames'; import { debounce, isEmpty } from 'lodash'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; -import { EuiFilterButton, EuiFilterGroup, EuiInputPopover } from '@elastic/eui'; +import { + EuiFilterButton, + EuiFilterGroup, + EuiFlexGroup, + EuiFlexItem, + EuiInputPopover, + EuiToken, + EuiToolTip, +} from '@elastic/eui'; import { MAX_OPTIONS_LIST_REQUEST_SIZE } from '../types'; import { OptionsListStrings } from './options_list_strings'; @@ -34,7 +42,6 @@ export const OptionsListControl = ({ const error = optionsList.select((state) => state.componentState.error); const isPopoverOpen = optionsList.select((state) => state.componentState.popoverOpen); - const validSelections = optionsList.select((state) => state.componentState.validSelections); const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); const fieldSpec = optionsList.select((state) => state.componentState.field); @@ -90,74 +97,116 @@ export const OptionsListControl = ({ [loadMoreSubject] ); - const { hasSelections, selectionDisplayNode, validSelectionsCount } = useMemo(() => { - const delimiter = OptionsListStrings.control.getSeparator(fieldSpec?.type); + const delimiter = useMemo( + () => OptionsListStrings.control.getSeparator(fieldSpec?.type), + [fieldSpec?.type] + ); + const { hasSelections, selectionDisplayNode, selectedOptionsCount } = useMemo(() => { return { - hasSelections: !isEmpty(validSelections) || !isEmpty(invalidSelections), - validSelectionsCount: validSelections?.length, + hasSelections: !isEmpty(selectedOptions), + selectedOptionsCount: selectedOptions?.length, selectionDisplayNode: ( - <> - {exclude && ( - <> - - {existsSelected - ? OptionsListStrings.control.getExcludeExists() - : OptionsListStrings.control.getNegate()} - {' '} - - )} - {existsSelected ? ( - - {OptionsListStrings.controlAndPopover.getExists(+Boolean(exclude))} - - ) : ( - <> - {validSelections?.length ? ( - - {validSelections.map((value) => fieldFormatter(value)).join(delimiter)} - - ) : null} - {validSelections?.length && invalidSelections?.length ? delimiter : null} - {invalidSelections?.length ? ( - - {invalidSelections.map((value) => fieldFormatter(value)).join(delimiter)} + + +

+ {exclude && ( + <> + + {existsSelected + ? OptionsListStrings.control.getExcludeExists() + : OptionsListStrings.control.getNegate()} + {' '} + + )} + {existsSelected ? ( + + {OptionsListStrings.controlAndPopover.getExists(+Boolean(exclude))} - ) : null} - + ) : ( + <> + {selectedOptions?.length + ? selectedOptions.map((value: string, i, { length }) => { + const text = `${fieldFormatter(value)}${ + i + 1 === length ? '' : delimiter + } `; + const isInvalid = invalidSelections?.includes(value); + return ( + + {text} + + ); + }) + : null} + + )} +
+ + {invalidSelections && invalidSelections.length > 0 && ( + + + + + )} - + ), }; - }, [ - exclude, - existsSelected, - validSelections, - invalidSelections, - fieldFormatter, - fieldSpec?.type, - ]); + }, [selectedOptions, exclude, existsSelected, fieldFormatter, delimiter, invalidSelections]); const button = ( - optionsList.dispatch.setPopoverOpen(!isPopoverOpen)} - isSelected={isPopoverOpen} - numActiveFilters={validSelectionsCount} - hasActiveFilters={Boolean(validSelectionsCount)} - textProps={{ className: 'optionsList--selectionText' }} - > - {hasSelections || existsSelected - ? selectionDisplayNode - : placeholder ?? OptionsListStrings.control.getPlaceholder()} - + <> + optionsList.dispatch.setPopoverOpen(!isPopoverOpen)} + isSelected={isPopoverOpen} + numActiveFilters={selectedOptionsCount} + hasActiveFilters={Boolean(selectedOptionsCount)} + aria-label={`${selectedOptions + ?.map((value) => { + const isInvalid = invalidSelections?.includes(value); + return `${ + isInvalid + ? OptionsListStrings.popover.getInvalidSelectionScreenReaderText() + ' ' + : '' + }${fieldFormatter(value)}`; + }) + .join(delimiter)}`} + textProps={{ className: 'optionsList--selectionText' }} + > + {hasSelections || existsSelected + ? selectionDisplayNode + : placeholder ?? OptionsListStrings.control.getPlaceholder()} + + ); return error ? ( diff --git a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx index 59f1c9f2b058c..8b39efbc040c2 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx @@ -7,10 +7,9 @@ */ import React from 'react'; -import { ReactWrapper } from 'enzyme'; +import { render, RenderResult, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; -import { mountWithIntl } from '@kbn/test-jest-helpers'; -import { findTestSubject } from '@elastic/eui/lib/test'; import { FieldSpec } from '@kbn/data-views-plugin/common'; import { pluginServices } from '../../services'; @@ -42,41 +41,36 @@ describe('Options list popover', () => { output: options?.output ?? {}, } as Partial); - return mountWithIntl( + return render( ); } - const clickShowOnlySelections = (popover: ReactWrapper) => { - const showOnlySelectedButton = findTestSubject( - popover, - 'optionsList-control-show-only-selected' - ); - showOnlySelectedButton.simulate('click'); + const clickShowOnlySelections = (popover: RenderResult) => { + const showOnlySelectedButton = popover.getByTestId('optionsList-control-show-only-selected'); + userEvent.click(showOnlySelectedButton); }; test('no available options', async () => { const popover = await mountComponent({ componentState: { availableOptions: [] } }); - const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - const noOptionsDiv = findTestSubject( - availableOptionsDiv, + const availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + const noOptionsDiv = within(availableOptionsDiv).getByTestId( 'optionsList-control-noSelectionsMessage' ); - expect(noOptionsDiv.exists()).toBeTruthy(); + expect(noOptionsDiv).toBeInTheDocument(); }); describe('show only selected', () => { test('display error message when the show only selected toggle is true but there are no selections', async () => { const popover = await mountComponent(); clickShowOnlySelections(popover); - const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - const noSelectionsDiv = findTestSubject( - availableOptionsDiv, + const availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + const noSelectionsDiv = within(availableOptionsDiv).getByTestId( 'optionsList-control-selectionsEmptyMessage' ); - expect(noSelectionsDiv.exists()).toBeTruthy(); + expect(noSelectionsDiv).toBeInTheDocument(); }); test('show only selected options', async () => { @@ -85,11 +79,11 @@ describe('Options list popover', () => { explicitInput: { selectedOptions: selections }, }); clickShowOnlySelections(popover); - const availableOptions = popover.find( - '[data-test-subj="optionsList-control-available-options"] ul' - ); - availableOptions.children().forEach((child, i) => { - expect(child.text()).toBe(`${selections[i]}. Checked option.`); + const availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + const availableOptionsList = within(availableOptionsDiv).getByRole('listbox'); + const availableOptions = within(availableOptionsList).getAllByRole('option'); + availableOptions.forEach((child, i) => { + expect(child).toHaveTextContent(`${selections[i]}. Checked option.`); }); }); @@ -99,16 +93,16 @@ describe('Options list popover', () => { explicitInput: { selectedOptions: selections }, componentState: { field: { type: 'string' } as any as FieldSpec }, }); - let searchBox = findTestSubject(popover, 'optionsList-control-search-input'); - let sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - expect(searchBox.prop('disabled')).toBeFalsy(); - expect(sortButton.prop('disabled')).toBeFalsy(); + let searchBox = popover.getByTestId('optionsList-control-search-input'); + let sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + expect(searchBox).not.toBeDisabled(); + expect(sortButton).not.toBeDisabled(); clickShowOnlySelections(popover); - searchBox = findTestSubject(popover, 'optionsList-control-search-input'); - sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - expect(searchBox.prop('disabled')).toBe(true); - expect(sortButton.prop('disabled')).toBe(true); + searchBox = popover.getByTestId('optionsList-control-search-input'); + sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + expect(searchBox).toBeDisabled(); + expect(sortButton).toBeDisabled(); }); }); @@ -124,23 +118,16 @@ describe('Options list popover', () => { invalidSelections: ['woof'], }, }); - const validSelection = findTestSubject(popover, 'optionsList-control-selection-bark'); - expect(validSelection.find('.euiSelectableListItem__text').text()).toEqual( - 'bark. Checked option.' - ); + const validSelection = popover.getByTestId('optionsList-control-selection-bark'); + expect(validSelection).toHaveTextContent('bark. Checked option.'); expect( - validSelection.find('div[data-test-subj="optionsList-document-count-badge"]').text().trim() - ).toEqual('75'); - const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); - expect(title).toEqual('Ignored selection'); - const invalidSelection = findTestSubject( - popover, - 'optionsList-control-ignored-selection-woof' - ); - expect(invalidSelection.find('.euiSelectableListItem__text').text()).toEqual( - 'woof. Checked option.' - ); - expect(invalidSelection.hasClass('optionsList__selectionInvalid')).toBe(true); + within(validSelection).getByTestId('optionsList-document-count-badge') + ).toHaveTextContent('75'); + const title = popover.getByTestId('optionList__invalidSelectionLabel'); + expect(title).toHaveTextContent('Invalid selection'); + const invalidSelection = popover.getByTestId('optionsList-control-invalid-selection-woof'); + expect(invalidSelection).toHaveTextContent('woof. Checked option.'); + expect(invalidSelection).toHaveClass('optionsList__selectionInvalid'); }); test('test title when multiple invalid selections', async () => { @@ -152,28 +139,28 @@ describe('Options list popover', () => { invalidSelections: ['woof', 'meow'], }, }); - const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); - expect(title).toEqual('Ignored selections'); + const title = popover.getByTestId('optionList__invalidSelectionLabel'); + expect(title).toHaveTextContent('Invalid selections'); }); }); describe('include/exclude toggle', () => { test('should default to exclude = false', async () => { const popover = await mountComponent(); - const includeButton = findTestSubject(popover, 'optionsList__includeResults'); - const excludeButton = findTestSubject(popover, 'optionsList__excludeResults'); - expect(includeButton.prop('aria-pressed')).toBe(true); - expect(excludeButton.prop('aria-pressed')).toBe(false); + const includeButton = popover.getByTestId('optionsList__includeResults'); + const excludeButton = popover.getByTestId('optionsList__excludeResults'); + expect(includeButton).toHaveAttribute('aria-pressed', 'true'); + expect(excludeButton).toHaveAttribute('aria-pressed', 'false'); }); test('if exclude = true, select appropriate button in button group', async () => { const popover = await mountComponent({ explicitInput: { exclude: true }, }); - const includeButton = findTestSubject(popover, 'optionsList__includeResults'); - const excludeButton = findTestSubject(popover, 'optionsList__excludeResults'); - expect(includeButton.prop('aria-pressed')).toBe(false); - expect(excludeButton.prop('aria-pressed')).toBe(true); + const includeButton = popover.getByTestId('optionsList__includeResults'); + const excludeButton = popover.getByTestId('optionsList__excludeResults'); + expect(includeButton).toHaveAttribute('aria-pressed', 'false'); + expect(excludeButton).toHaveAttribute('aria-pressed', 'true'); }); }); @@ -182,14 +169,16 @@ describe('Options list popover', () => { const popover = await mountComponent({ explicitInput: { existsSelected: true }, }); - const woofOption = findTestSubject(popover, 'optionsList-control-selection-woof'); - woofOption.simulate('click'); + const woofOption = popover.getByTestId('optionsList-control-selection-woof'); + userEvent.click(woofOption); - const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - availableOptionsDiv.children().forEach((child, i) => { - if (child.text() === 'woof') expect(child.prop('aria-pressed')).toBe(true); - else expect(child.prop('aria-pressed')).toBeFalsy(); + const availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + const availableOptionsList = within(availableOptionsDiv).getByRole('listbox'); + const selectedOptions = within(availableOptionsList).getAllByRole('option', { + checked: true, }); + expect(selectedOptions).toHaveLength(1); + expect(selectedOptions[0]).toHaveTextContent('woof. Checked option.'); }); test('clicking "Exists" unselects all other selections', async () => { @@ -197,19 +186,18 @@ describe('Options list popover', () => { const popover = await mountComponent({ explicitInput: { existsSelected: false, selectedOptions: selections }, }); - const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); - let availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - availableOptionsDiv.children().forEach((child, i) => { - if (selections.includes(child.text())) expect(child.prop('aria-pressed')).toBe(true); - else expect(child.prop('aria-pressed')).toBeFalsy(); - }); - - existsOption.simulate('click'); - availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - availableOptionsDiv.children().forEach((child, i) => { - if (child.text() === 'Exists (*)') expect(child.prop('aria-pressed')).toBe(true); - else expect(child.prop('aria-pressed')).toBeFalsy(); - }); + const existsOption = popover.getByTestId('optionsList-control-selection-exists'); + let availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + let checkedOptions = within(availableOptionsDiv).getAllByRole('option', { checked: true }); + expect(checkedOptions).toHaveLength(2); + expect(checkedOptions[0]).toHaveTextContent('woof. Checked option.'); + expect(checkedOptions[1]).toHaveTextContent('bark. Checked option.'); + + userEvent.click(existsOption); + availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + checkedOptions = within(availableOptionsDiv).getAllByRole('option', { checked: true }); + expect(checkedOptions).toHaveLength(1); + expect(checkedOptions[0]).toHaveTextContent('Exists. Checked option.'); }); test('if existsSelected = false and no suggestions, then "Exists" does not show up', async () => { @@ -217,8 +205,8 @@ describe('Options list popover', () => { componentState: { availableOptions: [] }, explicitInput: { existsSelected: false }, }); - const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); - expect(existsOption.exists()).toBeFalsy(); + const existsOption = popover.queryByTestId('optionsList-control-selection-exists'); + expect(existsOption).toBeNull(); }); test('if existsSelected = true, "Exists" is the only option when "Show only selected options" is toggled', async () => { @@ -226,10 +214,10 @@ describe('Options list popover', () => { explicitInput: { existsSelected: true }, }); clickShowOnlySelections(popover); - const availableOptions = popover.find( - '[data-test-subj="optionsList-control-available-options"] ul' - ); - expect(availableOptions.text()).toBe('Exists. Checked option.'); + const availableOptionsDiv = popover.getByTestId('optionsList-control-available-options'); + const availableOptionsList = within(availableOptionsDiv).getByRole('listbox'); + const availableOptions = within(availableOptionsList).getAllByRole('option'); + expect(availableOptions[0]).toHaveTextContent('Exists. Checked option.'); }); }); @@ -240,11 +228,13 @@ describe('Options list popover', () => { field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, }, }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); + const sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + userEvent.click(sortButton); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + const sortingOptionsDiv = popover.getByTestId('optionsListControl__sortingOptions'); + const optionsText = within(sortingOptionsDiv) + .getAllByRole('option') + .map((el) => el.textContent); expect(optionsText).toEqual(['By document count. Checked option.', 'Alphabetically']); }); @@ -255,16 +245,18 @@ describe('Options list popover', () => { field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, }, }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); + const sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + userEvent.click(sortButton); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + const sortingOptionsDiv = popover.getByTestId('optionsListControl__sortingOptions'); + const optionsText = within(sortingOptionsDiv) + .getAllByRole('option') + .map((el) => el.textContent); expect(optionsText).toEqual(['By document count', 'Alphabetically. Checked option.']); - const ascendingButton = findTestSubject(popover, 'optionsList__sortOrder_asc').instance(); + const ascendingButton = popover.getByTestId('optionsList__sortOrder_asc'); expect(ascendingButton).toHaveClass('euiButtonGroupButton-isSelected'); - const descendingButton = findTestSubject(popover, 'optionsList__sortOrder_desc').instance(); + const descendingButton = popover.getByTestId('optionsList__sortOrder_desc'); expect(descendingButton).not.toHaveClass('euiButtonGroupButton-isSelected'); }); @@ -272,11 +264,13 @@ describe('Options list popover', () => { const popover = await mountComponent({ componentState: { field: { name: 'Test IP field', type: 'ip' } as FieldSpec }, }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); + const sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + userEvent.click(sortButton); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + const sortingOptionsDiv = popover.getByTestId('optionsListControl__sortingOptions'); + const optionsText = within(sortingOptionsDiv) + .getAllByRole('option') + .map((el) => el.textContent); expect(optionsText).toEqual(['By document count. Checked option.']); }); @@ -284,11 +278,13 @@ describe('Options list popover', () => { const popover = await mountComponent({ componentState: { field: { name: 'Test date field', type: 'date' } as FieldSpec }, }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); + const sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + userEvent.click(sortButton); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + const sortingOptionsDiv = popover.getByTestId('optionsListControl__sortingOptions'); + const optionsText = within(sortingOptionsDiv) + .getAllByRole('option') + .map((el) => el.textContent); expect(optionsText).toEqual(['By document count. Checked option.', 'By date']); }); @@ -296,11 +292,13 @@ describe('Options list popover', () => { const popover = await mountComponent({ componentState: { field: { name: 'Test number field', type: 'number' } as FieldSpec }, }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); + const sortButton = popover.getByTestId('optionsListControl__sortingOptionsButton'); + userEvent.click(sortButton); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + const sortingOptionsDiv = popover.getByTestId('optionsListControl__sortingOptions'); + const optionsText = within(sortingOptionsDiv) + .getAllByRole('option') + .map((el) => el.textContent); expect(optionsText).toEqual(['By document count. Checked option.', 'Numerically']); }); }); @@ -310,8 +308,8 @@ describe('Options list popover', () => { const popover = await mountComponent({ componentState: { field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec }, }); - const warning = findTestSubject(popover, 'optionsList-allow-expensive-queries-warning'); - expect(warning).toEqual({}); + const warning = popover.queryByTestId('optionsList-allow-expensive-queries-warning'); + expect(warning).toBeNull(); }); test('ensure warning icon shows up when testAllowExpensiveQueries = false', async () => { @@ -324,8 +322,8 @@ describe('Options list popover', () => { allowExpensiveQueries: false, }, }); - const warning = findTestSubject(popover, 'optionsList-allow-expensive-queries-warning'); - expect(warning.getDOMNode()).toBeInstanceOf(HTMLDivElement); + const warning = popover.getByTestId('optionsList-allow-expensive-queries-warning'); + expect(warning).toBeInstanceOf(HTMLDivElement); }); }); @@ -340,8 +338,8 @@ describe('Options list popover', () => { const popover = await mountComponent({ explicitInput, }); - const test = findTestSubject(popover, testSubject); - expect(test.exists()).toBeFalsy(); + const test = popover.queryByTestId(testSubject); + expect(test).toBeNull(); }; test('can hide exists option', async () => { diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx index 7ff64482b2c60..4de8a2f483ea0 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx @@ -9,16 +9,19 @@ import React, { useEffect, useState } from 'react'; import { - EuiSelectableOption, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiScreenReaderOnly, EuiSelectable, + EuiSelectableOption, EuiSpacer, EuiTitle, - EuiScreenReaderOnly, } from '@elastic/eui'; -import { OptionsListStrings } from './options_list_strings'; -import { useOptionsList } from '../embeddable/options_list_embeddable'; import { useFieldFormatter } from '../../hooks/use_field_formatter'; +import { useOptionsList } from '../embeddable/options_list_embeddable'; +import { OptionsListStrings } from './options_list_strings'; export const OptionsListPopoverInvalidSelections = () => { const optionsList = useOptionsList(); @@ -40,7 +43,7 @@ export const OptionsListPopoverInvalidSelections = () => { label: fieldFormatter(key), checked: 'on', className: 'optionsList__selectionInvalid', - 'data-test-subj': `optionsList-control-ignored-selection-${key}`, + 'data-test-subj': `optionsList-control-invalid-selection-${key}`, prepend: (
@@ -60,13 +63,25 @@ export const OptionsListPopoverInvalidSelections = () => { - + + + + + + + + + i18n.translate('controls.optionsList.control.invalidSelectionWarningLabel', { + defaultMessage: + '{invalidSelectionCount} {invalidSelectionCount, plural, one {selection returns} other {selections return}} no results.', + values: { + invalidSelectionCount, + }, + }), }, editor: { getSelectionOptionsTitle: () => @@ -43,7 +51,7 @@ export const OptionsListStrings = { multi: { getLabel: () => i18n.translate('controls.optionsList.editor.multiSelectLabel', { - defaultMessage: 'Allow multiple selections', + defaultMessage: 'Allow multiple selections', }), }, single: { @@ -195,19 +203,19 @@ export const OptionsListStrings = { getInvalidSelectionsSectionAriaLabel: (fieldName: string, invalidSelectionCount: number) => i18n.translate('controls.optionsList.popover.invalidSelectionsAriaLabel', { defaultMessage: - 'Ignored {invalidSelectionCount, plural, one {selection} other {selections}} for {fieldName}', + 'Invalid {invalidSelectionCount, plural, one {selection} other {selections}} for {fieldName}', values: { fieldName, invalidSelectionCount }, }), getInvalidSelectionsSectionTitle: (invalidSelectionCount: number) => i18n.translate('controls.optionsList.popover.invalidSelectionsSectionTitle', { defaultMessage: - 'Ignored {invalidSelectionCount, plural, one {selection} other {selections}}', + 'Invalid {invalidSelectionCount, plural, one {selection} other {selections}}', values: { invalidSelectionCount }, }), getInvalidSelectionsLabel: (selectedOptions: number) => i18n.translate('controls.optionsList.popover.invalidSelectionsLabel', { defaultMessage: - '{selectedOptions} {selectedOptions, plural, one {selection} other {selections}} ignored', + '{selectedOptions} {selectedOptions, plural, one {selection} other {selections}} invalid', values: { selectedOptions }, }), getInvalidSelectionScreenReaderText: () => diff --git a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx index 0426523c09107..ea3d4d80399c5 100644 --- a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx +++ b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx @@ -6,41 +6,42 @@ * Side Public License, v 1. */ -import ReactDOM from 'react-dom'; -import { batch } from 'react-redux'; import deepEqual from 'fast-deep-equal'; import { isEmpty, isEqual } from 'lodash'; -import { merge, Subject, Subscription, switchMap, tap } from 'rxjs'; import React, { createContext, useContext } from 'react'; -import { debounceTime, map, distinctUntilChanged, skip } from 'rxjs/operators'; +import ReactDOM from 'react-dom'; +import { batch } from 'react-redux'; +import { merge, Subject, Subscription, switchMap, tap } from 'rxjs'; +import { debounceTime, distinctUntilChanged, map, skip } from 'rxjs/operators'; +import { DataView, FieldSpec } from '@kbn/data-views-plugin/public'; +import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; import { - Filter, - compareFilters, + buildExistsFilter, buildPhraseFilter, buildPhrasesFilter, + compareFilters, COMPARE_ALL_OPTIONS, - buildExistsFilter, + Filter, } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import { DataView, FieldSpec } from '@kbn/data-views-plugin/public'; -import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; -import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { + ControlGroupContainer, ControlInput, ControlOutput, - OPTIONS_LIST_CONTROL, OptionsListEmbeddableInput, + OPTIONS_LIST_CONTROL, } from '../..'; import { pluginServices } from '../../services'; -import { IClearableControl } from '../../types'; -import { OptionsListControl } from '../components/options_list_control'; import { ControlsDataViewsService } from '../../services/data_views/types'; import { ControlsOptionsListService } from '../../services/options_list/types'; -import { MIN_OPTIONS_LIST_REQUEST_SIZE, OptionsListReduxState } from '../types'; +import { IClearableControl } from '../../types'; +import { OptionsListControl } from '../components/options_list_control'; import { getDefaultComponentState, optionsListReducers } from '../options_list_reducers'; +import { MIN_OPTIONS_LIST_REQUEST_SIZE, OptionsListReduxState } from '../types'; const diffDataFetchProps = ( last?: OptionsListDataFetchProps, @@ -83,6 +84,7 @@ export class OptionsListEmbeddable { public readonly type = OPTIONS_LIST_CONTROL; public deferEmbeddableLoad = true; + public parent: ControlGroupContainer; private subscriptions: Subscription = new Subscription(); private node?: HTMLElement; @@ -113,6 +115,7 @@ export class OptionsListEmbeddable parent?: IContainer ) { super(input, output, parent); + this.parent = parent as ControlGroupContainer; // Destructure controls services ({ dataViews: this.dataViewsService, optionsList: this.optionsListService } = @@ -130,7 +133,6 @@ export class OptionsListEmbeddable reducers: optionsListReducers, initialComponentState: getDefaultComponentState(), }); - this.select = reduxEmbeddableTools.select; this.getState = reduxEmbeddableTools.getState; this.dispatch = reduxEmbeddableTools.dispatch; @@ -142,17 +144,17 @@ export class OptionsListEmbeddable private initialize = async () => { const { selectedOptions: initialSelectedOptions } = this.getInput(); - if (!initialSelectedOptions) this.setInitializationFinished(); + if (initialSelectedOptions) { + const filters = await this.buildFilter(); + this.dispatch.publishFilters(filters); + } + this.setInitializationFinished(); this.dispatch.setAllowExpensiveQueries( await this.optionsListService.getAllowExpensiveQueries() ); this.runOptionsListQuery().then(async () => { - if (initialSelectedOptions) { - await this.buildFilter(); - this.setInitializationFinished(); - } this.setupSubscriptions(); }); }; @@ -324,6 +326,7 @@ export class OptionsListEmbeddable }, this.abortController.signal ); + if (this.optionsListService.optionsListResponseWasFailure(response)) { if (response.error === 'aborted') { // This prevents an aborted request (which can happen, for example, when a user types a search string too quickly) @@ -347,6 +350,7 @@ export class OptionsListEmbeddable validSelections: selectedOptions, totalCardinality, }); + this.reportInvalidSelections(false); } else { const valid: string[] = []; const invalid: string[] = []; @@ -360,14 +364,12 @@ export class OptionsListEmbeddable validSelections: valid, totalCardinality, }); + this.reportInvalidSelections(true); } - // publish filter - const newFilters = await this.buildFilter(); batch(() => { this.dispatch.setErrorMessage(undefined); this.dispatch.setLoading(false); - this.dispatch.publishFilters(newFilters); }); } else { batch(() => { @@ -380,12 +382,18 @@ export class OptionsListEmbeddable } }; + private reportInvalidSelections = (hasInvalidSelections: boolean) => { + this.parent?.reportInvalidSelections({ + id: this.id, + hasInvalidSelections, + }); + }; + private buildFilter = async () => { - const { validSelections } = this.getState().componentState ?? {}; - const { existsSelected } = this.getState().explicitInput ?? {}; + const { existsSelected, selectedOptions } = this.getState().explicitInput ?? {}; const { exclude } = this.getInput(); - if ((!validSelections || isEmpty(validSelections)) && !existsSelected) { + if ((!selectedOptions || isEmpty(selectedOptions)) && !existsSelected) { return []; } const { dataView, field } = await this.getCurrentDataViewAndField(); @@ -394,11 +402,11 @@ export class OptionsListEmbeddable let newFilter: Filter | undefined; if (existsSelected) { newFilter = buildExistsFilter(field, dataView); - } else if (validSelections) { - if (validSelections.length === 1) { - newFilter = buildPhraseFilter(field, validSelections[0], dataView); + } else if (selectedOptions) { + if (selectedOptions.length === 1) { + newFilter = buildPhraseFilter(field, selectedOptions[0], dataView); } else { - newFilter = buildPhrasesFilter(field, validSelections, dataView); + newFilter = buildPhrasesFilter(field, selectedOptions, dataView); } } @@ -411,6 +419,7 @@ export class OptionsListEmbeddable public clearSelections() { this.dispatch.clearSelections({}); + this.reportInvalidSelections(false); } reload = () => { diff --git a/src/plugins/controls/public/options_list/options_list_reducers.ts b/src/plugins/controls/public/options_list/options_list_reducers.ts index 3300072c089f9..488a20313c8d9 100644 --- a/src/plugins/controls/public/options_list/options_list_reducers.ts +++ b/src/plugins/controls/public/options_list/options_list_reducers.ts @@ -50,6 +50,12 @@ export const optionsListReducers = { ) => { state.componentState.allowExpensiveQueries = action.payload; }, + setInvalidSelectionWarningOpen: ( + state: WritableDraft, + action: PayloadAction + ) => { + state.componentState.showInvalidSelectionWarning = action.payload; + }, setPopoverOpen: (state: WritableDraft, action: PayloadAction) => { state.componentState.popoverOpen = action.payload; }, diff --git a/src/plugins/controls/public/options_list/types.ts b/src/plugins/controls/public/options_list/types.ts index 30b561d5e7964..da3f52a4cb870 100644 --- a/src/plugins/controls/public/options_list/types.ts +++ b/src/plugins/controls/public/options_list/types.ts @@ -34,6 +34,7 @@ export interface OptionsListComponentState { popoverOpen: boolean; field?: FieldSpec; error?: string; + showInvalidSelectionWarning?: boolean; } // public only - redux embeddable state type diff --git a/src/plugins/controls/public/range_slider/components/range_slider.scss b/src/plugins/controls/public/range_slider/components/range_slider.scss index c386705382d87..33795ea6f5286 100644 --- a/src/plugins/controls/public/range_slider/components/range_slider.scss +++ b/src/plugins/controls/public/range_slider/components/range_slider.scss @@ -1,14 +1,30 @@ .rangeSliderAnchor__button { .euiFormControlLayout { + align-items: center; box-shadow: none; background-color: transparent; - padding: 0 0 2px 0; .euiFormControlLayout__childrenWrapper { border-top-left-radius: 0; border-bottom-left-radius: 0; border-top-right-radius: $euiBorderRadius - 1px; border-bottom-right-radius: $euiBorderRadius - 1px; + + .euiFormControlLayoutDelimited__delimiter, .euiFormControlLayoutIcons--static { + height: auto !important; + } + } + } + + .rangeSlider__invalidToken { + height: $euiSizeS * 2; + padding: 0 $euiSizeS; + + .euiIcon { + background-color: transparent; + width: $euiSizeS * 2; + border-radius: $euiSizeXS; + padding: 0 calc($euiSizeXS / 2); } } } @@ -24,9 +40,7 @@ } &.rangeSliderAnchor__fieldNumber--invalid { - color: $euiTextSubduedColor; - text-decoration: line-through; - font-weight: $euiFontWeightRegular; + color: $euiColorWarningText; } &:placeholder-shown, &::placeholder { @@ -34,4 +48,4 @@ color: $euiTextSubduedColor; text-decoration: none; } -} \ No newline at end of file +} diff --git a/src/plugins/controls/public/range_slider/components/range_slider_control.tsx b/src/plugins/controls/public/range_slider/components/range_slider_control.tsx index cd879961c5f10..86bf298da91c7 100644 --- a/src/plugins/controls/public/range_slider/components/range_slider_control.tsx +++ b/src/plugins/controls/public/range_slider/components/range_slider_control.tsx @@ -9,7 +9,7 @@ import { debounce } from 'lodash'; import React, { FC, useState, useMemo, useEffect, useCallback, useRef } from 'react'; -import { EuiRangeTick, EuiDualRange, EuiDualRangeProps } from '@elastic/eui'; +import { EuiRangeTick, EuiDualRange, EuiDualRangeProps, EuiToken, EuiToolTip } from '@elastic/eui'; import { RangeValue } from '../../../common/range_slider/types'; import { useRangeSlider } from '../embeddable/range_slider_embeddable'; @@ -18,6 +18,7 @@ import { ControlError } from '../../control_group/component/control_error_compon import './range_slider.scss'; import { MIN_POPOVER_WIDTH } from '../../constants'; import { useFieldFormatter } from '../../hooks/use_field_formatter'; +import { RangeSliderStrings } from './range_slider_strings'; export const RangeSliderControl: FC = () => { /** Controls Services Context */ @@ -164,6 +165,27 @@ export const RangeSliderControl: FC = () => { inputPopoverProps={{ panelMinWidth: MIN_POPOVER_WIDTH, }} + append={ + isInvalid ? ( +
+ + + +
+ ) : undefined + } onMouseUp={() => { // when the pin is dropped (on mouse up), cancel any pending debounced changes and force the change // in value to happen instantly (which, in turn, will re-calculate the min/max for the slider due to diff --git a/src/plugins/controls/public/range_slider/components/range_slider_strings.ts b/src/plugins/controls/public/range_slider/components/range_slider_strings.ts index c37dc49c5306a..a0ed2a051f94a 100644 --- a/src/plugins/controls/public/range_slider/components/range_slider_strings.ts +++ b/src/plugins/controls/public/range_slider/components/range_slider_strings.ts @@ -9,6 +9,12 @@ import { i18n } from '@kbn/i18n'; export const RangeSliderStrings = { + control: { + getInvalidSelectionWarningLabel: () => + i18n.translate('controls.rangeSlider.control.invalidSelectionWarningLabel', { + defaultMessage: 'Selected range returns no results.', + }), + }, editor: { getStepTitle: () => i18n.translate('controls.rangeSlider.editor.stepSizeTitle', { diff --git a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.test.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.test.tsx index 9629e78dd5285..6a07cd531f8e5 100644 --- a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.test.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.test.tsx @@ -132,7 +132,7 @@ describe('initialize', () => { await new Promise((resolve) => process.nextTick(resolve)); const reduxState = control.getState(); - expect(reduxState.output.filters?.length).toBe(0); + expect(reduxState.output.filters?.length).toBe(1); expect(reduxState.componentState.isInvalid).toBe(true); }); diff --git a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx index 6ac09cbbaea5a..6efc6e9fc1a65 100644 --- a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx @@ -12,7 +12,7 @@ import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; import { batch } from 'react-redux'; import { lastValueFrom, Subscription, switchMap } from 'rxjs'; -import { distinctUntilChanged, map, skip } from 'rxjs/operators'; +import { distinctUntilChanged, map } from 'rxjs/operators'; import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; @@ -28,6 +28,7 @@ import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util- import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { + ControlGroupContainer, ControlInput, ControlOutput, RangeSliderEmbeddableInput, @@ -81,6 +82,7 @@ export class RangeSliderEmbeddable { public readonly type = RANGE_SLIDER_CONTROL; public deferEmbeddableLoad = true; + public parent: ControlGroupContainer; private subscriptions: Subscription = new Subscription(); private node?: HTMLElement; @@ -92,7 +94,6 @@ export class RangeSliderEmbeddable // Internal data fetching state for this input control. private dataView?: DataView; private field?: DataViewField; - private filters: Filter[] = []; // state management public select: RangeSliderReduxEmbeddableTools['select']; @@ -109,6 +110,7 @@ export class RangeSliderEmbeddable parent?: IContainer ) { super(input, output, parent); // get filters for initial output... + this.parent = parent as ControlGroupContainer; // Destructure controls services ({ data: this.dataService, dataViews: this.dataViewsService } = pluginServices.getServices()); @@ -126,27 +128,23 @@ export class RangeSliderEmbeddable this.dispatch = reduxEmbeddableTools.dispatch; this.onStateChange = reduxEmbeddableTools.onStateChange; this.cleanupStateTools = reduxEmbeddableTools.cleanup; + this.initialize(); } private initialize = async () => { - const initialValue = this.getInput().value; - if (!initialValue) { - this.setInitializationFinished(); - } - - try { - await this.runRangeSliderQuery(); - await this.buildFilter(); - } catch (e) { - this.onLoadingError(e.message); - } - - if (initialValue) { - this.setInitializationFinished(); + const [initialMin, initialMax] = this.getInput().value ?? []; + if (!isEmpty(initialMin) || !isEmpty(initialMax)) { + const filter = await this.buildFilter(); + this.dispatch.publishFilters(filter); } + this.setInitializationFinished(); - this.setupSubscriptions(); + this.runRangeSliderQuery() + .then(async () => { + this.setupSubscriptions(); + }) + .catch((e) => this.onLoadingError(e.message)); }; private setupSubscriptions = () => { @@ -161,18 +159,22 @@ export class RangeSliderEmbeddable filters: newInput.filters, query: newInput.query, })), - distinctUntilChanged(diffDataFetchProps), - skip(1) + distinctUntilChanged(diffDataFetchProps) + ); + + const valueChangePipe = this.getInput$().pipe( + distinctUntilChanged((a, b) => isEqual(a.value ?? ['', ''], b.value ?? ['', ''])) ); - // fetch available min/max when input changes this.subscriptions.add( dataFetchPipe .pipe( - switchMap(async (changes) => { + switchMap(async () => { try { + this.dispatch.setLoading(true); await this.runRangeSliderQuery(); - await this.buildFilter(); + await this.runValidations(); + this.dispatch.setLoading(false); } catch (e) { this.onLoadingError(e.message); } @@ -181,13 +183,21 @@ export class RangeSliderEmbeddable .subscribe() ); - // build filters when value changes + // publish filters when value changes this.subscriptions.add( - this.getInput$() + valueChangePipe .pipe( - distinctUntilChanged((a, b) => isEqual(a.value ?? ['', ''], b.value ?? ['', ''])), - skip(1), // skip the first input update because initial filters will be built by initialize. - switchMap(this.buildFilter) + switchMap(async () => { + try { + this.dispatch.setLoading(true); + const rangeFilter = await this.buildFilter(); + this.dispatch.publishFilters(rangeFilter); + await this.runValidations(); + this.dispatch.setLoading(false); + } catch (e) { + this.onLoadingError(e.message); + } + }) ) .subscribe() ); @@ -228,39 +238,18 @@ export class RangeSliderEmbeddable }; private runRangeSliderQuery = async () => { - this.dispatch.setLoading(true); - const { dataView, field } = await this.getCurrentDataViewAndField(); if (!dataView || !field) return; - const embeddableInput = this.getInput(); - const { ignoreParentSettings, timeRange: globalTimeRange, timeslice } = embeddableInput; - let { filters = [] } = embeddableInput; - - const timeRange = - timeslice !== undefined - ? { - from: new Date(timeslice[0]).toISOString(), - to: new Date(timeslice[1]).toISOString(), - mode: 'absolute' as 'absolute', - } - : globalTimeRange; - if (!ignoreParentSettings?.ignoreTimerange && timeRange) { - const timeFilter = this.dataService.timefilter.createFilter(dataView, timeRange); - if (timeFilter) { - filters = filters.concat(timeFilter); - } - } - - this.filters = filters; const { min, max } = await this.fetchMinMax({ dataView, field, }); - this.dispatch.setMinMax({ - min, - max, + batch(() => { + this.dispatch.setMinMax({ min, max }); + this.dispatch.setDataViewId(dataView.id); + this.dispatch.setErrorMessage(undefined); }); }; @@ -271,15 +260,11 @@ export class RangeSliderEmbeddable dataView: DataView; field: DataViewField; }): Promise<{ min?: number; max?: number }> => { + const { query } = this.getInput(); const searchSource = await this.dataService.searchSource.create(); searchSource.setField('size', 0); searchSource.setField('index', dataView); - - const { ignoreParentSettings, query } = this.getInput(); - - if (!ignoreParentSettings?.ignoreFilters) { - searchSource.setField('filter', this.filters); - } + searchSource.setField('filter', this.getGlobalFilters(dataView)); if (query) { searchSource.setField('query', query); @@ -317,40 +302,25 @@ export class RangeSliderEmbeddable private buildFilter = async () => { const { - componentState: { min: availableMin, max: availableMax }, explicitInput: { value }, } = this.getState(); - const { ignoreParentSettings, query } = this.getInput(); - const [selectedMin, selectedMax] = value ?? ['', '']; - const hasData = availableMin !== undefined && availableMax !== undefined; - const hasLowerSelection = !isEmpty(selectedMin); - const hasUpperSelection = !isEmpty(selectedMax); - const hasEitherSelection = hasLowerSelection || hasUpperSelection; + const [min, max] = [selectedMin, selectedMax].map(parseFloat); const { dataView, field } = await this.getCurrentDataViewAndField(); - if (!dataView || !field) return; + if (!dataView || !field) return []; - if (!hasData || !hasEitherSelection) { - batch(() => { - this.dispatch.setLoading(false); - this.dispatch.setIsInvalid(!ignoreParentSettings?.ignoreValidations && hasEitherSelection); - this.dispatch.setDataViewId(dataView.id); - this.dispatch.publishFilters([]); - this.dispatch.setErrorMessage(undefined); - }); - return; - } + if (isEmpty(selectedMin) && isEmpty(selectedMax)) return []; const params = {} as RangeFilterParams; if (selectedMin) { - params.gte = Math.max(parseFloat(selectedMin), availableMin); + params.gte = min; } if (selectedMax) { - params.lte = Math.min(parseFloat(selectedMax), availableMax); + params.lte = max; } const rangeFilter = buildRangeFilter(field, params, dataView); @@ -358,11 +328,60 @@ export class RangeSliderEmbeddable rangeFilter.meta.type = 'range'; rangeFilter.meta.params = params; + return [rangeFilter]; + }; + + private onLoadingError(errorMessage: string) { + batch(() => { + this.dispatch.setLoading(false); + this.dispatch.publishFilters([]); + this.dispatch.setErrorMessage(errorMessage); + }); + } + + private getGlobalFilters = (dataView: DataView) => { + const { + filters: globalFilters, + ignoreParentSettings, + timeRange: globalTimeRange, + timeslice, + } = this.getInput(); + + const filters: Filter[] = []; + + if (!ignoreParentSettings?.ignoreFilters && globalFilters) { + filters.push(...globalFilters); + } + + const timeRange = + timeslice !== undefined + ? { + from: new Date(timeslice[0]).toISOString(), + to: new Date(timeslice[1]).toISOString(), + mode: 'absolute' as 'absolute', + } + : globalTimeRange; + + if (!ignoreParentSettings?.ignoreTimerange && timeRange) { + const timeFilter = this.dataService.timefilter.createFilter(dataView, timeRange); + if (timeFilter) filters.push(timeFilter); + } + + return filters; + }; + + private runValidations = async () => { + const { dataView } = await this.getCurrentDataViewAndField(); + if (!dataView) return; // Check if new range filter results in no data - if (!ignoreParentSettings?.ignoreValidations) { + const { ignoreParentSettings, query } = this.getInput(); + if (ignoreParentSettings?.ignoreValidations) { + this.dispatch.setIsInvalid(false); + } else { const searchSource = await this.dataService.searchSource.create(); - const filters = [...this.filters, rangeFilter]; + const { filters: rangeFilters = [] } = this.getOutput(); + const filters = this.getGlobalFilters(dataView).concat(rangeFilters); searchSource.setField('size', 0); searchSource.setField('index', dataView); @@ -375,43 +394,33 @@ export class RangeSliderEmbeddable const total = resp?.rawResponse?.hits?.total; const docCount = typeof total === 'number' ? total : total?.value; - if (!docCount) { - batch(() => { - this.dispatch.setLoading(false); - this.dispatch.setIsInvalid(true); - this.dispatch.setDataViewId(dataView.id); - this.dispatch.publishFilters([]); - this.dispatch.setErrorMessage(undefined); - }); - return; - } - } - batch(() => { - this.dispatch.setLoading(false); - this.dispatch.setIsInvalid(false); - this.dispatch.setDataViewId(dataView.id); - this.dispatch.publishFilters([rangeFilter]); - this.dispatch.setErrorMessage(undefined); - }); + const { + explicitInput: { value }, + } = this.getState(); + this.reportInvalidSelections( + !value || (value[0] === '' && value[1] === '') ? false : !docCount // don't set the range slider invalid if it has no selections + ); + } }; - private onLoadingError(errorMessage: string) { - batch(() => { - this.dispatch.setLoading(false); - this.dispatch.publishFilters([]); - this.dispatch.setErrorMessage(errorMessage); + private reportInvalidSelections = (hasInvalidSelections: boolean) => { + this.dispatch.setIsInvalid(hasInvalidSelections); + this.parent?.reportInvalidSelections({ + id: this.id, + hasInvalidSelections, }); - } + }; public clearSelections() { this.dispatch.setSelectedRange(['', '']); } public reload = async () => { + this.dispatch.setLoading(true); try { await this.runRangeSliderQuery(); - await this.buildFilter(); + this.dispatch.setLoading(false); } catch (e) { this.onLoadingError(e.message); } diff --git a/src/plugins/controls/public/services/core/core.story.ts b/src/plugins/controls/public/services/core/core.story.ts index 5549f15461553..dca544758970e 100644 --- a/src/plugins/controls/public/services/core/core.story.ts +++ b/src/plugins/controls/public/services/core/core.story.ts @@ -17,5 +17,6 @@ export const coreServiceFactory: CoreServiceFactory = () => { return { theme: themeServiceMock.createSetupContract(), i18n: corePluginMock.i18n, + notifications: corePluginMock.notifications, }; }; diff --git a/src/plugins/controls/public/services/core/core_service.ts b/src/plugins/controls/public/services/core/core_service.ts index 3a8ac7bb89098..ed63bc54e7a60 100644 --- a/src/plugins/controls/public/services/core/core_service.ts +++ b/src/plugins/controls/public/services/core/core_service.ts @@ -16,10 +16,11 @@ export type CoreServiceFactory = KibanaPluginServiceFactory< >; export const coreServiceFactory: CoreServiceFactory = ({ coreStart }) => { - const { theme, i18n } = coreStart; + const { theme, i18n, notifications } = coreStart; return { theme, i18n, + notifications, }; }; diff --git a/src/plugins/controls/public/services/core/types.ts b/src/plugins/controls/public/services/core/types.ts index f5a769c077163..cd2a167402ec1 100644 --- a/src/plugins/controls/public/services/core/types.ts +++ b/src/plugins/controls/public/services/core/types.ts @@ -11,4 +11,5 @@ import { CoreStart } from '@kbn/core/public'; export interface ControlsCoreService { i18n: CoreStart['i18n']; theme: CoreStart['theme']; + notifications: CoreStart['notifications']; } diff --git a/src/plugins/controls/public/services/plugin_services.story.ts b/src/plugins/controls/public/services/plugin_services.story.ts index e214349095695..1db9d3e201bfd 100644 --- a/src/plugins/controls/public/services/plugin_services.story.ts +++ b/src/plugins/controls/public/services/plugin_services.story.ts @@ -21,6 +21,7 @@ import { httpServiceFactory } from './http/http.stub'; import { optionsListServiceFactory } from './options_list/options_list.story'; import { overlaysServiceFactory } from './overlays/overlays.story'; import { settingsServiceFactory } from './settings/settings.story'; +import { storageServiceFactory } from './storage/storage_service.stub'; import { ControlsServices } from './types'; import { unifiedSearchServiceFactory } from './unified_search/unified_search.story'; @@ -33,7 +34,7 @@ export const providers: PluginServiceProviders = { settings: new PluginServiceProvider(settingsServiceFactory), core: new PluginServiceProvider(coreServiceFactory), embeddable: new PluginServiceProvider(embeddableServiceFactory), - + storage: new PluginServiceProvider(storageServiceFactory), controls: new PluginServiceProvider(controlsServiceFactory), optionsList: new PluginServiceProvider(optionsListServiceFactory), }; diff --git a/src/plugins/controls/public/services/plugin_services.stub.ts b/src/plugins/controls/public/services/plugin_services.stub.ts index 07c24d72ce630..ceadd25c9a349 100644 --- a/src/plugins/controls/public/services/plugin_services.stub.ts +++ b/src/plugins/controls/public/services/plugin_services.stub.ts @@ -26,6 +26,7 @@ import { overlaysServiceFactory } from './overlays/overlays.stub'; import { registry as stubRegistry } from './plugin_services.story'; import { settingsServiceFactory } from './settings/settings.story'; import { unifiedSearchServiceFactory } from './unified_search/unified_search.story'; +import { storageServiceFactory } from './storage/storage_service.stub'; export const providers: PluginServiceProviders = { embeddable: new PluginServiceProvider(embeddableServiceFactory), @@ -37,6 +38,7 @@ export const providers: PluginServiceProviders = { overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), core: new PluginServiceProvider(coreServiceFactory), + storage: new PluginServiceProvider(storageServiceFactory), unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), }; diff --git a/src/plugins/controls/public/services/plugin_services.ts b/src/plugins/controls/public/services/plugin_services.ts index e6caa3565ed60..b02a59265c13e 100644 --- a/src/plugins/controls/public/services/plugin_services.ts +++ b/src/plugins/controls/public/services/plugin_services.ts @@ -25,6 +25,7 @@ import { httpServiceFactory } from './http/http_service'; import { optionsListServiceFactory } from './options_list/options_list_service'; import { overlaysServiceFactory } from './overlays/overlays_service'; import { settingsServiceFactory } from './settings/settings_service'; +import { controlsStorageServiceFactory } from './storage/storage_service'; import { unifiedSearchServiceFactory } from './unified_search/unified_search_service'; export const providers: PluginServiceProviders< @@ -39,6 +40,7 @@ export const providers: PluginServiceProviders< optionsList: new PluginServiceProvider(optionsListServiceFactory, ['data', 'http']), overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), + storage: new PluginServiceProvider(controlsStorageServiceFactory), core: new PluginServiceProvider(coreServiceFactory), unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), }; diff --git a/src/plugins/controls/public/services/storage/storage_service.stub.ts b/src/plugins/controls/public/services/storage/storage_service.stub.ts new file mode 100644 index 0000000000000..14a2ff13a6138 --- /dev/null +++ b/src/plugins/controls/public/services/storage/storage_service.stub.ts @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; +import { ControlsStorageService } from './types'; + +type StorageServiceFactory = PluginServiceFactory; + +export const storageServiceFactory: StorageServiceFactory = () => { + return { + getShowInvalidSelectionWarning: () => false, + setShowInvalidSelectionWarning: (value: boolean) => null, + }; +}; diff --git a/src/plugins/controls/public/services/storage/storage_service.ts b/src/plugins/controls/public/services/storage/storage_service.ts new file mode 100644 index 0000000000000..db06b3d6a3c00 --- /dev/null +++ b/src/plugins/controls/public/services/storage/storage_service.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { ControlsStorageService } from './types'; + +const STORAGE_KEY = 'controls:showInvalidSelectionWarning'; + +class StorageService implements ControlsStorageService { + private storage: Storage; + + constructor() { + this.storage = new Storage(localStorage); + } + + getShowInvalidSelectionWarning = () => { + return this.storage.get(STORAGE_KEY); + }; + + setShowInvalidSelectionWarning = (value: boolean) => { + this.storage.set(STORAGE_KEY, value); + }; +} + +export const controlsStorageServiceFactory = () => new StorageService(); diff --git a/src/plugins/controls/public/services/storage/types.ts b/src/plugins/controls/public/services/storage/types.ts new file mode 100644 index 0000000000000..8bae4c70fbe27 --- /dev/null +++ b/src/plugins/controls/public/services/storage/types.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export interface ControlsStorageService { + getShowInvalidSelectionWarning: () => boolean; + setShowInvalidSelectionWarning: (value: boolean) => void; +} diff --git a/src/plugins/controls/public/services/types.ts b/src/plugins/controls/public/services/types.ts index bb86855302989..9160a63192916 100644 --- a/src/plugins/controls/public/services/types.ts +++ b/src/plugins/controls/public/services/types.ts @@ -15,6 +15,7 @@ import { ControlsHTTPService } from './http/types'; import { ControlsOptionsListService } from './options_list/types'; import { ControlsOverlaysService } from './overlays/types'; import { ControlsSettingsService } from './settings/types'; +import { ControlsStorageService } from './storage/types'; import { ControlsUnifiedSearchService } from './unified_search/types'; export interface ControlsServices { @@ -31,4 +32,5 @@ export interface ControlsServices { // controls plugin's own services controls: ControlsServiceType; optionsList: ControlsOptionsListService; + storage: ControlsStorageService; } diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index 57c5a9041dac2..db500566d3143 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -7,7 +7,14 @@ */ import './discover_layout.scss'; import React, { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { EuiPage, EuiPageBody, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; +import { + EuiPage, + EuiPageBody, + EuiPanel, + EuiProgress, + useEuiBackgroundColor, + EuiDelayRender, +} from '@elastic/eui'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { METRIC_TYPE } from '@kbn/analytics'; @@ -81,7 +88,10 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { if (uiSettings.get(SHOW_FIELD_STATISTICS) !== true) return VIEW_MODE.DOCUMENT_LEVEL; return state.viewMode ?? VIEW_MODE.DOCUMENT_LEVEL; }); - const dataView = useInternalStateSelector((state) => state.dataView!); + const [dataView, dataViewLoading] = useInternalStateSelector((state) => [ + state.dataView!, + state.isDataViewLoading, + ]); const dataState: DataMainMsg = useDataState(main$); const savedSearch = useSavedSearchInitial(); @@ -293,6 +303,11 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { height: 100%; `} > + {dataViewLoading && ( + + + + )} { const savedSearch = savedSearchMock; @@ -26,7 +28,14 @@ const setupTestParams = (dataView: DataView | undefined) => { discoverState.internalState.transitions.setDataView(savedSearch.searchSource.getField('index')!); services.dataViews.get = jest.fn(() => Promise.resolve(dataView as DataView)); discoverState.appState.update = jest.fn(); - return { services, appState: discoverState.appState, internalState: discoverState.internalState }; + discoverState.internalState.transitions = { + setIsDataViewLoading: jest.fn(), + } as unknown as Readonly>; + return { + services, + appState: discoverState.appState, + internalState: discoverState.internalState, + }; }; describe('changeDataView', () => { @@ -38,6 +47,8 @@ describe('changeDataView', () => { index: 'data-view-with-user-default-column-id', sort: [['@timestamp', 'desc']], }); + expect(params.internalState.transitions.setIsDataViewLoading).toHaveBeenNthCalledWith(1, true); + expect(params.internalState.transitions.setIsDataViewLoading).toHaveBeenNthCalledWith(2, false); }); it('should set the right app state when a valid data view to switch to is given', async () => { @@ -48,11 +59,15 @@ describe('changeDataView', () => { index: 'data-view-with-various-field-types-id', sort: [['data', 'desc']], }); + expect(params.internalState.transitions.setIsDataViewLoading).toHaveBeenNthCalledWith(1, true); + expect(params.internalState.transitions.setIsDataViewLoading).toHaveBeenNthCalledWith(2, false); }); it('should not set the app state when an invalid data view to switch to is given', async () => { const params = setupTestParams(undefined); await changeDataView('data-view-with-various-field-types', params); expect(params.appState.update).not.toHaveBeenCalled(); + expect(params.internalState.transitions.setIsDataViewLoading).toHaveBeenNthCalledWith(1, true); + expect(params.internalState.transitions.setIsDataViewLoading).toHaveBeenNthCalledWith(2, false); }); }); diff --git a/src/plugins/discover/public/application/main/hooks/utils/change_data_view.ts b/src/plugins/discover/public/application/main/hooks/utils/change_data_view.ts index 7218ea76b2ac6..41a911295cacd 100644 --- a/src/plugins/discover/public/application/main/hooks/utils/change_data_view.ts +++ b/src/plugins/discover/public/application/main/hooks/utils/change_data_view.ts @@ -39,6 +39,7 @@ export async function changeDataView( const dataView = internalState.getState().dataView; const state = appState.getState(); let nextDataView: DataView | null = null; + internalState.transitions.setIsDataViewLoading(true); try { nextDataView = typeof id === 'string' ? await dataViews.get(id, false) : id; @@ -63,4 +64,5 @@ export async function changeDataView( internalState.transitions.setExpandedDoc(undefined); } } + internalState.transitions.setIsDataViewLoading(false); } diff --git a/src/plugins/discover/public/application/main/services/discover_internal_state_container.ts b/src/plugins/discover/public/application/main/services/discover_internal_state_container.ts index ec8d7ec6c57f2..5825e4d2cef6c 100644 --- a/src/plugins/discover/public/application/main/services/discover_internal_state_container.ts +++ b/src/plugins/discover/public/application/main/services/discover_internal_state_container.ts @@ -17,14 +17,16 @@ import type { DataTableRecord } from '@kbn/discover-utils/types'; export interface InternalState { dataView: DataView | undefined; + isDataViewLoading: boolean; savedDataViews: DataViewListItem[]; adHocDataViews: DataView[]; expandedDoc: DataTableRecord | undefined; customFilters: Filter[]; } -interface InternalStateTransitions { +export interface InternalStateTransitions { setDataView: (state: InternalState) => (dataView: DataView) => InternalState; + setIsDataViewLoading: (state: InternalState) => (isLoading: boolean) => InternalState; setSavedDataViews: (state: InternalState) => (dataView: DataViewListItem[]) => InternalState; setAdHocDataViews: (state: InternalState) => (dataViews: DataView[]) => InternalState; appendAdHocDataViews: ( @@ -52,6 +54,7 @@ export function getInternalStateContainer() { return createStateContainer( { dataView: undefined, + isDataViewLoading: false, adHocDataViews: [], savedDataViews: [], expandedDoc: undefined, @@ -62,6 +65,10 @@ export function getInternalStateContainer() { ...prevState, dataView: nextDataView, }), + setIsDataViewLoading: (prevState: InternalState) => (loading: boolean) => ({ + ...prevState, + isDataViewLoading: loading, + }), setSavedDataViews: (prevState: InternalState) => (nextDataViewList: DataViewListItem[]) => ({ ...prevState, savedDataViews: nextDataViewList, diff --git a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts index 744ca9039d8c0..08fae23110972 100644 --- a/src/plugins/discover/public/application/main/utils/fetch_all.test.ts +++ b/src/plugins/discover/public/application/main/utils/fetch_all.test.ts @@ -72,6 +72,7 @@ describe('test fetchAll', () => { getAppState: () => ({}), getInternalState: () => ({ dataView: undefined, + isDataViewLoading: false, savedDataViews: [], adHocDataViews: [], expandedDoc: undefined, @@ -269,6 +270,7 @@ describe('test fetchAll', () => { getAppState: () => ({ query }), getInternalState: () => ({ dataView: undefined, + isDataViewLoading: false, savedDataViews: [], adHocDataViews: [], expandedDoc: undefined, @@ -394,6 +396,7 @@ describe('test fetchAll', () => { getAppState: () => ({ query }), getInternalState: () => ({ dataView: undefined, + isDataViewLoading: false, savedDataViews: [], adHocDataViews: [], expandedDoc: undefined, diff --git a/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap b/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap index 5e7c8fe4618c8..b3e8cac2afa3c 100644 --- a/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap +++ b/src/plugins/kibana_react/public/page_template/solution_nav/__snapshots__/solution_nav.test.tsx.snap @@ -2,7 +2,7 @@ exports[`KibanaPageTemplateSolutionNav accepts EuiSideNavProps 1`] = ` - - - >; export interface ItemSet { - set: Record; + set: FieldValuePair[]; size: number; maxPValue: number; doc_count: number; diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_group_table_items.test.ts b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_group_table_items.test.ts index c674da3948644..0976382377245 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_group_table_items.test.ts +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_group_table_items.test.ts @@ -15,34 +15,10 @@ describe('getGroupTableItems', () => { expect(groupTableItems).toEqual([ { - docCount: 632, - groupItemsSortedByUniqueness: [ - { - key: 'user:Peter', - type: 'keyword', - fieldName: 'user', - fieldValue: 'Peter', - docCount: 632, - duplicate: 2, - pValue: 0.012783309213417932, - }, - { - key: 'url:login.php', - type: 'keyword', - fieldName: 'url', - fieldValue: 'login.php', - docCount: 790, - duplicate: 2, - pValue: 0.012783309213417932, - }, - ], - histogram: undefined, - id: '1937394803', - pValue: 0.012783309213417932, - uniqueItemsCount: 0, - }, - { + id: '2675980076', docCount: 792, + pValue: 0.00974308761016614, + uniqueItemsCount: 0, groupItemsSortedByUniqueness: [ { key: 'response_code:500', @@ -50,8 +26,8 @@ describe('getGroupTableItems', () => { fieldName: 'response_code', fieldValue: '500', docCount: 792, - duplicate: 2, pValue: 0.012783309213417932, + duplicate: 2, }, { key: 'url:home.php', @@ -59,17 +35,16 @@ describe('getGroupTableItems', () => { fieldName: 'url', fieldValue: 'home.php', docCount: 792, - duplicate: 2, pValue: 0.00974308761016614, + duplicate: 2, }, ], - histogram: undefined, - id: '2675980076', - pValue: 0.00974308761016614, - uniqueItemsCount: 0, }, { + id: '3819687732', docCount: 790, + pValue: 0.012783309213417932, + uniqueItemsCount: 0, groupItemsSortedByUniqueness: [ { key: 'url:login.php', @@ -77,8 +52,8 @@ describe('getGroupTableItems', () => { fieldName: 'url', fieldValue: 'login.php', docCount: 790, - duplicate: 2, pValue: 0.012783309213417932, + duplicate: 2, }, { key: 'response_code:500', @@ -86,17 +61,16 @@ describe('getGroupTableItems', () => { fieldName: 'response_code', fieldValue: '500', docCount: 792, - duplicate: 2, pValue: 0.012783309213417932, + duplicate: 2, }, ], - histogram: undefined, - id: '3819687732', - pValue: 0.012783309213417932, - uniqueItemsCount: 0, }, { + id: '2091742187', docCount: 636, + pValue: 0.00974308761016614, + uniqueItemsCount: 0, groupItemsSortedByUniqueness: [ { key: 'user:Peter', @@ -104,8 +78,8 @@ describe('getGroupTableItems', () => { fieldName: 'user', fieldValue: 'Peter', docCount: 636, - duplicate: 2, pValue: 0.00974308761016614, + duplicate: 2, }, { key: 'url:home.php', @@ -113,14 +87,36 @@ describe('getGroupTableItems', () => { fieldName: 'url', fieldValue: 'home.php', docCount: 792, - duplicate: 2, pValue: 0.00974308761016614, + duplicate: 2, }, ], - histogram: undefined, - id: '2091742187', - pValue: 0.00974308761016614, + }, + { + id: '1937394803', + docCount: 632, + pValue: 0.012783309213417932, uniqueItemsCount: 0, + groupItemsSortedByUniqueness: [ + { + key: 'user:Peter', + type: 'keyword', + fieldName: 'user', + fieldValue: 'Peter', + docCount: 632, + pValue: 0.012783309213417932, + duplicate: 2, + }, + { + key: 'url:login.php', + type: 'keyword', + fieldName: 'url', + fieldValue: 'login.php', + docCount: 790, + pValue: 0.012783309213417932, + duplicate: 2, + }, + ], }, ]); }); diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_table_item_as_kql.test.ts b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_table_item_as_kql.test.ts index 64f4fde55a660..28c4d8c40a676 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_table_item_as_kql.test.ts +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/get_table_item_as_kql.test.ts @@ -20,9 +20,8 @@ describe('getTableItemAsKQL', () => { }); it('returns a KQL syntax for a group of significant items', () => { const groupTableItems = getGroupTableItems(finalSignificantItemGroups); - expect(getTableItemAsKQL(groupTableItems[0])).toBe('user:Peter AND url:login.php'); - expect(getTableItemAsKQL(groupTableItems[1])).toBe('response_code:500 AND url:home.php'); - expect(getTableItemAsKQL(groupTableItems[2])).toBe('url:login.php AND response_code:500'); - expect(getTableItemAsKQL(groupTableItems[3])).toBe('user:Peter AND url:home.php'); + expect(getTableItemAsKQL(groupTableItems[0])).toBe('response_code:500 AND url:home.php'); + expect(getTableItemAsKQL(groupTableItems[1])).toBe('url:login.php AND response_code:500'); + expect(getTableItemAsKQL(groupTableItems[2])).toBe('user:Peter AND url:home.php'); }); }); diff --git a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx index 957385780ceaa..0d250a08f8c11 100644 --- a/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx +++ b/x-pack/plugins/aiops/public/components/log_rate_analysis_results_table/log_rate_analysis_results_table_groups.tsx @@ -31,6 +31,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { SignificantItem } from '@kbn/ml-agg-utils'; import type { TimeRange as TimeRangeMs } from '@kbn/ml-date-picker'; import type { DataView } from '@kbn/data-views-plugin/public'; +import { stringHash } from '@kbn/ml-string-hash'; import { MiniHistogram } from '../mini_histogram'; @@ -203,7 +204,7 @@ export const LogRateAnalysisResultsGroupsTable: FC= MAX_GROUP_BADGES) break; valuesBadges.push( - + { expect(shouldClauses).toEqual([ { - terms: { - user: ['Peter'], + term: { + user: 'Peter', }, }, { - terms: { - response_code: ['500'], + term: { + response_code: '500', }, }, { - terms: { - url: ['home.php', 'login.php'], + term: { + url: 'home.php', + }, + }, + { + term: { + url: 'login.php', }, }, ]); diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts index ff02251b64f95..b5cd15636434c 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_frequent_item_sets.ts @@ -51,17 +51,36 @@ export function groupDuplicates( return groups; } +/** + * Creates ES bool should clauses for each provided significant item. + * In previous versions of this helper we grouped values for the same field + * in a `terms` agg, but this might clash with the `minimum_should_match: 2` clause + * used in the query for the `frequent_item_sets`, because even multiple matches within + * the `terms` agg would count as just 1 match in the outer `should` part. + * + * @param significantItems + * @returns an array of term filters + */ export function getShouldClauses(significantItems: SignificantItem[]) { - return Array.from( - group(significantItems, ({ fieldName }) => fieldName), - ([field, values]) => ({ terms: { [field]: values.map((d) => d.fieldValue) } }) - ); + return significantItems.map((d) => ({ term: { [d.fieldName]: d.fieldValue } })); } +/** + * Creates a filter for each field to be used in the `frequent_items_sets` agg. + * Considers a limit per field to work around scaling limitations of the agg. + * + * @param significantItems + * @returns field filter for the `frequent_item_sets` agg + */ export function getFrequentItemSetsAggFields(significantItems: SignificantItem[]) { return Array.from( group(significantItems, ({ fieldName }) => fieldName), - ([field, values]) => ({ field, include: values.map((d) => String(d.fieldValue)) }) + ([field, values]) => ({ + field, + include: values + .map((d) => String(d.fieldValue)) + .slice(0, LOG_RATE_ANALYSIS_SETTINGS.FREQUENT_ITEMS_SETS_FIELD_VALUE_LIMIT), + }) ); } @@ -166,7 +185,7 @@ export async function fetchFrequentItemSets( fiss.forEach((fis) => { const result: ItemSet = { - set: {}, + set: [], size: 0, maxPValue: 0, doc_count: 0, @@ -174,16 +193,18 @@ export async function fetchFrequentItemSets( total_doc_count: 0, }; let maxPValue: number | undefined; - Object.entries(fis.key).forEach(([key, value]) => { - result.set[key] = value[0]; + Object.entries(fis.key).forEach(([key, values]) => { + values.forEach((value) => { + result.set.push({ fieldName: key, fieldValue: value }); - const pValue = sortedSignificantItems.find( - (t) => t.fieldName === key && t.fieldValue === value[0] - )?.pValue; + const pValue = sortedSignificantItems.find( + (t) => t.fieldName === key && t.fieldValue === value + )?.pValue; - if (pValue !== undefined && pValue !== null) { - maxPValue = Math.max(maxPValue ?? 0, pValue); - } + if (pValue !== undefined && pValue !== null) { + maxPValue = Math.max(maxPValue ?? 0, pValue); + } + }); }); if (maxPValue === undefined) { @@ -203,7 +224,7 @@ export async function fetchFrequentItemSets( return b.doc_count - a.doc_count; }); - const uniqueFields = uniq(results.flatMap((r) => Object.keys(r.set))); + const uniqueFields = uniq(results.flatMap((r) => r.set.map((d) => d.fieldName))); return { fields: uniqueFields, diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts index a39418807e40a..e991d5d925bde 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_terms_2_categories_counts.ts @@ -99,10 +99,10 @@ export async function fetchTerms2CategoriesCounts( ) as estypes.MsearchMultisearchBody ); results.push({ - set: { - [term.fieldName]: term.fieldValue, - [category.fieldName]: category.fieldValue, - }, + set: [ + { fieldName: term.fieldName, fieldValue: term.fieldValue }, + { fieldName: category.fieldName, fieldValue: category.fieldValue }, + ], size: 2, maxPValue: Math.max(term.pValue ?? 1, category.pValue ?? 1), doc_count: 0, @@ -116,10 +116,7 @@ export async function fetchTerms2CategoriesCounts( searches.push( getTerm2CategoryCountRequest( params, - Object.entries(itemSet.set).map(([fieldName, fieldValue]) => ({ - fieldName, - fieldValue, - })), + itemSet.set, category.fieldName, { key: `${category.key}`, count: category.doc_count, examples: [], regex: '' }, from, @@ -127,10 +124,7 @@ export async function fetchTerms2CategoriesCounts( ) as estypes.MsearchMultisearchBody ); results.push({ - set: { - ...itemSet.set, - [category.fieldName]: category.fieldValue, - }, + set: [...itemSet.set, { fieldName: category.fieldName, fieldValue: category.fieldValue }], size: Object.keys(itemSet.set).length + 1, maxPValue: Math.max(itemSet.maxPValue ?? 1, category.pValue ?? 1), doc_count: 0, diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts index 4b48bb7ecd232..6aa1462026b61 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/fetch_top_terms.ts @@ -15,7 +15,7 @@ import { type RandomSamplerWrapper, } from '@kbn/ml-random-sampler-utils'; -import { RANDOM_SAMPLER_SEED } from '../../../../common/constants'; +import { LOG_RATE_ANALYSIS_SETTINGS, RANDOM_SAMPLER_SEED } from '../../../../common/constants'; import type { AiopsLogRateAnalysisSchema } from '../../../../common/api/log_rate_analysis/schema'; import { isRequestAbortedError } from '../../../lib/is_request_aborted_error'; @@ -60,7 +60,7 @@ export const getTopTermRequest = ( log_rate_top_terms: { terms: { field: fieldName, - size: 10, + size: LOG_RATE_ANALYSIS_SETTINGS.TOP_TERMS_FALLBACK_SIZE, }, }, }; diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts index 9f583f9e199df..eea96297b4622 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_group_filter.test.ts @@ -14,12 +14,12 @@ describe('getGroupFilter', () => { expect(getGroupFilter(finalSignificantItemGroups[0])).toStrictEqual([ { term: { - url: 'login.php', + response_code: '500', }, }, { term: { - user: 'Peter', + url: 'home.php', }, }, ]); diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_item_groups.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_item_groups.ts index 27dc06ad343b9..ace3bd73cba69 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_item_groups.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_significant_item_groups.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { uniqBy } from 'lodash'; + import type { SignificantItem, SignificantItemGroup } from '@kbn/ml-agg-utils'; import { duplicateIdentifier } from './duplicate_identifier'; @@ -28,7 +30,7 @@ export function getSignificantItemGroups( (g) => g.group.length > 1 ); - // `frequent_item_sets` returns lot of different small groups of field/value pairs that co-occur. + // `frequent_item_sets` returns lots of different small groups of field/value pairs that co-occur. // The following steps analyse these small groups, identify overlap between these groups, // and then summarize them in larger groups where possible. @@ -58,5 +60,5 @@ export function getSignificantItemGroups( ) ); - return significantItemGroups; + return uniqBy(significantItemGroups, 'id'); } diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts index 54cce87526bb1..d046058aa1095 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_simple_hierarchical_tree.ts @@ -59,7 +59,7 @@ function dfDepthFirstSearch( displayOther: boolean ) { const filteredItemSets = iss.filter((is) => { - for (const [key, setValue] of Object.entries(is.set)) { + for (const { fieldName: key, fieldValue: setValue } of is.set) { if (key === field && setValue === value) { return true; } diff --git a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts index 5c54412e46a5b..d08d2a21cd3cb 100644 --- a/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts +++ b/x-pack/plugins/aiops/server/routes/log_rate_analysis/queries/get_value_counts.ts @@ -9,10 +9,16 @@ import type { ItemSet } from '../../../../common/types'; export function getValueCounts(df: ItemSet[], field: string) { return df.reduce>((p, c) => { - if (c.set[field] === undefined) { + const fieldItems = c.set.filter((d) => d.fieldName === field); + + if (fieldItems.length === 0) { return p; } - p[c.set[field]] = p[c.set[field]] ? p[c.set[field]] + 1 : 1; + + for (const { fieldValue } of fieldItems) { + p[fieldValue] = p[fieldValue] ? p[fieldValue] + 1 : 1; + } + return p; }, {}); } diff --git a/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx b/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx index 15a280716c3c0..4da76d846dd9d 100644 --- a/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx +++ b/x-pack/plugins/cases/public/components/custom_fields/index.test.tsx @@ -17,7 +17,8 @@ import { MAX_CUSTOM_FIELDS_PER_CASE } from '../../../common/constants'; import { CustomFields } from '.'; import * as i18n from './translations'; -describe('CustomFields', () => { +// FLAKY: https://github.com/elastic/kibana/issues/176805 +describe.skip('CustomFields', () => { let appMockRender: AppMockRenderer; const props = { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step.tsx index 710fd49f72fb6..34490462c2c2d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step.tsx @@ -15,6 +15,7 @@ import { ANALYTICS_STEPS } from '../../page'; export interface ConfigurationStepProps extends CreateAnalyticsStepProps { isClone: boolean; + sourceDataViewTitle: string; } export const ConfigurationStep: FC = ({ @@ -24,6 +25,7 @@ export const ConfigurationStep: FC = ({ step, stepActivated, isClone, + sourceDataViewTitle, }) => { const showForm = step === ANALYTICS_STEPS.CONFIGURATION; const showDetails = step !== ANALYTICS_STEPS.CONFIGURATION && stepActivated === true; @@ -40,6 +42,7 @@ export const ConfigurationStep: FC = ({ isClone={isClone} state={state} setCurrentStep={setCurrentStep} + sourceDataViewTitle={sourceDataViewTitle} /> )} {showDetails && } diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx index d065ef84fa970..8ad8994fce64c 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx @@ -68,6 +68,7 @@ import { ExplorationQueryBarProps } from '../../../analytics_exploration/compone import { ScatterplotMatrix } from '../../../../../components/scatterplot_matrix'; import { RuntimeMappings } from '../runtime_mappings'; import { ConfigurationStepProps } from './configuration_step'; +import { IndexPermissionsCallout } from '../index_permissions_callout'; const runtimeMappingKey = 'runtime_mapping'; const notIncludedReason = 'field not in includes list'; @@ -119,6 +120,7 @@ export const ConfigurationStepForm: FC = ({ isClone, state, setCurrentStep, + sourceDataViewTitle, }) => { const { selectedDataView, selectedSavedSearch } = useDataSource(); const { savedSearchQuery, savedSearchQueryStr } = useSavedSearch(); @@ -583,6 +585,7 @@ export const ConfigurationStepForm: FC = ({ + {savedSearchQuery === null && ( = ({ return ( + = ({ + indexName, + docsType, +}) => { + const { + services: { + docLinks: { + links: { + ml: { dFAStartJob, dFACreateJob }, + }, + }, + }, + } = useMlKibana(); + + const docsLink = docsType === 'start' ? dFAStartJob : dFACreateJob; + const hasRequiredIndicesPermissions = useHasRequiredIndicesPermissions( + indexName, + docsType === 'start' + ); + // If 'hasRequiredIndicesPermissions' is undefined - the index passed to the check is an empty string + if (hasRequiredIndicesPermissions === undefined || hasRequiredIndicesPermissions === true) { + return null; + } + + return ( + <> + +

+ + + + ), + }} + /> +

+
+ + + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/index.ts index b156af3dec66a..b7e112a934e1d 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/index.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/index.ts @@ -6,3 +6,4 @@ */ export { useIndexData } from './use_index_data'; +export { useHasRequiredIndicesPermissions } from './use_has_index_permission'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_has_index_permission.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_has_index_permission.ts new file mode 100644 index 0000000000000..2fc8fb6b85f14 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_has_index_permission.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useState, useEffect } from 'react'; + +import { useMlKibana } from '../../../../contexts/kibana'; + +export const useHasRequiredIndicesPermissions = ( + indexName: string, + isDestIndex: boolean = false +) => { + const [hasIndexPermissions, setHasIndexPermissions] = useState(true); + const { + services: { + mlServices: { + mlApiServices: { hasPrivileges }, + }, + }, + } = useMlKibana(); + + useEffect( + function checkRequiredIndexPermissions() { + async function checkPrivileges() { + const sourceIndexPriv = ['view_index_metadata']; + const destIndexPriv = ['create_index', 'manage', 'index']; + + const privileges = await hasPrivileges({ + index: [ + { + names: [indexName], + privileges: ['read', ...(isDestIndex ? destIndexPriv : sourceIndexPriv)], + }, + ], + }); + + setHasIndexPermissions(privileges.hasPrivileges?.has_all_requested === true); + } + + checkPrivileges(); + }, + [hasPrivileges, indexName, isDestIndex] + ); + + if (indexName === '') { + return; + } + + return hasIndexPermissions; +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/page.tsx index b86d819cc7938..cc5543ed6c23a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/page.tsx @@ -103,6 +103,7 @@ export const Page: FC = ({ jobId }) => { setCurrentStep={setCurrentStep} step={currentStep} stepActivated={activatedSteps[ANALYTICS_STEPS.CONFIGURATION]} + sourceDataViewTitle={selectedDataView.getIndexPattern()} /> ), status: @@ -188,7 +189,7 @@ export const Page: FC = ({ jobId }) => { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts index c097577bc289a..e8edab907ef9e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/use_create_analytics_form.ts @@ -10,6 +10,7 @@ import { useReducer } from 'react'; import { i18n } from '@kbn/i18n'; import { extractErrorMessage } from '@kbn/ml-error-utils'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; import type { DataFrameAnalyticsConfig } from '@kbn/ml-data-frame-analytics-utils'; import { useMlKibana } from '../../../../../contexts/kibana'; @@ -91,14 +92,21 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { const analyticsJobConfig = ( isAdvancedEditorEnabled ? jobConfig : getJobConfigFromFormState(form) ) as DataFrameAnalyticsConfig; + const errorMessage = i18n.translate( + 'xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob', + { + defaultMessage: 'An error occurred creating the data frame analytics job:', + } + ); try { - await ml.dataFrameAnalytics.createDataFrameAnalytics( + const creationResp = await ml.dataFrameAnalytics.createDataFrameAnalytics( jobId, analyticsJobConfig, createDataView, form.timeFieldName ); + addRequestMessage({ message: i18n.translate( 'xpack.ml.dataframe.stepCreateForm.createDataFrameAnalyticsSuccessMessage', @@ -108,21 +116,29 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => { } ), }); - setIsJobCreated(true); - refresh(); - return true; + + if ( + creationResp.dataFrameAnalyticsJobsCreated.length && + creationResp.dataFrameAnalyticsJobsErrors.length === 0 + ) { + setIsJobCreated(true); + refresh(); + return true; + } else if (creationResp.dataFrameAnalyticsJobsErrors.length) { + addRequestMessage({ + error: extractErrorProperties(creationResp.dataFrameAnalyticsJobsErrors[0].error).message, + message: errorMessage, + }); + return false; + } } catch (e) { addRequestMessage({ error: extractErrorMessage(e), - message: i18n.translate( - 'xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob', - { - defaultMessage: 'An error occurred creating the data frame analytics job:', - } - ), + message: errorMessage, }); return false; } + return false; }; const prepareFormValidation = async () => { diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts index 8b2c7bd294744..b0729db61c5ff 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/data_frame_analytics.ts @@ -23,6 +23,7 @@ import { useMlKibana } from '../../contexts/kibana'; import type { ValidateAnalyticsJobResponse } from '../../../../common/constants/validation'; import type { DeepPartial } from '../../../../common/types/common'; import type { JobMessage } from '../../../../common/types/audit_message'; +import type { PutDataFrameAnalyticsResponseSchema } from '../../../../server/routes/schemas/data_frame_analytics_schema'; export interface GetDataFrameAnalyticsStatsResponseOk { node_failures?: object; @@ -88,7 +89,7 @@ export const dataFrameAnalyticsApiProvider = (httpService: HttpService) => ({ timeFieldName?: string ) { const body = JSON.stringify(analyticsConfig); - return httpService.http({ + return httpService.http({ path: `${ML_INTERNAL_BASE_PATH}/data_frame/analytics/${analyticsId}`, method: 'PUT', query: { createDataView, timeFieldName }, diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 5213a24172357..c81d17e18ff1e 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -35,7 +35,7 @@ const StyledKibanaPageTemplate = styled(KibanaPageTemplate)< theme: EuiThemeComputed; // using computed EUI theme to be consistent with user profile theming } >` - .kbnSolutionNav { + .kbnSolutionNav__sidebar:not(.kbnSolutionNav__sidebar--shrink) { background-color: ${({ theme }) => theme.colors.emptyShade}; } diff --git a/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts b/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts index 352282b4ba41f..3fcc3dd339047 100644 --- a/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts +++ b/x-pack/test/api_integration/apis/aiops/log_rate_analysis_full_analysis.ts @@ -52,16 +52,15 @@ export default ({ getService }: FtrProviderContext) => { }); async function assertAnalysisResult(data: any[]) { - expect(data.length).to.eql( - testData.expected.actionsLength, - `Expected 'actionsLength' to be ${testData.expected.actionsLength}, got ${data.length}.` - ); data.forEach((d) => { expect(typeof d.type).to.be('string'); }); const addSignificantItemsActions = getAddSignificationItemsActions(data, apiVersion); - expect(addSignificantItemsActions.length).to.greaterThan(0); + expect(addSignificantItemsActions.length).to.greaterThan( + 0, + 'Expected significant items actions to be greater than 0.' + ); const significantItems = orderBy( addSignificantItemsActions.flatMap((d) => d.payload), @@ -80,7 +79,10 @@ export default ({ getService }: FtrProviderContext) => { expect(histogramActions.length).to.be(significantItems.length); // each histogram should have a length of 20 items. histograms.forEach((h, index) => { - expect(h.histogram.length).to.be(20); + expect(h.histogram.length).to.eql( + testData.expected.histogramLength, + `Expected histogram length to be ${testData.expected.histogramLength}, got ${h.histogram.length}` + ); }); const groupActions = getGroupActions(data, apiVersion); @@ -102,7 +104,10 @@ export default ({ getService }: FtrProviderContext) => { expect(groupHistograms.length).to.be(groups.length); // each histogram should have a length of 20 items. groupHistograms.forEach((h, index) => { - expect(h.histogram.length).to.be(20); + expect(h.histogram.length).to.eql( + testData.expected.histogramLength, + `Expected group histogram length to be ${testData.expected.histogramLength}, got ${h.histogram.length}` + ); }); } @@ -128,11 +133,6 @@ export default ({ getService }: FtrProviderContext) => { const chunks: string[] = resp.body.toString().split('\n'); - expect(chunks.length).to.eql( - testData.expected.chunksLength, - `Expected 'chunksLength' to be ${testData.expected.chunksLength}, got ${chunks.length}.` - ); - const lastChunk = chunks.pop(); expect(lastChunk).to.be(''); diff --git a/x-pack/test/api_integration/apis/aiops/log_rate_analysis_groups_only.ts b/x-pack/test/api_integration/apis/aiops/log_rate_analysis_groups_only.ts index fcc4bafabdf3c..f3aeeed8d067c 100644 --- a/x-pack/test/api_integration/apis/aiops/log_rate_analysis_groups_only.ts +++ b/x-pack/test/api_integration/apis/aiops/log_rate_analysis_groups_only.ts @@ -74,10 +74,6 @@ export default ({ getService }: FtrProviderContext) => { }); async function assertAnalysisResult(data: any[]) { - expect(data.length).to.eql( - testData.expected.actionsLengthGroupOnly, - `Expected 'actionsLengthGroupOnly' to be ${testData.expected.actionsLengthGroupOnly}, got ${data.length}.` - ); data.forEach((d) => { expect(typeof d.type).to.be('string'); }); @@ -101,7 +97,9 @@ export default ({ getService }: FtrProviderContext) => { expect(orderBy(groups, ['docCount'], ['desc'])).to.eql( orderBy(testData.expected.groups, ['docCount'], ['desc']), - 'Grouping result does not match expected values.' + `Grouping result does not match expected values. Expected ${JSON.stringify( + testData.expected.groups + )}, got ${JSON.stringify(groups)}` ); const groupHistogramActions = getGroupHistogramActions(data, apiVersion); @@ -110,7 +108,10 @@ export default ({ getService }: FtrProviderContext) => { expect(groupHistograms.length).to.be(groups.length); // each histogram should have a length of 20 items. groupHistograms.forEach((h, index) => { - expect(h.histogram.length).to.be(20); + expect(h.histogram.length).to.eql( + testData.expected.histogramLength, + `Expected group histogram length to be ${testData.expected.histogramLength}, got ${h.histogram.length}` + ); }); } @@ -136,11 +137,6 @@ export default ({ getService }: FtrProviderContext) => { const chunks: string[] = resp.body.toString().split('\n'); - expect(chunks.length).to.eql( - testData.expected.chunksLengthGroupOnly, - `Expected 'chunksLength' to be ${testData.expected.chunksLengthGroupOnly}, got ${chunks.length}.` - ); - const lastChunk = chunks.pop(); expect(lastChunk).to.be(''); diff --git a/x-pack/test/api_integration/apis/aiops/test_data.ts b/x-pack/test/api_integration/apis/aiops/test_data.ts index 3c1793ab9efe3..455574e4c7e76 100644 --- a/x-pack/test/api_integration/apis/aiops/test_data.ts +++ b/x-pack/test/api_integration/apis/aiops/test_data.ts @@ -14,11 +14,14 @@ import { finalSignificantItemGroups as artificialLogsSignificantItemGroups } fro import { finalSignificantItemGroupsTextfield as artificialLogsSignificantItemGroupsTextfield } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/final_significant_item_groups_textfield'; import { topTerms } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/top_terms'; import { topTermsGroups } from '@kbn/aiops-plugin/common/__mocks__/artificial_logs/top_terms_groups'; - import type { AiopsLogRateAnalysisSchema, AiopsLogRateAnalysisApiVersion as ApiVersion, } from '@kbn/aiops-plugin/common/api/log_rate_analysis/schema'; +import { + frequentItemSetsLargeArraysGroups, + frequentItemSetsLargeArraysSignificantItems, +} from '../../../functional/apps/aiops/log_rate_analysis/test_data/__mocks__/frequent_item_sets_large_arrays'; import type { TestData } from './types'; @@ -35,16 +38,12 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 36, - chunksLengthGroupOnly: 5, - actionsLength: 35, - actionsLengthGroupOnly: 4, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: [ @@ -95,10 +94,6 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 28, - chunksLengthGroupOnly: 11, - actionsLength: 27, - actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: artificialLogSignificantTerms, @@ -122,10 +117,6 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 62, - chunksLengthGroupOnly: 32, - actionsLength: 61, - actionsLengthGroupOnly: 31, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: topTerms, @@ -149,10 +140,6 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 62, - chunksLengthGroupOnly: 32, - actionsLength: 61, - actionsLengthGroupOnly: 31, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: topTerms, @@ -176,10 +163,6 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 31, - chunksLengthGroupOnly: 11, - actionsLength: 30, - actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: [...artificialLogSignificantTerms, ...artificialLogSignificantLogPatterns], @@ -203,10 +186,6 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 28, - chunksLengthGroupOnly: 11, - actionsLength: 27, - actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: artificialLogSignificantTerms, @@ -230,10 +209,6 @@ export const getLogRateAnalysisTestData = (): Array, expected: { - chunksLength: 31, - chunksLengthGroupOnly: 11, - actionsLength: 30, - actionsLengthGroupOnly: 10, noIndexChunksLength: 4, noIndexActionsLength: 3, significantItems: [...artificialLogSignificantTerms, ...artificialLogSignificantLogPatterns], @@ -241,4 +216,27 @@ export const getLogRateAnalysisTestData = (): Array, + expected: { + noIndexChunksLength: 4, + noIndexActionsLength: 3, + groups: frequentItemSetsLargeArraysGroups, + significantItems: frequentItemSetsLargeArraysSignificantItems, + histogramLength: 1, + }, + }, ]; diff --git a/x-pack/test/api_integration/apis/aiops/types.ts b/x-pack/test/api_integration/apis/aiops/types.ts index df38825d9698d..acb948a2fea50 100644 --- a/x-pack/test/api_integration/apis/aiops/types.ts +++ b/x-pack/test/api_integration/apis/aiops/types.ts @@ -19,10 +19,6 @@ export interface TestData { dataGenerator?: LogRateAnalysisDataGenerator; requestBody: AiopsLogRateAnalysisSchema; expected: { - chunksLength: number; - chunksLengthGroupOnly: number; - actionsLength: number; - actionsLengthGroupOnly: number; noIndexChunksLength: number; noIndexActionsLength: number; significantItems: SignificantItem[]; diff --git a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts index 67b501a784462..2507700be6fef 100644 --- a/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts +++ b/x-pack/test/cases_api_integration/security_and_spaces/tests/trial/cases/push_case.ts @@ -515,7 +515,8 @@ export default ({ getService }: FtrProviderContext): void => { }); }); - describe('user profile uid', () => { + // FLAKY: https://github.com/elastic/kibana/issues/157588 + describe.skip('user profile uid', () => { let headers: Record; let superUserWithProfile: User; let superUserInfo: User; diff --git a/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/frequent_item_sets_large_arrays.ts b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/frequent_item_sets_large_arrays.ts new file mode 100644 index 0000000000000..7296fdfc4dc66 --- /dev/null +++ b/x-pack/test/functional/apps/aiops/log_rate_analysis/test_data/__mocks__/frequent_item_sets_large_arrays.ts @@ -0,0 +1,2497 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SignificantItem, SignificantItemGroup } from '@kbn/ml-agg-utils'; + +// This was generated from real data using the following code: +// +// const data = testDataFrequentItemSets.response.hits.hits.map((doc) => { +// return doc.fields['files.pathAndFilename'].map((d) => `${stringHash(d)}`); +// }); +// +// The data is used for testing `frequent_item_sets` with somewhat larger arrays +// within the same field of a document. +export const frequentItemSetsLargeArraysSource = [ + [ + '3122117744', + '1846471239', + '3850123478', + '491462432', + '2145700416', + '1136953980', + '1423181790', + '784813175', + '3950054860', + ], + [ + '1451672588', + '3112205174', + '359183539', + '3434731980', + '1217080824', + '4072732370', + '764323024', + '1380284592', + '471153522', + '1104487811', + '3759120234', + '1896487004', + '225357271', + '1157393364', + '1600940924', + '1846471239', + '3178671192', + '32293944', + '2469262600', + '1052777253', + '801912434', + '1371557807', + '3850123478', + '505971607', + '732360692', + '2134530418', + '1416571574', + '2145700416', + '2887166226', + '1518623688', + '550501793', + '2179218510', + '742260364', + '1129761594', + '3625900628', + '3887145594', + '484181980', + '3779185628', + '3234612808', + ], + ['1416571574', '2145700416', '2887166226'], + ['1451672588', '978800164', '3122117744', '2145700416', '1136953980', '1129761594', '1969286181'], + [ + '471153522', + '1104487811', + '606140382', + '1896487004', + '225357271', + '1416571574', + '2145700416', + '2887166226', + '1901595662', + '1136953980', + '1129761594', + '1064752192', + ], + [ + '2432527302', + '1685611991', + '3709732414', + '3434731980', + '914276804', + '629341211', + '3561517034', + '1104874254', + '603630362', + '850909865', + '511726177', + '774389318', + '65096514', + '464464365', + '4072732370', + '3989522974', + '369147719', + '1756911944', + '2132661800', + '1351408438', + '2566334776', + '365681874', + '1994406554', + '4183628510', + '2411863132', + '153528430', + '2125040243', + '1919882921', + '484454192', + '2285116966', + '694867546', + '1275826846', + '1863819958', + '1332658570', + '764323024', + '4195111702', + '652426849', + '1380284592', + '471153522', + '2524040016', + '1325698447', + '998290200', + '1104487811', + '625944587', + '1140596684', + '1979833532', + '1475971156', + '1529680269', + '1925258778', + '3095335232', + '4009453878', + '953226620', + '3127779728', + '1432609585', + '3345566998', + '1867260038', + '3108151752', + '484256652', + '2138441449', + '1992199813', + '644711789', + '355508418', + '2463774400', + '1974037124', + '1437073018', + '2062952599', + '2317033220', + '165907306', + '39215494', + '2924749428', + '1831094494', + '606140382', + '1366342370', + '2084416768', + '55129418', + '899074608', + '1947835436', + '3759120234', + '978208972', + '1896487004', + '225357271', + '3096101166', + '3767723084', + '3035540120', + '853675916', + '1901780335', + '2639455238', + '1853107498', + '1157393364', + '2009375757', + '652223620', + '4028673796', + '1600940924', + '1808235940', + '1846471239', + '3178671192', + '1690511642', + '875090116', + '32293944', + '1073297840', + '1310779384', + '1940824684', + '1616339276', + '1878061294', + '2387317510', + '3899749436', + '1028474515', + '80529116', + '1081175620', + '3850123478', + '505971607', + '1091455238', + '1864834432', + '1569561080', + '1919542521', + '595513713', + '1627808055', + '1509804328', + '2134530418', + '894704468', + '1416571574', + '1227568232', + '1165010485', + '2145700416', + '3568288730', + '1758096804', + '2939442746', + '2057777897', + '456806730', + '1883416844', + '338634143', + '343273046', + '2290476620', + '1912970652', + '918833910', + '3419329722', + '2998254532', + '2887166226', + '3588882742', + '3305614422', + '333459111', + '3700075048', + '422724693', + '2172049200', + '912657112', + '1493078886', + '1924864316', + '1901595662', + '872035779', + '1143829704', + '2513611754', + '1136953980', + '1372713988', + '2087729311', + '209053355', + '1423181790', + '784813175', + '2380606900', + '1439966722', + '1518623688', + '3085917568', + '134825909', + '2848504534', + '1792385756', + '23762256', + '683600999', + '2993287316', + '3662867336', + '550501793', + '3948564194', + '1764158287', + '1124226732', + '3928811594', + '1598402559', + '679133986', + '3777335490', + '1652565419', + '1129761594', + '4043271966', + '4237245772', + '3657936252', + '281200596', + '1501943035', + '628118277', + '3950054860', + '3234612808', + '1548316370', + '315977894', + ], + [ + '178851049', + '1293256852', + '967305015', + '945750959', + '3112205174', + '359183539', + '744667649', + '355870296', + '65096514', + '2411863132', + '471153522', + '644711789', + '165907306', + '606140382', + '1366342370', + '2084416768', + '55129418', + '899074608', + '1947835436', + '1901780335', + '2009375757', + '892741844', + '1175209945', + '894704468', + '2145700416', + '3568288730', + '1758096804', + '2057777897', + '456806730', + '1883416844', + '338634143', + '94643047', + '578183673', + '3419329722', + '1372713988', + '209053355', + '784813175', + '715654930', + '679133986', + '3777335490', + '1652565419', + '1129761594', + '1445245168', + '484181980', + ], + [ + '3709732414', + '1774067546', + '629341211', + '3561517034', + '511726177', + '774389318', + '65096514', + '464464365', + '4072732370', + '1351408438', + '1880041108', + '1863819958', + '471153522', + '1104487811', + '625944587', + '1979833532', + '1475971156', + '3127779728', + '1432609585', + '644711789', + '2463774400', + '1437073018', + '2062952599', + '165907306', + '39215494', + '3279070400', + '33232218', + '3702216900', + '169386347', + '4282833864', + '343056858', + '1048252464', + '3328723350', + '766594295', + '864457688', + '1881750640', + '358188834', + '3464086212', + '1318649342', + '2007178432', + '2039579870', + '2028869426', + '1782757648', + '198736362', + '1459346396', + '261701072', + '860004934', + '1416825454', + '1901780335', + '1853107498', + '1157393364', + '1808235940', + '287718944', + '32293944', + '1310779384', + '1616339276', + '1878061294', + '3899749436', + '3850123478', + '1919542521', + '894704468', + '2803891618', + '1416571574', + '2145700416', + '1758096804', + '2057777897', + '456806730', + '1883416844', + '338634143', + '94643047', + '578183673', + '2998254532', + '333459111', + '912657112', + '1493078886', + '1901595662', + '872035779', + '2513611754', + '1136953980', + '1372713988', + '2087729311', + '209053355', + '1423181790', + '784813175', + '2380606900', + '1214124778', + '23762256', + '715654930', + '550501793', + '2148044396', + '679133986', + '3777335490', + '1652565419', + '4043271966', + '1418709661', + '3950054860', + '923662969', + '3234612808', + '1548316370', + '315977894', + '296863626', + ], + ['1183454328', '1104487811', '2639455238', '1808235940', '2145700416', '1329827342'], + [ + '3341153940', + '1499538523', + '1104487811', + '1979833532', + '2639455238', + '1853107498', + '1157393364', + '1808235940', + '1416571574', + '2145700416', + '2998254532', + '23762256', + '683600999', + '550501793', + '2148044396', + '1329827342', + ], + [ + '2432527302', + '396010280', + '888945742', + '1236602460', + '3709732414', + '629341211', + '3561517034', + '850909865', + '511726177', + '774389318', + '65096514', + '464464365', + '3989522974', + '369147719', + '1756911944', + '2132661800', + '1351408438', + '365681874', + '1994406554', + '4183628510', + '2411863132', + '153528430', + '2125040243', + '1919882921', + '484454192', + '2134641614', + '793940582', + '1782787351', + '1219720056', + '243787747', + '2970663200', + '1380206706', + '1473389172', + '3341153940', + '1023429210', + '1499538523', + '1103890809', + '1183454328', + '408379606', + '83894197', + '2349146622', + '1759997430', + '1880041108', + '672264778', + '1965916128', + '1213194741', + '1962101985', + '2524040016', + '1325698447', + '998290200', + '1104487811', + '625944587', + '1979833532', + '1475971156', + '4009453878', + '953226620', + '3127779728', + '1432609585', + '3345566998', + '1867260038', + '3108151752', + '484256652', + '1992199813', + '644711789', + '355508418', + '2463774400', + '52266782', + '1974037124', + '1437073018', + '2062952599', + '39215494', + '1853107498', + '1157393364', + '2009375757', + '4028673796', + '1600940924', + '1302044808', + '1808235940', + '1846471239', + '1690511642', + '875090116', + '32293944', + '1073297840', + '1310779384', + '1940824684', + '1616339276', + '1878061294', + '2387317510', + '3899749436', + '1028474515', + '80529116', + '1592492698', + '505971607', + '1091455238', + '1864834432', + '1569561080', + '1919542521', + '595513713', + '1627808055', + '892741844', + '1175209945', + '894704468', + '1416571574', + '1165010485', + '2145700416', + '3568288730', + '2057777897', + '456806730', + '1883416844', + '338634143', + '3419329722', + '2998254532', + '2073206647', + '3588882742', + '3305614422', + '684643770', + '333459111', + '3700075048', + '422724693', + '2172049200', + '912657112', + '1493078886', + '1924864316', + '2380606900', + '1439966722', + '1214124778', + '3085917568', + '134825909', + '2848504534', + '23762256', + '683600999', + '2993287316', + '550501793', + '3948564194', + '1764158287', + '1124226732', + '2148044396', + '3777335490', + '1652565419', + '4043271966', + '4237245772', + '3657936252', + '249588550', + '281200596', + '1501943035', + '3939021644', + '1418709661', + '628118277', + '998280499', + '1445245168', + '3950054860', + '1361617926', + '923662969', + '1264884266', + '3234612808', + '1548316370', + '315977894', + '296863626', + ], + [ + '1447828634', + '396010280', + '888945742', + '1439926706', + '1236602460', + '3709732414', + '629341211', + '3561517034', + '850909865', + '511726177', + '774389318', + '65096514', + '464464365', + '3989522974', + '369147719', + '1756911944', + '2132661800', + '1351408438', + '365681874', + '1994406554', + '4183628510', + '2411863132', + '153528430', + '2125040243', + '1919882921', + '484454192', + '2134641614', + '793940582', + '1782787351', + '1219720056', + '243787747', + '2970663200', + '1380206706', + '1473389172', + '3341153940', + '1023429210', + '1499538523', + '1103890809', + '1183454328', + '408379606', + '83894197', + '2349146622', + '1759997430', + '1880041108', + '672264778', + '1965916128', + '1213194741', + '1962101985', + '2524040016', + '1325698447', + '998290200', + '1104487811', + '625944587', + '1979833532', + '1475971156', + '4009453878', + '953226620', + '3127779728', + '1432609585', + '3345566998', + '1867260038', + '3108151752', + '484256652', + '1992199813', + '644711789', + '355508418', + '2463774400', + '52266782', + '1974037124', + '1437073018', + '2062952599', + '39215494', + '1853107498', + '1157393364', + '2009375757', + '4028673796', + '1600940924', + '1302044808', + '1808235940', + '1846471239', + '1690511642', + '875090116', + '32293944', + '1073297840', + '1310779384', + '1940824684', + '1616339276', + '1878061294', + '2387317510', + '3899749436', + '1028474515', + '80529116', + '1592492698', + '505971607', + '1091455238', + '1864834432', + '1569561080', + '1919542521', + '595513713', + '1627808055', + '892741844', + '1175209945', + '894704468', + '1416571574', + '1165010485', + '2145700416', + '3568288730', + '2057777897', + '456806730', + '1883416844', + '338634143', + '3419329722', + '2998254532', + '2073206647', + '3588882742', + '3305614422', + '684643770', + '333459111', + '3700075048', + '422724693', + '2172049200', + '912657112', + '1493078886', + '1924864316', + '2380606900', + '1439966722', + '1214124778', + '3085917568', + '134825909', + '2848504534', + '23762256', + '683600999', + '2993287316', + '550501793', + '3948564194', + '1764158287', + '1124226732', + '2148044396', + '3777335490', + '1652565419', + '4043271966', + '4237245772', + '3657936252', + '249588550', + '281200596', + '1501943035', + '3939021644', + '1418709661', + '628118277', + '998280499', + '1445245168', + '3950054860', + '1361617926', + '923662969', + '1264884266', + '3234612808', + '1548316370', + '315977894', + '296863626', + ], + [ + '850909865', + '464464365', + '3341153940', + '1103890809', + '83894197', + '1759997430', + '2524040016', + '998290200', + '1104487811', + '625944587', + '1979833532', + '4009453878', + '3127779728', + '52266782', + '1157393364', + '1808235940', + '1024718864', + '32293944', + '505971607', + '894704468', + '1416571574', + '2145700416', + '3568288730', + '2998254532', + '3588882742', + '3305614422', + '684643770', + '333459111', + '3700075048', + '422724693', + '2172049200', + '1493078886', + '1924864316', + '2380606900', + '1439966722', + '23762256', + '683600999', + '2148044396', + '3777335490', + '3234612808', + '1548316370', + ], + [ + '178851049', + '1685611991', + '869061781', + '3122117744', + '1712164321', + '914276804', + '850909865', + '65096514', + '464464365', + '369147719', + '1351408438', + '1325698447', + '998290200', + '1104487811', + '625944587', + '3127779728', + '1157393364', + '1808235940', + '1690511642', + '32293944', + '1616339276', + '2387317510', + '505971607', + '894704468', + '2145700416', + '2998254532', + '1214124778', + '550501793', + '3777335490', + '1652565419', + '1548316370', + ], + ['945750959', '850909865', '1416571574', '2145700416', '2998254532', '3305614422', '1548316370'], + [ + '2432527302', + '1144870203', + '1293256852', + '945750959', + '2861405270', + '3157340152', + '869061781', + '289756479', + '744667649', + '1217080824', + '362845723', + '355870296', + '3122117744', + '1774067546', + '1284354426', + '1712164321', + '629341211', + '3561517034', + '850909865', + '511726177', + '774389318', + '65096514', + '464464365', + '4072732370', + '3989522974', + '369147719', + '1756911944', + '2132661800', + '1351408438', + '365681874', + '1994406554', + '4183628510', + '2411863132', + '153528430', + '2125040243', + '1919882921', + '484454192', + '1028927795', + '2134641614', + '793940582', + '1782787351', + '1219720056', + '243787747', + '2970663200', + '1380206706', + '1473389172', + '3341153940', + '1023429210', + '1499538523', + '1103890809', + '1183454328', + '408379606', + '83894197', + '2349146622', + '1759997430', + '1880041108', + '672264778', + '1965916128', + '1213194741', + '1962101985', + '2524040016', + '1325698447', + '998290200', + '1104487811', + '625944587', + '1979833532', + '1475971156', + '4009453878', + '953226620', + '3127779728', + '1432609585', + '3345566998', + '1867260038', + '3108151752', + '484256652', + '1992199813', + '644711789', + '355508418', + '2463774400', + '52266782', + '1974037124', + '1437073018', + '2062952599', + '39215494', + '2173098414', + '1157393364', + '4035065194', + '1600940924', + '1808235940', + '1846471239', + '287718944', + '1690511642', + '875090116', + '32293944', + '1073297840', + '1310779384', + '1940824684', + '1616339276', + '1878061294', + '2387317510', + '3899749436', + '1028474515', + '80529116', + '1592492698', + '3850123478', + '505971607', + '1091455238', + '3521104552', + '1864834432', + '1569561080', + '1919542521', + '595513713', + '1627808055', + '892741844', + '1175209945', + '894704468', + '1622294140', + '1416571574', + '1165010485', + '2145700416', + '3568288730', + '2057777897', + '456806730', + '1883416844', + '338634143', + '3419329722', + '2998254532', + '2073206647', + '3588882742', + '3305614422', + '684643770', + '333459111', + '3700075048', + '422724693', + '2172049200', + '912657112', + '1493078886', + '1924864316', + '2380606900', + '1439966722', + '1214124778', + '3085917568', + '134825909', + '2848504534', + '23762256', + '683600999', + '2993287316', + '550501793', + '3948564194', + '1764158287', + '1124226732', + '2148044396', + '3777335490', + '1445245168', + '3950054860', + '484181980', + '1361617926', + '923662969', + '1264884266', + '3234612808', + '315977894', + '296863626', + ], +]; + +export const frequentItemSetsLargeArraysSignificantItems: SignificantItem[] = [ + { doc_count: 16, fieldValue: '2145700416' }, + { doc_count: 11, fieldValue: '1104487811' }, + { doc_count: 11, fieldValue: '1416571574' }, + { doc_count: 9, fieldValue: '1157393364' }, + { doc_count: 9, fieldValue: '1808235940' }, + { doc_count: 9, fieldValue: '2998254532' }, + { doc_count: 8, fieldValue: '32293944' }, + { doc_count: 8, fieldValue: '3777335490' }, + { doc_count: 8, fieldValue: '550501793' }, + { doc_count: 8, fieldValue: '894704468' }, + { doc_count: 7, fieldValue: '1548316370' }, + { doc_count: 7, fieldValue: '1979833532' }, + { doc_count: 7, fieldValue: '23762256' }, + { doc_count: 7, fieldValue: '3127779728' }, + { doc_count: 7, fieldValue: '3234612808' }, + { doc_count: 7, fieldValue: '464464365' }, + { doc_count: 7, fieldValue: '505971607' }, + { doc_count: 7, fieldValue: '625944587' }, + { doc_count: 7, fieldValue: '65096514' }, + { doc_count: 7, fieldValue: '850909865' }, + { doc_count: 6, fieldValue: '1351408438' }, + { doc_count: 6, fieldValue: '1493078886' }, + { doc_count: 6, fieldValue: '1616339276' }, + { doc_count: 6, fieldValue: '1652565419' }, + { doc_count: 6, fieldValue: '1846471239' }, + { doc_count: 6, fieldValue: '1883416844' }, + { doc_count: 6, fieldValue: '2057777897' }, + { doc_count: 6, fieldValue: '2148044396' }, + { doc_count: 6, fieldValue: '2380606900' }, + { doc_count: 6, fieldValue: '3305614422' }, + { doc_count: 6, fieldValue: '333459111' }, + { doc_count: 6, fieldValue: '338634143' }, + { doc_count: 6, fieldValue: '3568288730' }, + { doc_count: 6, fieldValue: '3950054860' }, + { doc_count: 6, fieldValue: '456806730' }, + { doc_count: 6, fieldValue: '644711789' }, + { doc_count: 6, fieldValue: '683600999' }, + { doc_count: 6, fieldValue: '998290200' }, + { doc_count: 5, fieldValue: '1129761594' }, + { doc_count: 5, fieldValue: '1136953980' }, + { doc_count: 5, fieldValue: '1214124778' }, + { doc_count: 5, fieldValue: '1310779384' }, + { doc_count: 5, fieldValue: '1325698447' }, + { doc_count: 5, fieldValue: '1432609585' }, + { doc_count: 5, fieldValue: '1437073018' }, + { doc_count: 5, fieldValue: '1439966722' }, + { doc_count: 5, fieldValue: '1475971156' }, + { doc_count: 5, fieldValue: '1600940924' }, + { doc_count: 5, fieldValue: '1690511642' }, + { doc_count: 5, fieldValue: '1853107498' }, + { doc_count: 5, fieldValue: '1878061294' }, + { doc_count: 5, fieldValue: '1919542521' }, + { doc_count: 5, fieldValue: '1924864316' }, + { doc_count: 5, fieldValue: '2062952599' }, + { doc_count: 5, fieldValue: '2172049200' }, + { doc_count: 5, fieldValue: '2387317510' }, + { doc_count: 5, fieldValue: '2411863132' }, + { doc_count: 5, fieldValue: '2463774400' }, + { doc_count: 5, fieldValue: '2524040016' }, + { doc_count: 5, fieldValue: '315977894' }, + { doc_count: 5, fieldValue: '3341153940' }, + { doc_count: 5, fieldValue: '3419329722' }, + { doc_count: 5, fieldValue: '3561517034' }, + { doc_count: 5, fieldValue: '3588882742' }, + { doc_count: 5, fieldValue: '369147719' }, + { doc_count: 5, fieldValue: '3700075048' }, + { doc_count: 5, fieldValue: '3850123478' }, + { doc_count: 5, fieldValue: '3899749436' }, + { doc_count: 5, fieldValue: '39215494' }, + { doc_count: 5, fieldValue: '4009453878' }, + { doc_count: 5, fieldValue: '422724693' }, + { doc_count: 5, fieldValue: '471153522' }, + { doc_count: 5, fieldValue: '511726177' }, + { doc_count: 5, fieldValue: '629341211' }, + { doc_count: 5, fieldValue: '774389318' }, + { doc_count: 5, fieldValue: '912657112' }, + { doc_count: 4, fieldValue: '1028474515' }, + { doc_count: 4, fieldValue: '1073297840' }, + { doc_count: 4, fieldValue: '1091455238' }, + { doc_count: 4, fieldValue: '1103890809' }, + { doc_count: 4, fieldValue: '1124226732' }, + { doc_count: 4, fieldValue: '1165010485' }, + { doc_count: 4, fieldValue: '1175209945' }, + { doc_count: 4, fieldValue: '1183454328' }, + { doc_count: 4, fieldValue: '134825909' }, + { doc_count: 4, fieldValue: '1445245168' }, + { doc_count: 4, fieldValue: '1499538523' }, + { doc_count: 4, fieldValue: '153528430' }, + { doc_count: 4, fieldValue: '1569561080' }, + { doc_count: 4, fieldValue: '1627808055' }, + { doc_count: 4, fieldValue: '1756911944' }, + { doc_count: 4, fieldValue: '1759997430' }, + { doc_count: 4, fieldValue: '1764158287' }, + { doc_count: 4, fieldValue: '1864834432' }, + { doc_count: 4, fieldValue: '1867260038' }, + { doc_count: 4, fieldValue: '1880041108' }, + { doc_count: 4, fieldValue: '1919882921' }, + { doc_count: 4, fieldValue: '1940824684' }, + { doc_count: 4, fieldValue: '1974037124' }, + { doc_count: 4, fieldValue: '1992199813' }, +].map((d) => ({ + doc_count: d.doc_count, + bg_count: 0, + fieldName: 'items', + fieldValue: d.fieldValue, + key: `items:${d.fieldValue}`, + normalizedScore: 0, + pValue: 1, + score: 0, + total_bg_count: 0, + total_doc_count: 0, + type: 'keyword', +})); + +export const frequentItemSetsLargeArraysGroups: SignificantItemGroup[] = [ + { + id: '1934634723', + group: [ + { + key: 'items:2145700416', + type: 'keyword', + fieldName: 'items', + fieldValue: '2145700416', + docCount: 11, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 11, + pValue: 1, + }, + { + id: '4064691194', + group: [ + { + key: 'items:1104487811', + type: 'keyword', + fieldName: 'items', + fieldValue: '1104487811', + docCount: 11, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 11, + pValue: 1, + }, + { + id: '278504570', + group: [ + { + key: 'items:1416571574', + type: 'keyword', + fieldName: 'items', + fieldValue: '1416571574', + docCount: 11, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 11, + pValue: 1, + }, + { + id: '2852990448', + group: [ + { + key: 'items:1157393364', + type: 'keyword', + fieldName: 'items', + fieldValue: '1157393364', + docCount: 9, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 9, + pValue: 1, + }, + { + id: '1149803824', + group: [ + { + key: 'items:2998254532', + type: 'keyword', + fieldName: 'items', + fieldValue: '2998254532', + docCount: 9, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 9, + pValue: 1, + }, + { + id: '2420022128', + group: [ + { + key: 'items:1808235940', + type: 'keyword', + fieldName: 'items', + fieldValue: '1808235940', + docCount: 9, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 9, + pValue: 1, + }, + { + id: '2094912046', + group: [ + { + key: 'items:32293944', + type: 'keyword', + fieldName: 'items', + fieldValue: '32293944', + docCount: 8, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 8, + pValue: 1, + }, + { + id: '1178034827', + group: [ + { + key: 'items:894704468', + type: 'keyword', + fieldName: 'items', + fieldValue: '894704468', + docCount: 8, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 8, + pValue: 1, + }, + { + id: '1506122217', + group: [ + { + key: 'items:3777335490', + type: 'keyword', + fieldName: 'items', + fieldValue: '3777335490', + docCount: 8, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 8, + pValue: 1, + }, + { + id: '3982887134', + group: [ + { + key: 'items:550501793', + type: 'keyword', + fieldName: 'items', + fieldValue: '550501793', + docCount: 8, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 8, + pValue: 1, + }, + { + id: '1840747948', + group: [ + { + key: 'items:23762256', + type: 'keyword', + fieldName: 'items', + fieldValue: '23762256', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '1444907364', + group: [ + { + key: 'items:464464365', + type: 'keyword', + fieldName: 'items', + fieldValue: '464464365', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '1328704288', + group: [ + { + key: 'items:625944587', + type: 'keyword', + fieldName: 'items', + fieldValue: '625944587', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '1326810570', + group: [ + { + key: 'items:1979833532', + type: 'keyword', + fieldName: 'items', + fieldValue: '1979833532', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '1030906346', + group: [ + { + key: 'items:3127779728', + type: 'keyword', + fieldName: 'items', + fieldValue: '3127779728', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '1205078154', + group: [ + { + key: 'items:65096514', + type: 'keyword', + fieldName: 'items', + fieldValue: '65096514', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '3905594284', + group: [ + { + key: 'items:3234612808', + type: 'keyword', + fieldName: 'items', + fieldValue: '3234612808', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '779923180', + group: [ + { + key: 'items:1548316370', + type: 'keyword', + fieldName: 'items', + fieldValue: '1548316370', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '4203644800', + group: [ + { + key: 'items:505971607', + type: 'keyword', + fieldName: 'items', + fieldValue: '505971607', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '1237028536', + group: [ + { + key: 'items:850909865', + type: 'keyword', + fieldName: 'items', + fieldValue: '850909865', + docCount: 7, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 7, + pValue: 1, + }, + { + id: '3610776542', + group: [ + { + key: 'items:333459111', + type: 'keyword', + fieldName: 'items', + fieldValue: '333459111', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '4004516458', + group: [ + { + key: 'items:1351408438', + type: 'keyword', + fieldName: 'items', + fieldValue: '1351408438', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '100632746', + group: [ + { + key: 'items:1493078886', + type: 'keyword', + fieldName: 'items', + fieldValue: '1493078886', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '2934370474', + group: [ + { + key: 'items:1616339276', + type: 'keyword', + fieldName: 'items', + fieldValue: '1616339276', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '2551307178', + group: [ + { + key: 'items:2380606900', + type: 'keyword', + fieldName: 'items', + fieldValue: '2380606900', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '1429234', + group: [ + { + key: 'items:338634143', + type: 'keyword', + fieldName: 'items', + fieldValue: '338634143', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '133348698', + group: [ + { + key: 'items:456806730', + type: 'keyword', + fieldName: 'items', + fieldValue: '456806730', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '3952677514', + group: [ + { + key: 'items:644711789', + type: 'keyword', + fieldName: 'items', + fieldValue: '644711789', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '3477852394', + group: [ + { + key: 'items:1883416844', + type: 'keyword', + fieldName: 'items', + fieldValue: '1883416844', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '130442059', + group: [ + { + key: 'items:2057777897', + type: 'keyword', + fieldName: 'items', + fieldValue: '2057777897', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '2237656830', + group: [ + { + key: 'items:683600999', + type: 'keyword', + fieldName: 'items', + fieldValue: '683600999', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '1080686951', + group: [ + { + key: 'items:998290200', + type: 'keyword', + fieldName: 'items', + fieldValue: '998290200', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '817958570', + group: [ + { + key: 'items:1652565419', + type: 'keyword', + fieldName: 'items', + fieldValue: '1652565419', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '1326053323', + group: [ + { + key: 'items:2148044396', + type: 'keyword', + fieldName: 'items', + fieldValue: '2148044396', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '830437418', + group: [ + { + key: 'items:3950054860', + type: 'keyword', + fieldName: 'items', + fieldValue: '3950054860', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '823747627', + group: [ + { + key: 'items:3568288730', + type: 'keyword', + fieldName: 'items', + fieldValue: '3568288730', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '1450084203', + group: [ + { + key: 'items:3305614422', + type: 'keyword', + fieldName: 'items', + fieldValue: '3305614422', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '1388829802', + group: [ + { + key: 'items:1846471239', + type: 'keyword', + fieldName: 'items', + fieldValue: '1846471239', + docCount: 6, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 6, + pValue: 1, + }, + { + id: '1557137580', + group: [ + { + key: 'items:1310779384', + type: 'keyword', + fieldName: 'items', + fieldValue: '1310779384', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '2006719596', + group: [ + { + key: 'items:1432609585', + type: 'keyword', + fieldName: 'items', + fieldValue: '1432609585', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '1660605452', + group: [ + { + key: 'items:1437073018', + type: 'keyword', + fieldName: 'items', + fieldValue: '1437073018', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '956896360', + group: [ + { + key: 'items:1475971156', + type: 'keyword', + fieldName: 'items', + fieldValue: '1475971156', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '1382579660', + group: [ + { + key: 'items:1853107498', + type: 'keyword', + fieldName: 'items', + fieldValue: '1853107498', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '2888678696', + group: [ + { + key: 'items:1136953980', + type: 'keyword', + fieldName: 'items', + fieldValue: '1136953980', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '2874685992', + group: [ + { + key: 'items:1214124778', + type: 'keyword', + fieldName: 'items', + fieldValue: '1214124778', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '845567660', + group: [ + { + key: 'items:1325698447', + type: 'keyword', + fieldName: 'items', + fieldValue: '1325698447', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '2473046696', + group: [ + { + key: 'items:1439966722', + type: 'keyword', + fieldName: 'items', + fieldValue: '1439966722', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '611449132', + group: [ + { + key: 'items:1690511642', + type: 'keyword', + fieldName: 'items', + fieldValue: '1690511642', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '1474040428', + group: [ + { + key: 'items:1129761594', + type: 'keyword', + fieldName: 'items', + fieldValue: '1129761594', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '122100136', + group: [ + { + key: 'items:1600940924', + type: 'keyword', + fieldName: 'items', + fieldValue: '1600940924', + docCount: 5, + pValue: 1, + duplicate: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '942208673', + group: [ + { + key: 'items:1129761594', + type: 'keyword', + fieldName: 'items', + fieldValue: '1129761594', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1136953980', + type: 'keyword', + fieldName: 'items', + fieldValue: '1136953980', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1214124778', + type: 'keyword', + fieldName: 'items', + fieldValue: '1214124778', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1310779384', + type: 'keyword', + fieldName: 'items', + fieldValue: '1310779384', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1325698447', + type: 'keyword', + fieldName: 'items', + fieldValue: '1325698447', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1432609585', + type: 'keyword', + fieldName: 'items', + fieldValue: '1432609585', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1437073018', + type: 'keyword', + fieldName: 'items', + fieldValue: '1437073018', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1439966722', + type: 'keyword', + fieldName: 'items', + fieldValue: '1439966722', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1475971156', + type: 'keyword', + fieldName: 'items', + fieldValue: '1475971156', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1600940924', + type: 'keyword', + fieldName: 'items', + fieldValue: '1600940924', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1690511642', + type: 'keyword', + fieldName: 'items', + fieldValue: '1690511642', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1853107498', + type: 'keyword', + fieldName: 'items', + fieldValue: '1853107498', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1878061294', + type: 'keyword', + fieldName: 'items', + fieldValue: '1878061294', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1919542521', + type: 'keyword', + fieldName: 'items', + fieldValue: '1919542521', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:1924864316', + type: 'keyword', + fieldName: 'items', + fieldValue: '1924864316', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:2062952599', + type: 'keyword', + fieldName: 'items', + fieldValue: '2062952599', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:2172049200', + type: 'keyword', + fieldName: 'items', + fieldValue: '2172049200', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:2387317510', + type: 'keyword', + fieldName: 'items', + fieldValue: '2387317510', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:2411863132', + type: 'keyword', + fieldName: 'items', + fieldValue: '2411863132', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:2463774400', + type: 'keyword', + fieldName: 'items', + fieldValue: '2463774400', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:2524040016', + type: 'keyword', + fieldName: 'items', + fieldValue: '2524040016', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:315977894', + type: 'keyword', + fieldName: 'items', + fieldValue: '315977894', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3341153940', + type: 'keyword', + fieldName: 'items', + fieldValue: '3341153940', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3419329722', + type: 'keyword', + fieldName: 'items', + fieldValue: '3419329722', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3561517034', + type: 'keyword', + fieldName: 'items', + fieldValue: '3561517034', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3588882742', + type: 'keyword', + fieldName: 'items', + fieldValue: '3588882742', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:369147719', + type: 'keyword', + fieldName: 'items', + fieldValue: '369147719', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3700075048', + type: 'keyword', + fieldName: 'items', + fieldValue: '3700075048', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3850123478', + type: 'keyword', + fieldName: 'items', + fieldValue: '3850123478', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:3899749436', + type: 'keyword', + fieldName: 'items', + fieldValue: '3899749436', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:39215494', + type: 'keyword', + fieldName: 'items', + fieldValue: '39215494', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:4009453878', + type: 'keyword', + fieldName: 'items', + fieldValue: '4009453878', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:422724693', + type: 'keyword', + fieldName: 'items', + fieldValue: '422724693', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:471153522', + type: 'keyword', + fieldName: 'items', + fieldValue: '471153522', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:511726177', + type: 'keyword', + fieldName: 'items', + fieldValue: '511726177', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:629341211', + type: 'keyword', + fieldName: 'items', + fieldValue: '629341211', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:774389318', + type: 'keyword', + fieldName: 'items', + fieldValue: '774389318', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + { + key: 'items:912657112', + type: 'keyword', + fieldName: 'items', + fieldValue: '912657112', + duplicate: 1, + docCount: 5, + pValue: 1, + }, + ], + docCount: 5, + pValue: 1, + }, + { + id: '3524731882', + group: [ + { + key: 'items:1028474515', + type: 'keyword', + fieldName: 'items', + fieldValue: '1028474515', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1073297840', + type: 'keyword', + fieldName: 'items', + fieldValue: '1073297840', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1091455238', + type: 'keyword', + fieldName: 'items', + fieldValue: '1091455238', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1103890809', + type: 'keyword', + fieldName: 'items', + fieldValue: '1103890809', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1124226732', + type: 'keyword', + fieldName: 'items', + fieldValue: '1124226732', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1165010485', + type: 'keyword', + fieldName: 'items', + fieldValue: '1165010485', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1175209945', + type: 'keyword', + fieldName: 'items', + fieldValue: '1175209945', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1183454328', + type: 'keyword', + fieldName: 'items', + fieldValue: '1183454328', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:134825909', + type: 'keyword', + fieldName: 'items', + fieldValue: '134825909', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1445245168', + type: 'keyword', + fieldName: 'items', + fieldValue: '1445245168', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1499538523', + type: 'keyword', + fieldName: 'items', + fieldValue: '1499538523', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:153528430', + type: 'keyword', + fieldName: 'items', + fieldValue: '153528430', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1569561080', + type: 'keyword', + fieldName: 'items', + fieldValue: '1569561080', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1627808055', + type: 'keyword', + fieldName: 'items', + fieldValue: '1627808055', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1756911944', + type: 'keyword', + fieldName: 'items', + fieldValue: '1756911944', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1759997430', + type: 'keyword', + fieldName: 'items', + fieldValue: '1759997430', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1764158287', + type: 'keyword', + fieldName: 'items', + fieldValue: '1764158287', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1864834432', + type: 'keyword', + fieldName: 'items', + fieldValue: '1864834432', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1867260038', + type: 'keyword', + fieldName: 'items', + fieldValue: '1867260038', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1880041108', + type: 'keyword', + fieldName: 'items', + fieldValue: '1880041108', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1919882921', + type: 'keyword', + fieldName: 'items', + fieldValue: '1919882921', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1940824684', + type: 'keyword', + fieldName: 'items', + fieldValue: '1940824684', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1974037124', + type: 'keyword', + fieldName: 'items', + fieldValue: '1974037124', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + { + key: 'items:1992199813', + type: 'keyword', + fieldName: 'items', + fieldValue: '1992199813', + duplicate: 1, + docCount: 4, + pValue: 1, + }, + ], + docCount: 4, + pValue: 1, + }, +]; diff --git a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts index 06ec8e19b10c3..1f90a77862a97 100644 --- a/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts +++ b/x-pack/test/functional/apps/ml/stack_management_jobs/manage_spaces.ts @@ -106,7 +106,8 @@ export default function ({ getService }: FtrProviderContext) { } } - describe('manage spaces', function () { + // Failing: See https://github.com/elastic/kibana/issues/177762 + describe.skip('manage spaces', function () { this.tags(['ml']); before(async () => { await esArchiver.loadIfNeeded('x-pack/test/functional/es_archives/ml/farequote'); @@ -195,7 +196,8 @@ export default function ({ getService }: FtrProviderContext) { ]); }); - it('should edit job space assignment', async () => { + // FLAKY: https://github.com/elastic/kibana/issues/146722 + it.skip('should edit job space assignment', async () => { // AD await ml.navigation.navigateToStackManagementJobsListPageAnomalyDetectionTab(); await ml.stackManagementJobs.openJobSpacesFlyout('anomaly-detector', testData.adJobId); diff --git a/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts b/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts index 824e296d448d3..bd1d6c94810b9 100644 --- a/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts +++ b/x-pack/test/functional/services/aiops/log_rate_analysis_data_generator.ts @@ -11,6 +11,8 @@ import { LOG_RATE_ANALYSIS_TYPE } from '@kbn/aiops-utils'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { frequentItemSetsLargeArraysSource } from '../../apps/aiops/log_rate_analysis/test_data/__mocks__/frequent_item_sets_large_arrays'; + const LOG_RATE_ANALYSYS_DATA_GENERATOR = { KIBANA_SAMPLE_DATA_LOGS: 'kibana_sample_data_logs', FAREQUOTE_WITH_SPIKE: 'farequote_with_spike', @@ -24,6 +26,7 @@ const LOG_RATE_ANALYSYS_DATA_GENERATOR = { ARTIFICIAL_LOGS_WITH_SPIKE_TEXTFIELD: 'artificial_logs_with_spike_textfield', ARTIFICIAL_LOGS_WITH_DIP: 'artificial_logs_with_dip', ARTIFICIAL_LOGS_WITH_DIP_TEXTFIELD: 'artificial_logs_with_dip_textfield', + LARGE_ARRAYS: 'large_arrays', } as const; export type LogRateAnalysisDataGenerator = typeof LOG_RATE_ANALYSYS_DATA_GENERATOR[keyof typeof LOG_RATE_ANALYSYS_DATA_GENERATOR]; @@ -272,14 +275,10 @@ export function LogRateAnalysisDataGeneratorProvider({ getService }: FtrProvider case 'artificial_logs_with_dip_zerodocsfallback': case 'artificial_logs_with_dip_textfield_zerodocsfallback': try { - const indexExists = await es.indices.exists({ + await es.indices.delete({ index: dataGenerator, + ignore_unavailable: true, }); - if (indexExists) { - await es.indices.delete({ - index: dataGenerator, - }); - } } catch (e) { log.info(`Could not delete index '${dataGenerator}' in before() callback`); } @@ -324,6 +323,47 @@ export function LogRateAnalysisDataGeneratorProvider({ getService }: FtrProvider }); break; + case 'large_arrays': + try { + await es.indices.delete({ + index: dataGenerator, + ignore_unavailable: true, + }); + } catch (e) { + log.info(`Could not delete index '${dataGenerator}' in before() callback`); + } + + // Create index with mapping + await es.indices.create({ + index: dataGenerator, + mappings: { + properties: { + items: { type: 'keyword' }, + '@timestamp': { type: 'date' }, + }, + }, + }); + + interface DocWithArray { + '@timestamp': number; + items: string[]; + } + + await es.bulk({ + refresh: 'wait_for', + body: frequentItemSetsLargeArraysSource.reduce((docs, items) => { + if (docs === undefined) return []; + docs.push({ index: { _index: dataGenerator } }); + docs.push({ + '@timestamp': 1562254538700, + items, + }); + return docs; + }, [] as estypes.BulkRequest['body']), + }); + + break; + default: log.error(`Unsupported data generator '${dataGenerator}`); } @@ -349,6 +389,7 @@ export function LogRateAnalysisDataGeneratorProvider({ getService }: FtrProvider case 'artificial_logs_with_spike_textfield_zerodocsfallback': case 'artificial_logs_with_dip_zerodocsfallback': case 'artificial_logs_with_dip_textfield_zerodocsfallback': + case 'large_arrays': try { await es.indices.delete({ index: dataGenerator, diff --git a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts index eb3881f8123a5..5e99105c08e43 100644 --- a/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts +++ b/x-pack/test/security_solution_cypress/cypress/e2e/investigations/dasbhoards/detection_response.cy.ts @@ -43,7 +43,13 @@ import { ALERTS_URL, DASHBOARDS_URL, DETECTION_AND_RESPONSE_URL } from '../../.. const TEST_USER_NAME = 'test'; const SIEM_KIBANA_HOST_NAME = 'siem-kibana'; -describe('Detection response view', { tags: ['@ess', '@serverless'] }, () => { +// Failing: See https://github.com/elastic/kibana/issues/177761 +// FLAKY: https://github.com/elastic/kibana/issues/168768 +// FLAKY: https://github.com/elastic/kibana/issues/168769 +// FLAKY: https://github.com/elastic/kibana/issues/168770 +// FLAKY: https://github.com/elastic/kibana/issues/168771 +// FLAKY: https://github.com/elastic/kibana/issues/168772 +describe.skip('Detection response view', { tags: ['@ess', '@serverless'] }, () => { before(() => { deleteAlertsAndRules(); cy.task('esArchiverLoad', { archiveName: 'auditbeat_multiple' }); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/infra/metadata.ts b/x-pack/test_serverless/api_integration/test_suites/observability/infra/metadata.ts index ee75931cb528a..3dc4f333611ea 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/infra/metadata.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/infra/metadata.ts @@ -39,7 +39,8 @@ export default function ({ getService }: FtrProviderContext) { describe('API /infra/metadata', () => { describe('works', () => { - describe('Host asset type', () => { + // FLAKY: https://github.com/elastic/kibana/issues/177381 + describe.skip('Host asset type', () => { before(() => esArchiver.load(ARCHIVE_NAME)); after(() => esArchiver.unload(ARCHIVE_NAME)); diff --git a/yarn.lock b/yarn.lock index 98b09ef7cd0dd..98a38ae6066cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1743,10 +1743,10 @@ resolved "https://registry.yarnpkg.com/@elastic/eslint-plugin-eui/-/eslint-plugin-eui-0.0.2.tgz#56b9ef03984a05cc213772ae3713ea8ef47b0314" integrity sha512-IoxURM5zraoQ7C8f+mJb9HYSENiZGgRVcG4tLQxE61yHNNRDXtGDWTZh8N1KIHcsqN1CEPETjuzBXkJYF/fDiQ== -"@elastic/eui@93.1.1": - version "93.1.1" - resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-93.1.1.tgz#03c9667456664978c3477abef52afd6aceb48ebf" - integrity sha512-YnmNST8PmgAyeahDFTUTpToiBAc6gOyK/RSSqx6NTxCmUCwXnV7Oog9TUHlQ8tYGLjfFXrAja6Msy0X2VuxHOA== +"@elastic/eui@93.2.0": + version "93.2.0" + resolved "https://registry.yarnpkg.com/@elastic/eui/-/eui-93.2.0.tgz#ebe74c4aefcf78cd72a8db7332666a7aa0681954" + integrity sha512-YPJM+hs1Ouv7Fc04JZiEOT5nCNBMFKsQwKmmGqzEwpMDsXpx6NKT0s0AlRO43JabTWBHL9yIRE4aORhAzH6pBQ== dependencies: "@hello-pangea/dnd" "^16.3.0" "@types/lodash" "^4.14.198"