Skip to content

Commit

Permalink
Merge branch '7.x' into backport/7.x/pr-90632
Browse files Browse the repository at this point in the history
  • Loading branch information
kibanamachine authored Feb 15, 2021
2 parents baf0e99 + 25e1bf5 commit b31079e
Show file tree
Hide file tree
Showing 140 changed files with 4,200 additions and 2,019 deletions.
6 changes: 3 additions & 3 deletions docs/user/dashboard/url-drilldown.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ internal {kib} navigations with carrying over current filters.
| Current query string.

|
| context.panel.query.lang
| context.panel.query.language
| Current query language.

|
Expand All @@ -200,8 +200,8 @@ context.panel.timeRange.to
Tip: Use in combination with <<helpers, date>> helper to format date.

|
| context.panel.timeRange.indexPatternId +
context.panel.timeRange.indexPatternIds
| context.panel.indexPatternId +
context.panel.indexPatternIds
|Index pattern ids used by a panel.

|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ test('parses fully specified config', () => {
"apiVersion": "v7.0.0",
"hosts": Array [
Object {
"auth": "elastic:changeme",
"headers": Object {
"x-elastic-product-origin": "kibana",
"xsrf": "something",
Expand All @@ -111,7 +110,6 @@ test('parses fully specified config', () => {
"query": null,
},
Object {
"auth": "elastic:changeme",
"headers": Object {
"x-elastic-product-origin": "kibana",
"xsrf": "something",
Expand All @@ -123,7 +121,6 @@ test('parses fully specified config', () => {
"query": null,
},
Object {
"auth": "elastic:changeme",
"headers": Object {
"x-elastic-product-origin": "kibana",
"xsrf": "something",
Expand All @@ -135,6 +132,7 @@ test('parses fully specified config', () => {
"query": null,
},
],
"httpAuth": "elastic:changeme",
"keepAlive": true,
"log": [Function],
"pingTimeout": 12345,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,14 @@ export function parseElasticsearchClientConfig(
esClientConfig.sniffInterval = getDurationAsMs(config.sniffInterval);
}

const needsAuth = auth !== false && config.username && config.password;
if (needsAuth) {
esClientConfig.httpAuth = `${config.username}:${config.password}`;
}

if (Array.isArray(config.hosts)) {
const needsAuth = auth !== false && config.username && config.password;
esClientConfig.hosts = config.hosts.map((nodeUrl: string) => {
const uri = url.parse(nodeUrl);

const httpsURI = uri.protocol === 'https:';
const httpURI = uri.protocol === 'http:';

Expand All @@ -126,10 +129,6 @@ export function parseElasticsearchClientConfig(
},
};

if (needsAuth) {
host.auth = `${config.username}:${config.password}`;
}

return host;
});
}
Expand Down
1 change: 1 addition & 0 deletions src/dev/storybook/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const storybookAliases = {
apm: 'x-pack/plugins/apm/.storybook',
canvas: 'x-pack/plugins/canvas/storybook',
codeeditor: 'src/plugins/kibana_react/public/code_editor/.storybook',
url_template_editor: 'src/plugins/kibana_react/public/url_template_editor/.storybook',
dashboard: 'src/plugins/dashboard/.storybook',
dashboard_enhanced: 'x-pack/plugins/dashboard_enhanced/.storybook',
data_enhanced: 'x-pack/plugins/data_enhanced/.storybook',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const mockSource2 = { excludes: ['bar-*'] };

const indexPattern = ({
title: 'foo',
fields: [{ name: 'foo-bar' }, { name: 'field1' }, { name: 'field2' }],
getComputedFields,
getSourceFiltering: () => mockSource,
} as unknown) as IndexPattern;
Expand All @@ -51,6 +52,11 @@ describe('SearchSource', () => {
let searchSource: SearchSource;

beforeEach(() => {
const getConfigMock = jest
.fn()
.mockImplementation((param) => param === 'metaFields' && ['_type', '_source'])
.mockName('getConfig');

mockSearchMethod = jest
.fn()
.mockReturnValue(
Expand All @@ -61,7 +67,7 @@ describe('SearchSource', () => {
);

searchSourceDependencies = {
getConfig: jest.fn(),
getConfig: getConfigMock,
search: mockSearchMethod,
onResponse: (req, res) => res,
legacy: {
Expand Down Expand Up @@ -518,6 +524,54 @@ describe('SearchSource', () => {
expect(request.script_fields).toEqual({ hello: {} });
});

test('request all fields except the ones specified with source filters', async () => {
searchSource.setField('index', ({
...indexPattern,
getComputedFields: () => ({
storedFields: [],
scriptFields: [],
docvalueFields: [],
}),
} as unknown) as IndexPattern);
searchSource.setField('fields', ['hello', 'foo']);

const request = await searchSource.getSearchRequestBody();
expect(request.fields).toEqual(['hello']);
});

test('request all fields from index pattern except the ones specified with source filters', async () => {
searchSource.setField('index', ({
...indexPattern,
getComputedFields: () => ({
storedFields: [],
scriptFields: [],
docvalueFields: [],
}),
} as unknown) as IndexPattern);
searchSource.setField('fields', ['*']);

const request = await searchSource.getSearchRequestBody();
expect(request.fields).toEqual([{ field: 'field1' }, { field: 'field2' }]);
});

test('request all fields from index pattern except the ones specified with source filters with unmapped_fields option', async () => {
searchSource.setField('index', ({
...indexPattern,
getComputedFields: () => ({
storedFields: [],
scriptFields: [],
docvalueFields: [],
}),
} as unknown) as IndexPattern);
searchSource.setField('fields', [{ field: '*', include_unmapped: 'true' }]);

const request = await searchSource.getSearchRequestBody();
expect(request.fields).toEqual([
{ field: 'field1', include_unmapped: 'true' },
{ field: 'field2', include_unmapped: 'true' },
]);
});

test('returns all scripted fields when one fields entry is *', async () => {
searchSource.setField('index', ({
...indexPattern,
Expand Down Expand Up @@ -836,5 +890,25 @@ describe('SearchSource', () => {
expect(references[1].type).toEqual('index-pattern');
expect(JSON.parse(searchSourceJSON).filter[0].meta.indexRefName).toEqual(references[1].name);
});

test('mvt geoshape layer test', async () => {
// @ts-expect-error TS won't like using this field name, but technically it's possible.
searchSource.setField('docvalue_fields', ['prop1']);
searchSource.setField('source', ['geometry']);
searchSource.setField('fieldsFromSource', ['geometry', 'prop1']);
searchSource.setField('index', ({
...indexPattern,
getSourceFiltering: () => ({ excludes: [] }),
getComputedFields: () => ({
storedFields: ['*'],
scriptFields: {},
docvalueFields: [],
}),
} as unknown) as IndexPattern);
const request = await searchSource.getSearchRequestBody();
expect(request.stored_fields).toEqual(['geometry', 'prop1']);
expect(request.docvalue_fields).toEqual(['prop1']);
expect(request._source).toEqual(['geometry']);
});
});
});
132 changes: 87 additions & 45 deletions src/plugins/data/common/search/search_source/search_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@
*/

import { setWith } from '@elastic/safer-lodash-set';
import { uniqueId, keyBy, pick, difference, omit, isObject, isFunction } from 'lodash';
import { uniqueId, keyBy, pick, difference, omit, isFunction, isEqual } from 'lodash';
import { map, switchMap, tap } from 'rxjs/operators';
import { defer, from } from 'rxjs';
import { isObject } from 'rxjs/internal-compatibility';
import { normalizeSortRequest } from './normalize_sort_request';
import { fieldWildcardFilter } from '../../../../kibana_utils/common';
import { IIndexPattern } from '../../index_patterns';
import { IIndexPattern, IndexPattern, IndexPatternField } from '../../index_patterns';
import { ISearchGeneric, ISearchOptions } from '../..';
import type {
ISearchSource,
Expand Down Expand Up @@ -500,10 +501,53 @@ export class SearchSource {
}
}

private readonly getFieldName = (fld: string | Record<string, any>): string =>
typeof fld === 'string' ? fld : fld.field;

private getFieldsWithoutSourceFilters(
index: IndexPattern | undefined,
bodyFields: SearchFieldValue[]
) {
if (!index) {
return bodyFields;
}
const { fields } = index;
const sourceFilters = index.getSourceFiltering();
if (!sourceFilters || sourceFilters.excludes?.length === 0 || bodyFields.length === 0) {
return bodyFields;
}
const metaFields = this.dependencies.getConfig(UI_SETTINGS.META_FIELDS);
const sourceFiltersValues = sourceFilters.excludes;
const wildcardField = bodyFields.find(
(el: SearchFieldValue) => el === '*' || (el as Record<string, string>).field === '*'
);
const filterSourceFields = (fieldName: string) => {
return (
fieldName &&
!sourceFiltersValues.some((sourceFilter) => fieldName.match(sourceFilter)) &&
!metaFields.includes(fieldName)
);
};
if (!wildcardField) {
// we already have an explicit list of fields, so we just remove source filters from that list
return bodyFields.filter((fld: SearchFieldValue) =>
filterSourceFields(this.getFieldName(fld))
);
}
// we need to get the list of fields from an index pattern
return fields
.filter((fld: IndexPatternField) => filterSourceFields(fld.name))
.map((fld: IndexPatternField) => ({
field: fld.name,
...((wildcardField as Record<string, string>)?.include_unmapped && {
include_unmapped: (wildcardField as Record<string, string>).include_unmapped,
}),
}));
}

private flatten() {
const { getConfig } = this.dependencies;
const searchRequest = this.mergeProps();

searchRequest.body = searchRequest.body || {};
const { body, index, query, filters, highlightAll } = searchRequest;
searchRequest.indexType = this.getIndexType(index);
Expand All @@ -517,10 +561,7 @@ export class SearchSource {
storedFields: ['*'],
runtimeFields: {},
};

const fieldListProvided = !!body.fields;
const getFieldName = (fld: string | Record<string, any>): string =>
typeof fld === 'string' ? fld : fld.field;

// set defaults
let fieldsFromSource = searchRequest.fieldsFromSource || [];
Expand All @@ -539,26 +580,22 @@ export class SearchSource {
if (!body.hasOwnProperty('_source')) {
body._source = sourceFilters;
}
if (body._source.excludes) {
const filter = fieldWildcardFilter(
body._source.excludes,
getConfig(UI_SETTINGS.META_FIELDS)
);
// also apply filters to provided fields & default docvalueFields
body.fields = body.fields.filter((fld: SearchFieldValue) => filter(getFieldName(fld)));
fieldsFromSource = fieldsFromSource.filter((fld: SearchFieldValue) =>
filter(getFieldName(fld))
);
filteredDocvalueFields = filteredDocvalueFields.filter((fld: SearchFieldValue) =>
filter(getFieldName(fld))
);
}

const filter = fieldWildcardFilter(body._source.excludes, getConfig(UI_SETTINGS.META_FIELDS));
// also apply filters to provided fields & default docvalueFields
body.fields = body.fields.filter((fld: SearchFieldValue) => filter(this.getFieldName(fld)));
fieldsFromSource = fieldsFromSource.filter((fld: SearchFieldValue) =>
filter(this.getFieldName(fld))
);
filteredDocvalueFields = filteredDocvalueFields.filter((fld: SearchFieldValue) =>
filter(this.getFieldName(fld))
);
}

// specific fields were provided, so we need to exclude any others
if (fieldListProvided || fieldsFromSource.length) {
const bodyFieldNames = body.fields.map((field: string | Record<string, any>) =>
getFieldName(field)
this.getFieldName(field)
);
const uniqFieldNames = [...new Set([...bodyFieldNames, ...fieldsFromSource])];

Expand All @@ -579,28 +616,31 @@ export class SearchSource {
const remainingFields = difference(uniqFieldNames, [
...Object.keys(body.script_fields),
...Object.keys(body.runtime_mappings),
]).filter(Boolean);
]).filter((remainingField) => {
if (!remainingField) return false;
if (!body._source || !body._source.excludes) return true;
return !body._source.excludes.includes(remainingField);
});

// only include unique values
body.stored_fields = [...new Set(remainingFields)];

// only include unique values
if (fieldsFromSource.length) {
// include remaining fields in _source
setWith(body, '_source.includes', remainingFields, (nsValue) =>
isObject(nsValue) ? {} : nsValue
);

if (!isEqual(remainingFields, fieldsFromSource)) {
setWith(body, '_source.includes', remainingFields, (nsValue) =>
isObject(nsValue) ? {} : nsValue
);
}
// if items that are in the docvalueFields are provided, we should
// make sure those are added to the fields API unless they are
// already set in docvalue_fields
body.fields = [
...body.fields,
...filteredDocvalueFields.filter((fld: SearchFieldValue) => {
return (
fieldsFromSource.includes(getFieldName(fld)) &&
fieldsFromSource.includes(this.getFieldName(fld)) &&
!(body.docvalue_fields || [])
.map((d: string | Record<string, any>) => getFieldName(d))
.includes(getFieldName(fld))
.map((d: string | Record<string, any>) => this.getFieldName(d))
.includes(this.getFieldName(fld))
);
}),
];
Expand All @@ -614,20 +654,22 @@ export class SearchSource {
// if items that are in the docvalueFields are provided, we should
// inject the format from the computed fields if one isn't given
const docvaluesIndex = keyBy(filteredDocvalueFields, 'field');
body.fields = body.fields.map((fld: SearchFieldValue) => {
const fieldName = getFieldName(fld);
if (Object.keys(docvaluesIndex).includes(fieldName)) {
// either provide the field object from computed docvalues,
// or merge the user-provided field with the one in docvalues
return typeof fld === 'string'
? docvaluesIndex[fld]
: {
...docvaluesIndex[fieldName],
...fld,
};
body.fields = this.getFieldsWithoutSourceFilters(index, body.fields).map(
(fld: SearchFieldValue) => {
const fieldName = this.getFieldName(fld);
if (Object.keys(docvaluesIndex).includes(fieldName)) {
// either provide the field object from computed docvalues,
// or merge the user-provided field with the one in docvalues
return typeof fld === 'string'
? docvaluesIndex[fld]
: {
...docvaluesIndex[fieldName],
...fld,
};
}
return fld;
}
return fld;
});
);
}
} else {
body.fields = filteredDocvalueFields;
Expand Down
Loading

0 comments on commit b31079e

Please sign in to comment.