[data views] fix renamed field filters (#162860)

## Summary

Field filters (aka source filters) are given a `clientId` when they're
changed. Unfortunately this is missing from the schema which can result
in schema failures.

The schema has been fixed and a functional test has been added that
verifies field filter renaming functionality.

Closes: https://github.com/elastic/kibana/issues/162611

## Release Note

Resolves potential errors present in v8.9.0 with data views that contain
field filters that have been edited.
This commit is contained in:
Matthew Kime 2023-08-03 05:48:40 -05:00 committed by GitHub
parent 65fd7ad260
commit d9f5223e2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 0 deletions

View file

@ -4,6 +4,7 @@ exports[`Table editing should show a save button 1`] = `
<div>
<EuiButtonIcon
aria-label="Edit"
data-test-subj="edit_filter-tim*"
iconType="pencil"
onClick={[Function]}
size="s"

View file

@ -131,6 +131,7 @@ export class Table extends Component<TableProps, TableState> {
value={this.state.editingFilterValue}
onChange={this.onEditingFilterChange}
onKeyDown={this.onEditFieldKeyDown}
data-test-subj={`filter_input_${value}`}
/>
);
}
@ -187,6 +188,7 @@ export class Table extends Component<TableProps, TableState> {
}}
iconType="checkInCircleFilled"
aria-label={saveAria}
data-test-subj={`save_filter-${filter.value}`}
/>
<EuiButtonIcon
size="s"
@ -207,6 +209,7 @@ export class Table extends Component<TableProps, TableState> {
onClick={() => this.startEditingFilter(filter.clientId, filter.value)}
iconType="pencil"
aria-label={editAria}
data-test-subj={`edit_filter-${filter.value}`}
/>
<EuiButtonIcon
size="s"

View file

@ -27,6 +27,7 @@ const dataViewAttributesSchema = schema.object(
schema.arrayOf(
schema.object({
value: schema.string(),
clientId: schema.maybe(schema.oneOf([schema.string(), schema.number()])),
})
)
),

View file

@ -517,6 +517,7 @@ export type DataViewSpec = {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
export type SourceFilter = {
value: string;
clientId?: string | number;
};
export interface HasDataService {

View file

@ -40,6 +40,7 @@ const indexPatternUpdateSchema = schema.object({
schema.arrayOf(
schema.object({
value: schema.string(),
clientId: schema.maybe(schema.oneOf([schema.string(), schema.number()])),
})
)
),

View file

@ -17,6 +17,7 @@ import { RuntimePrimitiveTypes, RuntimeType } from '../../common/types';
export type SourceFilterRestResponse = {
value: string;
clientId?: string | number;
};
export type AggregationRestrictionsRestResponse = Record<

View file

@ -26,6 +26,7 @@ export const dataViewSpecSchema = schema.object({
schema.arrayOf(
schema.object({
value: schema.string(),
clientId: schema.maybe(schema.oneOf([schema.string(), schema.number()])),
})
)
),

View file

@ -0,0 +1,67 @@
/*
* 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.
*/
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const browser = getService('browser');
const retry = getService('retry');
const find = getService('find');
const PageObjects = getPageObjects(['common', 'home', 'settings', 'discover', 'header']);
describe('data view field filters', function describeIndexTests() {
before(async function () {
await browser.setWindowSize(1200, 800);
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
});
after(async () => {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
});
it('can create field filter', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
await PageObjects.settings.clickSourceFiltersTab();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.settings.addFieldFilter('a');
expect(parseInt(await PageObjects.settings.getFieldFilterTabCount(), 10)).to.be(1);
});
it('can modify a field filter', async function () {
await PageObjects.settings.editFieldFilter('a', 'z');
// reload page and verify change
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
await PageObjects.settings.clickSourceFiltersTab();
const table = await find.byClassName('euiTable');
await retry.waitFor('field filter that was changed', async () => {
const tableCells = await table.findAllByCssSelector('td');
const fieldNames = await Promise.all(
tableCells.map(async (cell) => {
return (await cell.getVisibleText()).trim();
})
);
return fieldNames.includes('z');
});
});
});
}

View file

@ -46,5 +46,6 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_data_view_relationships'));
loadTestFile(require.resolve('./_edit_field'));
loadTestFile(require.resolve('./_files'));
loadTestFile(require.resolve('./_data_view_field_filters'));
});
}

View file

@ -291,6 +291,13 @@ export class SettingsPageObject extends FtrService {
});
}
async getFieldFilterTabCount() {
return await this.retry.try(async () => {
const text = await this.testSubjects.getVisibleText('tab-sourceFilters');
return text.split(' ')[2].replace(/\((.*)\)/, '$1');
});
}
async getFieldNames() {
const fieldNameCells = await this.testSubjects.findAll('editIndexPattern > indexedFieldName');
return await Promise.all(
@ -747,6 +754,23 @@ export class SettingsPageObject extends FtrService {
}
}
async editFieldFilter(name: string, newName: string) {
await this.testSubjects.click(`edit_filter-${name}`);
await this.testSubjects.setValue(`filter_input_${name}`, newName);
await this.testSubjects.click(`save_filter-${name}`);
const table = await this.find.byClassName('euiTable');
await this.retry.waitFor('field filter to be changed', async () => {
const tableCells = await table.findAllByCssSelector('td');
const fieldNames = await Promise.all(
tableCells.map(async (cell) => {
return (await cell.getVisibleText()).trim();
})
);
return fieldNames.includes(newName);
});
}
async addFieldFilter(name: string) {
await this.testSubjects.click('tab-sourceFilters');
await this.find.setValue('.euiFieldText', name);