Fix issue with single value for fields params (#157930)

## Summary

The interface for fields allow array of string see below but the
validation with `@kbn/config-schema` only allow string. This will allow
to pass fields as array.

<img width="355" alt="image"
src="04c099c6-6a84-49ef-af45-587efa8e508b">


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Xavier Mouligneau 2023-05-17 08:42:45 -04:00 committed by GitHub
parent b736f02bc1
commit e850ef1ad3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 50 deletions

View file

@ -6,20 +6,20 @@
* Side Public License, v 1.
*/
import { parseMetaFields } from './fields_for';
import { parseFields } from './fields_for';
describe('_fields_for_wildcard', () => {
describe('parseMetaFields', () => {
it('should throw if receiving a string of comma-separated values', () => {
const value = '_source,_id';
expect(() => parseMetaFields(value)).toThrowErrorMatchingInlineSnapshot(
expect(() => parseFields(value)).toThrowErrorMatchingInlineSnapshot(
`"metaFields should be an array of field names, a JSON-stringified array of field names, or a single field name"`
);
});
it('should parse a stringified list of values', () => {
const value = JSON.stringify(['_source', '_id']);
const fields = parseMetaFields(value);
const fields = parseFields(value);
expect(fields).toMatchInlineSnapshot(`
Array [
"_source",
@ -30,7 +30,7 @@ describe('_fields_for_wildcard', () => {
it('should wrap a single value in an array', () => {
const value = '_source';
const fields = parseMetaFields(value);
const fields = parseFields(value);
expect(fields).toMatchInlineSnapshot(`
Array [
"_source",
@ -40,7 +40,7 @@ describe('_fields_for_wildcard', () => {
it('should return the array if already an array', () => {
const value = ['_source', '_id'];
const fields = parseMetaFields(value);
const fields = parseFields(value);
expect(fields).toMatchInlineSnapshot(`
Array [
"_source",

View file

@ -23,14 +23,14 @@ import type { DataViewsServerPluginStart, DataViewsServerPluginStartDependencies
* 2. A JSON-stringified array of field names
* 3. A single field name (not comma-separated)
* @returns an array of field names
* @param metaFields
* @param fields
*/
export const parseMetaFields = (metaFields: string | string[]): string[] => {
if (Array.isArray(metaFields)) return metaFields;
export const parseFields = (fields: string | string[]): string[] => {
if (Array.isArray(fields)) return fields;
try {
return JSON.parse(metaFields);
return JSON.parse(fields);
} catch (e) {
if (!metaFields.includes(',')) return [metaFields];
if (!fields.includes(',')) return [fields];
throw new Error(
'metaFields should be an array of field names, a JSON-stringified array of field names, or a single field name'
);
@ -60,7 +60,7 @@ const validate: RouteValidatorFullConfig<{}, IQuery, IBody> = {
rollup_index: schema.maybe(schema.string()),
allow_no_index: schema.maybe(schema.boolean()),
include_unmapped: schema.maybe(schema.boolean()),
fields: schema.maybe(schema.arrayOf(schema.string())),
fields: schema.maybe(schema.oneOf([schema.string(), schema.arrayOf(schema.string())])),
}),
// not available to get request
body: schema.maybe(schema.object({ index_filter: schema.any() })),
@ -81,8 +81,10 @@ const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, resp
const indexFilter = request.body?.index_filter;
let parsedFields: string[] = [];
let parsedMetaFields: string[] = [];
try {
parsedFields = parseMetaFields(metaFields);
parsedMetaFields = parseFields(metaFields);
parsedFields = parseFields(request.query.fields ?? []);
} catch (error) {
return response.badRequest();
}
@ -90,7 +92,7 @@ const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, resp
try {
const { fields, indices } = await indexPatterns.getFieldsForWildcard({
pattern,
metaFields: parsedFields,
metaFields: parsedMetaFields,
type,
rollupIndex,
fieldCapsOptions: {
@ -98,7 +100,7 @@ const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, resp
includeUnmapped,
},
indexFilter,
fields: request.query.fields,
...(parsedFields.length > 0 ? { fields: parsedFields } : {}),
});
return response.ok({

View file

@ -22,15 +22,6 @@ export default function ({ getService }) {
it('requires a pattern query param', () =>
supertest.get('/api/index_patterns/_fields_for_wildcard').query({}).expect(400));
it('accepts a JSON formatted meta_fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: JSON.stringify(['meta']),
})
.expect(200));
it('accepts include_unmapped param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
@ -40,33 +31,6 @@ export default function ({ getService }) {
})
.expect(200));
it('accepts meta_fields query param in string array', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: ['_id', 'meta'],
})
.expect(200));
it('accepts single meta_fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: ['_id'],
})
.expect(200));
it('rejects a comma-separated list of meta_fields', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: 'foo,bar',
})
.expect(400));
it('rejects unexpected query params', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
@ -75,5 +39,90 @@ export default function ({ getService }) {
[randomness.word()]: randomness.word(),
})
.expect(400));
describe('fields', () => {
it('accepts a JSON formatted fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
fields: JSON.stringify(['baz']),
})
.expect(200));
it('accepts meta_fields query param in string array', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
fields: ['baz', 'foo'],
})
.expect(200));
it('accepts single array fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
fields: ['baz'],
})
.expect(200));
it('accepts single fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
fields: 'baz',
})
.expect(200));
it('rejects a comma-separated list of fields', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
fields: 'foo,bar',
})
.expect(400));
});
describe('meta_fields', () => {
it('accepts a JSON formatted meta_fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: JSON.stringify(['meta']),
})
.expect(200));
it('accepts meta_fields query param in string array', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: ['_id', 'meta'],
})
.expect(200));
it('accepts single meta_fields query param', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: ['_id'],
})
.expect(200));
it('rejects a comma-separated list of meta_fields', () =>
supertest
.get('/api/index_patterns/_fields_for_wildcard')
.query({
pattern: '*',
meta_fields: 'foo,bar',
})
.expect(400));
});
});
}