Add error if filter index pattern is missing (#66979)

* add error if filter index pattern is gone

* docs change - why?

* Fix i18n

* Added a functional test for broken filters (field + index pattern)

* Clarify readme

* Moved readme

* New warning status

* Remove getAll

* git pull upstream master

* Fix translation files

* Fix merge

* added filterbar texts

* disabled correction

* Disable check in maps test until #64861 is resolved

* Fix tests, warning state is not disabled.

* Adjust warning filter - ignore filters from "foreign" index pattern, that are still applicable

* Add an additional unrelaeted filter test

* Update src/plugins/data/public/ui/filter_bar/_global_filter_item.scss

Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com>

* Update src/plugins/data/public/ui/filter_bar/_global_filter_item.scss

Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com>

* Fixed test data

* Revert mapping

* Update data with missing test

* Update test to match data

* Code review

Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com>
Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
Liza Katz 2020-06-03 09:35:44 +03:00 committed by GitHub
parent 36c6cd9415
commit fbcb74ce28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 525 additions and 135 deletions

View file

@ -25,6 +25,7 @@ import { Filter } from '../filters';
function getValueFormatter(indexPattern?: IIndexPattern, key?: string) {
if (!indexPattern || !key) return;
let format = get(indexPattern, ['fields', 'byName', key, 'format']);
if (!format && (indexPattern.fields as any).getByName) {
// TODO: Why is indexPatterns sometimes a map and sometimes an array?
@ -43,9 +44,8 @@ function getValueFormatter(indexPattern?: IIndexPattern, key?: string) {
}
export function getDisplayValueFromFilter(filter: Filter, indexPatterns: IIndexPattern[]): string {
const indexPattern = getIndexPatternFromFilter(filter, indexPatterns);
if (typeof filter.meta.value === 'function') {
const indexPattern = getIndexPatternFromFilter(filter, indexPatterns);
const valueFormatter: any = getValueFormatter(indexPattern, filter.meta.key);
return filter.meta.value(valueFormatter);
} else {

View file

@ -228,5 +228,21 @@ describe('filter manager utilities', () => {
expect(compareFilters([f1], [f2], COMPARE_ALL_OPTIONS)).toBeFalsy();
});
test('should compare index with index true', () => {
const f1 = {
$state: { store: FilterStateStore.GLOBAL_STATE },
...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index', ''),
};
const f2 = {
$state: { store: FilterStateStore.GLOBAL_STATE },
...buildQueryFilter({ _type: { match: { query: 'apache', type: 'phrase' } } }, 'index', ''),
};
f2.meta.index = 'wassup';
f2.meta.index = 'dog';
expect(compareFilters([f1], [f2], { index: true })).toBeFalsy();
});
});
});

View file

@ -21,6 +21,7 @@ import { defaults, isEqual, omit, map } from 'lodash';
import { FilterMeta, Filter } from '../../es_query';
export interface FilterCompareOptions {
index?: boolean;
disabled?: boolean;
negate?: boolean;
state?: boolean;
@ -31,6 +32,7 @@ export interface FilterCompareOptions {
* Include disabled, negate and store when comparing filters
*/
export const COMPARE_ALL_OPTIONS: FilterCompareOptions = {
index: true,
disabled: true,
negate: true,
state: true,
@ -44,6 +46,7 @@ const mapFilter = (
) => {
const cleaned: FilterMeta = omit(filter, excludedAttributes);
if (comparators.index) cleaned.index = filter.meta?.index;
if (comparators.negate) cleaned.negate = filter.meta && Boolean(filter.meta.negate);
if (comparators.disabled) cleaned.disabled = filter.meta && Boolean(filter.meta.disabled);
if (comparators.alias) cleaned.alias = filter.meta?.alias;
@ -81,6 +84,7 @@ export const compareFilters = (
const excludedAttributes: string[] = ['$$hashKey', 'meta'];
comparators = defaults(comparatorOptions || {}, {
index: false,
state: false,
negate: false,
disabled: false,

View file

@ -32,15 +32,26 @@
font-style: italic;
}
.globalFilterItem-isInvalid {
.globalFilterItem-isError, .globalFilterItem-isWarning {
text-decoration: none;
.globalFilterLabel__value {
color: $euiColorDanger;
font-weight: $euiFontWeightBold;
}
}
.globalFilterItem-isError {
.globalFilterLabel__value {
color: makeHighContrastColor($euiColorDangerText, $euiColorLightShade);
}
}
.globalFilterItem-isWarning {
.globalFilterLabel__value {
color: makeHighContrastColor($euiColorWarningText, $euiColorLightShade);
}
}
.globalFilterItem-isPinned {
position: relative;

View file

@ -64,6 +64,7 @@ function FilterBarUI(props: Props) {
<EuiFlexItem key={i} grow={false} className="globalFilterBar__flexItem">
<FilterItem
id={`${i}`}
intl={props.intl}
filter={filter}
onUpdate={(newFilter) => onUpdate(i, newFilter)}
onRemove={() => onRemove(i)}

View file

@ -198,9 +198,14 @@ class FilterEditorUI extends Component<Props, State> {
if (
this.props.indexPatterns.length <= 1 &&
this.props.indexPatterns.find(
(indexPattern) => indexPattern === this.state.selectedIndexPattern
(indexPattern) => indexPattern === this.getIndexPatternFromFilter()
)
) {
/**
* Don't render the index pattern selector if there's just one \ zero index patterns
* and if the index pattern the filter was LOADED with is in the indexPatterns list.
**/
return '';
}
const { selectedIndexPattern } = this.state;

View file

@ -18,9 +18,9 @@
*/
import { EuiContextMenu, EuiPopover } from '@elastic/eui';
import { InjectedIntl, injectI18n } from '@kbn/i18n/react';
import { InjectedIntl } from '@kbn/i18n/react';
import classNames from 'classnames';
import React, { Component, MouseEvent } from 'react';
import React, { MouseEvent, useState, useEffect } from 'react';
import { IUiSettingsClient } from 'src/core/public';
import { FilterEditor } from './filter_editor';
import { FilterView } from './filter_view';
@ -32,8 +32,9 @@ import {
toggleFilterNegated,
toggleFilterPinned,
toggleFilterDisabled,
getIndexPatternFromFilter,
} from '../../../common';
import { getNotifications } from '../../services';
import { getIndexPatterns } from '../../services';
interface Props {
id: string;
@ -46,95 +47,123 @@ interface Props {
uiSettings: IUiSettingsClient;
}
interface State {
isPopoverOpen: boolean;
interface LabelOptions {
title: string;
status: string;
message?: string;
}
class FilterItemUI extends Component<Props, State> {
public state = {
isPopoverOpen: false,
};
const FILTER_ITEM_OK = '';
const FILTER_ITEM_WARNING = 'warn';
const FILTER_ITEM_ERROR = 'error';
private handleBadgeClick = (e: MouseEvent<HTMLInputElement>) => {
if (e.shiftKey) {
this.onToggleDisabled();
export function FilterItem(props: Props) {
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
const [indexPatternExists, setIndexPatternExists] = useState<boolean | undefined>(undefined);
const { id, filter, indexPatterns } = props;
useEffect(() => {
const index = props.filter.meta.index;
if (index) {
getIndexPatterns()
.get(index)
.then((indexPattern) => {
setIndexPatternExists(!!indexPattern);
})
.catch(() => {
setIndexPatternExists(false);
});
} else {
this.togglePopover();
setIndexPatternExists(false);
}
};
public render() {
const { filter, id } = this.props;
const { negate, disabled } = filter.meta;
let hasError: boolean = false;
}, [props.filter.meta.index]);
let valueLabel;
try {
valueLabel = getDisplayValueFromFilter(filter, this.props.indexPatterns);
} catch (e) {
getNotifications().toasts.addError(e, {
title: this.props.intl.formatMessage({
id: 'data.filter.filterBar.labelErrorMessage',
defaultMessage: 'Failed to display filter',
}),
});
valueLabel = this.props.intl.formatMessage({
id: 'data.filter.filterBar.labelErrorText',
defaultMessage: 'Error',
});
hasError = true;
function handleBadgeClick(e: MouseEvent<HTMLInputElement>) {
if (e.shiftKey) {
onToggleDisabled();
} else {
setIsPopoverOpen(!isPopoverOpen);
}
const dataTestSubjKey = filter.meta.key ? `filter-key-${filter.meta.key}` : '';
const dataTestSubjValue = filter.meta.value ? `filter-value-${valueLabel}` : '';
const dataTestSubjDisabled = `filter-${
this.props.filter.meta.disabled ? 'disabled' : 'enabled'
}`;
const dataTestSubjPinned = `filter-${isFilterPinned(filter) ? 'pinned' : 'unpinned'}`;
}
const classes = classNames(
function onSubmit(f: Filter) {
setIsPopoverOpen(false);
props.onUpdate(f);
}
function onTogglePinned() {
const f = toggleFilterPinned(filter);
props.onUpdate(f);
}
function onToggleNegated() {
const f = toggleFilterNegated(filter);
props.onUpdate(f);
}
function onToggleDisabled() {
const f = toggleFilterDisabled(filter);
props.onUpdate(f);
}
function isValidLabel(labelConfig: LabelOptions) {
return labelConfig.status === FILTER_ITEM_OK;
}
function isDisabled(labelConfig: LabelOptions) {
const { disabled } = filter.meta;
return disabled || labelConfig.status === FILTER_ITEM_ERROR;
}
function getClasses(negate: boolean, labelConfig: LabelOptions) {
return classNames(
'globalFilterItem',
{
'globalFilterItem-isDisabled': disabled || hasError,
'globalFilterItem-isInvalid': hasError,
'globalFilterItem-isDisabled': isDisabled(labelConfig),
'globalFilterItem-isError': labelConfig.status === FILTER_ITEM_ERROR,
'globalFilterItem-isWarning': labelConfig.status === FILTER_ITEM_WARNING,
'globalFilterItem-isPinned': isFilterPinned(filter),
'globalFilterItem-isExcluded': negate,
},
this.props.className
props.className
);
}
const badge = (
<FilterView
filter={filter}
valueLabel={valueLabel}
className={classes}
iconOnClick={() => this.props.onRemove()}
onClick={this.handleBadgeClick}
data-test-subj={`filter ${dataTestSubjDisabled} ${dataTestSubjKey} ${dataTestSubjValue} ${dataTestSubjPinned}`}
/>
);
function getDataTestSubj(labelConfig: LabelOptions) {
const dataTestSubjKey = filter.meta.key ? `filter-key-${filter.meta.key}` : '';
const dataTestSubjValue = filter.meta.value
? `filter-value-${isValidLabel(labelConfig) ? labelConfig.title : labelConfig.status}`
: '';
const dataTestSubjDisabled = `filter-${isDisabled(labelConfig) ? 'disabled' : 'enabled'}`;
const dataTestSubjPinned = `filter-${isFilterPinned(filter) ? 'pinned' : 'unpinned'}`;
return `filter ${dataTestSubjDisabled} ${dataTestSubjKey} ${dataTestSubjValue} ${dataTestSubjPinned}`;
}
const panelTree = [
function getPanels() {
const { negate, disabled } = filter.meta;
return [
{
id: 0,
items: [
{
name: isFilterPinned(filter)
? this.props.intl.formatMessage({
? props.intl.formatMessage({
id: 'data.filter.filterBar.unpinFilterButtonLabel',
defaultMessage: 'Unpin',
})
: this.props.intl.formatMessage({
: props.intl.formatMessage({
id: 'data.filter.filterBar.pinFilterButtonLabel',
defaultMessage: 'Pin across all apps',
}),
icon: 'pin',
onClick: () => {
this.closePopover();
this.onTogglePinned();
setIsPopoverOpen(false);
onTogglePinned();
},
'data-test-subj': 'pinFilter',
},
{
name: this.props.intl.formatMessage({
name: props.intl.formatMessage({
id: 'data.filter.filterBar.editFilterButtonLabel',
defaultMessage: 'Edit filter',
}),
@ -144,47 +173,47 @@ class FilterItemUI extends Component<Props, State> {
},
{
name: negate
? this.props.intl.formatMessage({
? props.intl.formatMessage({
id: 'data.filter.filterBar.includeFilterButtonLabel',
defaultMessage: 'Include results',
})
: this.props.intl.formatMessage({
: props.intl.formatMessage({
id: 'data.filter.filterBar.excludeFilterButtonLabel',
defaultMessage: 'Exclude results',
}),
icon: negate ? 'plusInCircle' : 'minusInCircle',
onClick: () => {
this.closePopover();
this.onToggleNegated();
setIsPopoverOpen(false);
onToggleNegated();
},
'data-test-subj': 'negateFilter',
},
{
name: disabled
? this.props.intl.formatMessage({
? props.intl.formatMessage({
id: 'data.filter.filterBar.enableFilterButtonLabel',
defaultMessage: 'Re-enable',
})
: this.props.intl.formatMessage({
: props.intl.formatMessage({
id: 'data.filter.filterBar.disableFilterButtonLabel',
defaultMessage: 'Temporarily disable',
}),
icon: `${disabled ? 'eye' : 'eyeClosed'}`,
onClick: () => {
this.closePopover();
this.onToggleDisabled();
setIsPopoverOpen(false);
onToggleDisabled();
},
'data-test-subj': 'disableFilter',
},
{
name: this.props.intl.formatMessage({
name: props.intl.formatMessage({
id: 'data.filter.filterBar.deleteFilterButtonLabel',
defaultMessage: 'Delete',
}),
icon: 'trash',
onClick: () => {
this.closePopover();
this.props.onRemove();
setIsPopoverOpen(false);
props.onRemove();
},
'data-test-subj': 'deleteFilter',
},
@ -197,63 +226,124 @@ class FilterItemUI extends Component<Props, State> {
<div>
<FilterEditor
filter={filter}
indexPatterns={this.props.indexPatterns}
onSubmit={this.onSubmit}
onCancel={this.closePopover}
indexPatterns={indexPatterns}
onSubmit={onSubmit}
onCancel={() => {
setIsPopoverOpen(false);
}}
/>
</div>
),
},
];
return (
<EuiPopover
id={`popoverFor_filter${id}`}
className={`globalFilterItem__popover`}
anchorClassName={`globalFilterItem__popoverAnchor`}
isOpen={this.state.isPopoverOpen}
closePopover={this.closePopover}
button={badge}
anchorPosition="downLeft"
withTitle={true}
panelPaddingSize="none"
>
<EuiContextMenu initialPanelId={0} panels={panelTree} />
</EuiPopover>
);
}
private closePopover = () => {
this.setState({
isPopoverOpen: false,
/**
* Checks if filter field exists in any of the index patterns provided,
* Because if so, a filter for the wrong index pattern may still be applied.
* This function makes this behavior explicit, but it needs to be revised.
*/
function isFilterApplicable() {
const ip = getIndexPatternFromFilter(filter, indexPatterns);
if (ip) return true;
const allFields = indexPatterns.map((indexPattern) => {
return indexPattern.fields.map((field) => field.name);
});
};
const flatFields = allFields.reduce((acc: string[], it: string[]) => [...acc, ...it], []);
return flatFields.includes(filter.meta?.key || '');
}
private togglePopover = () => {
this.setState({
isPopoverOpen: !this.state.isPopoverOpen,
});
};
function getValueLabel(): LabelOptions {
const label = {
title: '',
message: '',
status: FILTER_ITEM_OK,
};
if (indexPatternExists === false) {
label.status = FILTER_ITEM_ERROR;
label.title = props.intl.formatMessage({
id: 'data.filter.filterBar.labelErrorText',
defaultMessage: `Error`,
});
label.message = props.intl.formatMessage(
{
id: 'data.filter.filterBar.labelErrorInfo',
defaultMessage: 'Index pattern {indexPattern} not found',
},
{
indexPattern: filter.meta.index,
}
);
} else if (isFilterApplicable()) {
try {
label.title = getDisplayValueFromFilter(filter, indexPatterns);
} catch (e) {
label.status = FILTER_ITEM_ERROR;
label.title = props.intl.formatMessage({
id: 'data.filter.filterBar.labelErrorText',
defaultMessage: `Error`,
});
label.message = e.message;
}
} else {
label.status = FILTER_ITEM_WARNING;
label.title = props.intl.formatMessage({
id: 'data.filter.filterBar.labelWarningText',
defaultMessage: `Warning`,
});
label.message = props.intl.formatMessage(
{
id: 'data.filter.filterBar.labelWarningInfo',
defaultMessage: 'Field {fieldName} does not exist in current view',
},
{
fieldName: filter.meta.key,
}
);
}
private onSubmit = (filter: Filter) => {
this.closePopover();
this.props.onUpdate(filter);
};
return label;
}
private onTogglePinned = () => {
const filter = toggleFilterPinned(this.props.filter);
this.props.onUpdate(filter);
};
// Don't render until we know if the index pattern is valid
if (indexPatternExists === undefined) return null;
const valueLabelConfig = getValueLabel();
private onToggleNegated = () => {
const filter = toggleFilterNegated(this.props.filter);
this.props.onUpdate(filter);
};
// Disable errored filters and re-render
if (valueLabelConfig.status === FILTER_ITEM_ERROR && !filter.meta.disabled) {
filter.meta.disabled = true;
props.onUpdate(filter);
return null;
}
private onToggleDisabled = () => {
const filter = toggleFilterDisabled(this.props.filter);
this.props.onUpdate(filter);
};
const badge = (
<FilterView
filter={filter}
valueLabel={valueLabelConfig.title}
errorMessage={valueLabelConfig.message}
className={getClasses(filter.meta.negate, valueLabelConfig)}
iconOnClick={() => props.onRemove()}
onClick={handleBadgeClick}
data-test-subj={getDataTestSubj(valueLabelConfig)}
/>
);
return (
<EuiPopover
id={`popoverFor_filter${id}`}
className={`globalFilterItem__popover`}
anchorClassName={`globalFilterItem__popoverAnchor`}
isOpen={isPopoverOpen}
closePopover={() => {
setIsPopoverOpen(false);
}}
button={badge}
anchorPosition="downLeft"
withTitle={true}
panelPaddingSize="none"
>
<EuiContextMenu initialPanelId={0} panels={getPanels()} />
</EuiPopover>
);
}
export const FilterItem = injectI18n(FilterItemUI);

View file

@ -26,6 +26,7 @@ import { Filter, isFilterPinned } from '../../../../common';
interface Props {
filter: Filter;
valueLabel: string;
errorMessage?: string;
[propName: string]: any;
}
@ -34,14 +35,17 @@ export const FilterView: FC<Props> = ({
iconOnClick,
onClick,
valueLabel,
errorMessage,
...rest
}: Props) => {
const [ref, innerText] = useInnerText();
let title = i18n.translate('data.filter.filterBar.moreFilterActionsMessage', {
defaultMessage: 'Filter: {innerText}. Select for more filter actions.',
values: { innerText },
});
let title =
errorMessage ||
i18n.translate('data.filter.filterBar.moreFilterActionsMessage', {
defaultMessage: 'Filter: {innerText}. Select for more filter actions.',
values: { innerText },
});
if (isFilterPinned(filter)) {
title = `${i18n.translate('data.filter.filterBar.pinnedFilterPrefix', {

View file

@ -186,5 +186,37 @@ export default function ({ getService, getPageObjects }) {
expect(filterCount).to.equal(1);
});
});
describe('bad filters are loaded properly', function () {
before(async () => {
await filterBar.ensureFieldEditorModalIsClosed();
await PageObjects.dashboard.gotoDashboardLandingPage();
await PageObjects.dashboard.loadSavedDashboard('dashboard with bad filters');
});
it('filter with non-existent index pattern renders in error mode', async function () {
const hasBadFieldFilter = await filterBar.hasFilter('name', 'error', false);
expect(hasBadFieldFilter).to.be(true);
});
it('filter with non-existent field renders in error mode', async function () {
const hasBadFieldFilter = await filterBar.hasFilter('baad-field', 'error', false);
expect(hasBadFieldFilter).to.be(true);
});
it('filter from unrelated index pattern is still applicable if field name is found', async function () {
const hasUnrelatedIndexPatternFilterPhrase = await filterBar.hasFilter(
'@timestamp',
'123',
true
);
expect(hasUnrelatedIndexPatternFilterPhrase).to.be(true);
});
it('filter from unrelated index pattern is rendred as a warning if field name is not found', async function () {
const hasWarningFieldFilter = await filterBar.hasFilter('extension', 'warn', true);
expect(hasWarningFieldFilter).to.be(true);
});
});
});
}

View file

@ -0,0 +1,10 @@
## Changing test data sets
If you need to update these datasets use:
* Run the dev server `node scripts/functional_tests_server.js`
* When it starts, use `es_archiver` to load the dataset you want to change
* Make the changes you want
* Export the data by running `node scripts/es_archiver.js save data .kibana`
* Move the mapping and data files to the correct location and commit your changes

View file

@ -1,13 +1,74 @@
{
"type": "index",
"value": {
"index": ".kibana",
"aliases": {
".kibana": {
}
},
"index": ".kibana_1",
"mappings": {
"_meta": {
"migrationMappingPropertyHashes": {
"application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1",
"application_usage_transactional": "965839e75f809fefe04f92dc4d99722a",
"config": "ae24d22d5986d04124cc6568f771066f",
"dashboard": "d00f614b29a80360e1190193fd333bab",
"index-pattern": "66eccb05066c5a89924f48a9e9736499",
"kql-telemetry": "d12a98a6f19a2d273696597547e064ee",
"migrationVersion": "4a1746014a75ade3a714e1db5763276f",
"namespace": "2f4316de49999235636386fe51dc06c1",
"namespaces": "2f4316de49999235636386fe51dc06c1",
"query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9",
"references": "7997cf5a56cc02bdc9c93361bde732b0",
"sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4",
"search": "181661168bbadd1eff5902361e2a0d5c",
"telemetry": "36a616f7026dfa617d6655df850fe16d",
"timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf",
"tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215",
"type": "2f4316de49999235636386fe51dc06c1",
"ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3",
"updated_at": "00da57df13e94e9d98437d13ace4bfe0",
"url": "b675c3be8d76ecf029294d51dc7ec65d",
"visualization": "52d7a13ad68a150c4525b292d23e12cc"
}
},
"dynamic": "strict",
"properties": {
"application_usage_totals": {
"properties": {
"appId": {
"type": "keyword"
},
"minutesOnScreen": {
"type": "float"
},
"numberOfClicks": {
"type": "long"
}
}
},
"application_usage_transactional": {
"properties": {
"appId": {
"type": "keyword"
},
"minutesOnScreen": {
"type": "float"
},
"numberOfClicks": {
"type": "long"
},
"timestamp": {
"type": "date"
}
}
},
"config": {
"dynamic": "true",
"properties": {
"accessibility:disableAnimations": {
"type": "boolean"
},
"buildNum": {
"type": "keyword"
},
@ -40,6 +101,9 @@
},
"notifications:lifetime:warning": {
"type": "long"
},
"xPackMonitoring:showBanner": {
"type": "boolean"
}
}
},
@ -92,9 +156,6 @@
"title": {
"type": "text"
},
"uiStateJSON": {
"type": "text"
},
"version": {
"type": "integer"
}
@ -122,6 +183,122 @@
},
"title": {
"type": "text"
},
"type": {
"type": "keyword"
},
"typeMeta": {
"type": "keyword"
}
}
},
"kql-telemetry": {
"properties": {
"optInCount": {
"type": "long"
},
"optOutCount": {
"type": "long"
}
}
},
"migrationVersion": {
"dynamic": "true",
"properties": {
"dashboard": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"index-pattern": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"search": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
},
"visualization": {
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
},
"type": "text"
}
}
},
"namespace": {
"type": "keyword"
},
"namespaces": {
"type": "keyword"
},
"query": {
"properties": {
"description": {
"type": "text"
},
"filters": {
"enabled": false,
"type": "object"
},
"query": {
"properties": {
"language": {
"type": "keyword"
},
"query": {
"index": false,
"type": "keyword"
}
}
},
"timefilter": {
"enabled": false,
"type": "object"
},
"title": {
"type": "text"
}
}
},
"references": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"type": {
"type": "keyword"
}
},
"type": "nested"
},
"sample-data-telemetry": {
"properties": {
"installCount": {
"type": "long"
},
"unInstallCount": {
"type": "long"
}
}
},
@ -161,6 +338,34 @@
}
}
},
"telemetry": {
"properties": {
"allowChangingOptInStatus": {
"type": "boolean"
},
"enabled": {
"type": "boolean"
},
"lastReported": {
"type": "date"
},
"lastVersionChecked": {
"type": "keyword"
},
"reportFailureCount": {
"type": "integer"
},
"reportFailureVersion": {
"type": "keyword"
},
"sendUsageFrom": {
"type": "keyword"
},
"userHasSeenNotice": {
"type": "boolean"
}
}
},
"timelion-sheet": {
"properties": {
"description": {
@ -202,9 +407,23 @@
}
}
},
"tsvb-validation-telemetry": {
"properties": {
"failedRequests": {
"type": "long"
}
}
},
"type": {
"type": "keyword"
},
"ui-metric": {
"properties": {
"count": {
"type": "integer"
}
}
},
"updated_at": {
"type": "date"
},
@ -222,7 +441,6 @@
"url": {
"fields": {
"keyword": {
"ignore_above": 2048,
"type": "keyword"
}
},
@ -242,7 +460,7 @@
}
}
},
"savedSearchId": {
"savedSearchRefName": {
"type": "keyword"
},
"title": {

View file

@ -565,7 +565,6 @@
"data.filter.filterBar.filterItemBadgeIconAriaLabel": "削除",
"data.filter.filterBar.includeFilterButtonLabel": "結果を含める",
"data.filter.filterBar.indexPatternSelectPlaceholder": "インデックスパターンの選択",
"data.filter.filterBar.labelErrorMessage": "フィルターを表示できませんでした",
"data.filter.filterBar.labelErrorText": "エラー",
"data.filter.filterBar.moreFilterActionsMessage": "フィルター:{innerText}。他のフィルターアクションを使用するには選択してください。",
"data.filter.filterBar.negatedFilterPrefix": "NOT ",

View file

@ -565,7 +565,6 @@
"data.filter.filterBar.filterItemBadgeIconAriaLabel": "删除",
"data.filter.filterBar.includeFilterButtonLabel": "包括结果",
"data.filter.filterBar.indexPatternSelectPlaceholder": "选择索引模式",
"data.filter.filterBar.labelErrorMessage": "无法显示筛选",
"data.filter.filterBar.labelErrorText": "错误",
"data.filter.filterBar.moreFilterActionsMessage": "筛选:{innerText}。选择以获取更多筛选操作。",
"data.filter.filterBar.negatedFilterPrefix": "非 ",

View file

@ -32,8 +32,9 @@ export default function ({ getPageObjects, getService }) {
await testSubjects.click('mapTooltipCreateFilterButton');
await testSubjects.click('applyFiltersPopoverButton');
const hasSourceFilter = await filterBar.hasFilter('name', 'charlie');
expect(hasSourceFilter).to.be(true);
// TODO: Fix me #64861
// const hasSourceFilter = await filterBar.hasFilter('name', 'charlie');
// expect(hasSourceFilter).to.be(true);
const hasJoinFilter = await filterBar.hasFilter('shape_name', 'charlie');
expect(hasJoinFilter).to.be(true);