Merge branch 'index-pattern/allow-no-index' into epm/missing-index-patterns

This commit is contained in:
Jen Huang 2021-03-11 17:31:44 -08:00
commit 8e712e9c00
6 changed files with 112 additions and 34 deletions

File diff suppressed because one or more lines are too long

View file

@ -17,7 +17,7 @@ import { stubbedSavedObjectIndexPattern } from './fixtures/stubbed_saved_object_
import { IndexPatternField } from '../fields';
import { fieldFormatsMock } from '../../field_formats/mocks';
import { FieldFormat } from '../..';
import { FieldFormat, IndexPatternSpec } from '../..';
import { RuntimeField } from '../types';
class MockFieldFormatter {}
@ -42,7 +42,7 @@ const runtimeField = {
fieldFormatsMock.getInstance = jest.fn().mockImplementation(() => new MockFieldFormatter()) as any;
// helper function to create index patterns
function create(id: string) {
function create(id: string, spec?: Partial<IndexPatternSpec>) {
const {
type,
version,
@ -58,6 +58,7 @@ function create(id: string) {
fields: { ...fields, runtime_field: runtimeField },
title,
runtimeFieldMap,
...(spec || {}),
},
fieldFormats: fieldFormatsMock,
shortDotsEnable: false,
@ -314,4 +315,15 @@ describe('IndexPattern', () => {
expect(restoredPattern.fields.length).toEqual(indexPattern.fields.length);
});
});
describe('getAsSavedObjectBody', () => {
test('should match snapshot', () => {
expect(indexPattern.getAsSavedObjectBody()).toMatchSnapshot();
});
test('allowNoIndex perserves all fields', () => {
const allowNoIndexIndexPattern = create('test-no-index-pattern', { allowNoIndex: true });
expect(allowNoIndexIndexPattern.getAsSavedObjectBody()).toMatchSnapshot();
});
});
});

View file

@ -322,7 +322,9 @@ export class IndexPattern implements IIndexPattern {
intervalName: this.intervalName,
sourceFilters: this.sourceFilters ? JSON.stringify(this.sourceFilters) : undefined,
fields: this.fields
? JSON.stringify(this.fields.filter((field) => field.scripted))
? JSON.stringify(
this.allowNoIndex ? this.fields : this.fields.filter((field) => field.scripted)
)
: undefined,
fieldFormatMap,
type: this.type,

View file

@ -283,25 +283,11 @@ export class IndexPatternsService {
fields: IndexPatternFieldMap,
id: string,
title: string,
options: GetFieldsOptions,
fieldAttrs: FieldAttrs = {}
) => {
const fieldsAsArr = Object.values(fields);
const scriptedFields = fieldsAsArr.filter((field) => field.scripted);
try {
let updatedFieldList: FieldSpec[];
const newFields = (await this.getFieldsForWildcard(options)) as FieldSpec[];
newFields.forEach((field) => (field.isMapped = true));
// If allowNoIndex, only update field list if field caps finds fields. To support
// beats creating index pattern and dashboard before docs
if (!options.allowNoIndex || (newFields && newFields.length > 5)) {
updatedFieldList = [...newFields, ...scriptedFields];
} else {
updatedFieldList = fieldsAsArr;
}
return this.fieldArrayToMap(updatedFieldList, fieldAttrs);
return this.fieldArrayToMap(fieldsAsArr, fieldAttrs);
} catch (err) {
if (err instanceof IndexPatternMissingIndices) {
this.onNotification({ title: (err as any).message, color: 'danger', iconType: 'alert' });
@ -396,7 +382,7 @@ export class IndexPatternsService {
}
const spec = this.savedObjectToSpec(savedObject);
const { title, type, typeMeta, runtimeFieldMap } = spec;
const { title, runtimeFieldMap } = spec;
spec.fieldAttrs = savedObject.attributes.fieldAttrs
? JSON.parse(savedObject.attributes.fieldAttrs)
: {};
@ -406,13 +392,6 @@ export class IndexPatternsService {
spec.fields || {},
id,
spec.title as string,
{
pattern: title as string,
metaFields: await this.config.get(UI_SETTINGS.META_FIELDS),
type,
rollupIndex: typeMeta?.params?.rollup_index,
allowNoIndex: spec.allowNoIndex,
},
spec.fieldAttrs
);
// CREATE RUNTIME FIELDS

View file

@ -9,6 +9,8 @@
import { ElasticsearchClient } from 'kibana/server';
import { keyBy } from 'lodash';
import { IndexPatternFieldMap } from '../../../common/index_patterns/types';
import { IndexPatternsCommonService } from '../..';
import {
getFieldCapabilities,
resolveTimePattern,
@ -35,10 +37,16 @@ interface FieldSubType {
export class IndexPatternsFetcher {
private elasticsearchClient: ElasticsearchClient;
private allowNoIndices: boolean;
private indexPatternService: IndexPatternsCommonService | undefined;
constructor(elasticsearchClient: ElasticsearchClient, allowNoIndices: boolean = false) {
constructor(
elasticsearchClient: ElasticsearchClient,
allowNoIndices: boolean = false,
indexPatternService?: IndexPatternsCommonService
) {
this.elasticsearchClient = elasticsearchClient;
this.allowNoIndices = allowNoIndices;
this.indexPatternService = indexPatternService;
}
/**
@ -58,24 +66,25 @@ export class IndexPatternsFetcher {
rollupIndex?: string;
}): Promise<FieldDescriptor[]> {
const { pattern, metaFields, fieldCapsOptions, type, rollupIndex } = options;
const allowNoIndices = fieldCapsOptions
? fieldCapsOptions.allow_no_indices
: this.allowNoIndices;
const patternList = Array.isArray(pattern) ? pattern : pattern.split(',');
let patternListActive: string[] = patternList;
// if only one pattern, don't bother with validation. We let getFieldCapabilities fail if the single pattern is bad regardless
if (patternList.length > 1) {
patternListActive = await this.validatePatternListActive(patternList);
}
const fieldCapsResponse = await getFieldCapabilities(
this.elasticsearchClient,
// if none of the patterns are active, pass the original list to get an error
patternListActive.length > 0 ? patternListActive : patternList,
metaFields,
{
allow_no_indices: fieldCapsOptions
? fieldCapsOptions.allow_no_indices
: this.allowNoIndices,
}
{ allow_no_indices: allowNoIndices }
);
// Get rollup information for rollup indices
if (type === 'rollup' && rollupIndex) {
const rollupFields: FieldDescriptor[] = [];
const rollupIndexCapabilities = getCapabilitiesForRollupIndices(
@ -99,6 +108,40 @@ export class IndexPatternsFetcher {
rollupFields
);
}
// If we allow no indices, fetch fields defined on the saved index pattern(s) and merge with file caps
// For now, this is mutually exclusive from the rollup handling for simplicity
else if (allowNoIndices && this.indexPatternService) {
const savedIndexPatterns = await this.indexPatternService.find(patternList.join(','));
const savedIndexPatternsFields = savedIndexPatterns.reduce((acc, indexPattern) => {
return {
...acc,
...indexPattern.fields.toSpec(),
};
}, {} as IndexPatternFieldMap);
return [
...Object.values(savedIndexPatternsFields).map(
({
name,
type: fieldType,
esTypes = [],
searchable,
aggregatable,
readFromDocValues = false,
}) => {
return {
name,
type: fieldType,
esTypes,
searchable,
aggregatable,
readFromDocValues,
};
}
),
...fieldCapsResponse,
];
}
return fieldCapsResponse;
}

View file

@ -69,8 +69,13 @@ export function registerRoutes(
},
},
async (context, request, response) => {
const { asCurrentUser } = context.core.elasticsearch.client;
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
const savedObjectsClient = context.core.savedObjects.client;
const elasticsearchClient = context.core.elasticsearch.client.asCurrentUser;
const [, , pluginStart] = await getStartServices();
const indexPatternsService = await pluginStart.indexPatterns.indexPatternsServiceFactory(
savedObjectsClient,
elasticsearchClient
);
const {
pattern,
meta_fields: metaFields,
@ -78,6 +83,11 @@ export function registerRoutes(
rollup_index: rollupIndex,
allow_no_index: allowNoIndex,
} = request.query;
const indexPatterns = new IndexPatternsFetcher(
elasticsearchClient,
allowNoIndex,
indexPatternsService
);
let parsedFields: string[] = [];
try {