Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partially convert index pattern server to typescript #43291

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@
* under the License.
*/

export { IndexPatternsService } from './service';
// @ts-ignore no types
export { indexPatternsMixin } from './mixin';
export { IndexPatternsService, FieldDescriptor } from './service';
export { IndexPatternsServiceFactory } from './mixin';
20 changes: 0 additions & 20 deletions src/legacy/server/index_patterns/service/index.d.ts

This file was deleted.

20 changes: 0 additions & 20 deletions src/legacy/server/index_patterns/service/index.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
* under the License.
*/

export { indexPatternsMixin } from './mixin';
export * from './index_patterns_service';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,41 @@
* under the License.
*/

import {
getFieldCapabilities,
resolveTimePattern,
createNoMatchingIndicesError,
} from './lib';
import { APICaller } from 'src/core/server';

import { getFieldCapabilities, resolveTimePattern, createNoMatchingIndicesError } from './lib';

export interface FieldDescriptor {
aggregatable: boolean;
name: string;
readFromDocValues: boolean;
searchable: boolean;
type: string;
esTypes: string[];
parent?: string;
subType?: string;
}

export class IndexPatternsService {
constructor(callDataCluster) {
private _callDataCluster: APICaller;

constructor(callDataCluster: APICaller) {
this._callDataCluster = callDataCluster;
}

/**
* Get a list of field objects for an index pattern that may contain wildcards
*
* @param {Object} [options={}]
* @param {Object} [options]
* @property {String} options.pattern The index pattern
* @property {Number} options.metaFields The list of underscore prefixed fields that should
* be left in the field list (all others are removed).
* @return {Promise<Array<Fields>>}
*/
async getFieldsForWildcard(options = {}) {
async getFieldsForWildcard(options: {
pattern: string | string[];
metaFields?: string[];
}): Promise<FieldDescriptor[]> {
const { pattern, metaFields } = options;
return await getFieldCapabilities(this._callDataCluster, pattern, metaFields);
}
Expand All @@ -52,7 +66,12 @@ export class IndexPatternsService {
* be left in the field list (all others are removed).
* @return {Promise<Array<Fields>>}
*/
async getFieldsForTimePattern(options = {}) {
async getFieldsForTimePattern(options: {
pattern: string;
metaFields: string[];
lookBack: number;
interval: string;
}) {
const { pattern, lookBack, metaFields } = options;
const { matches } = await resolveTimePattern(this._callDataCluster, pattern);
const indices = matches.slice(0, lookBack);
Expand All @@ -61,5 +80,4 @@ export class IndexPatternsService {
}
return await getFieldCapabilities(this._callDataCluster, indices, metaFields);
}

}
23 changes: 0 additions & 23 deletions src/legacy/server/index_patterns/service/lib/errors.test.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
* under the License.
*/

import { APICaller } from 'src/core/server';
// @ts-ignore
import { convertEsError } from './errors';
import { FieldCapsResponse } from './field_capabilities';

/**
* Call the index.getAlias API for a list of indices.
Expand All @@ -33,12 +36,12 @@ import { convertEsError } from './errors';
* @param {Array<String>|String} indices
* @return {Promise<IndexAliasResponse>}
*/
export async function callIndexAliasApi(callCluster, indices) {
export async function callIndexAliasApi(callCluster: APICaller, indices: string[] | string) {
try {
return await callCluster('indices.getAlias', {
index: indices,
ignoreUnavailable: true,
allowNoIndices: false
allowNoIndices: false,
});
} catch (error) {
throw convertEsError(indices, error);
Expand All @@ -56,14 +59,14 @@ export async function callIndexAliasApi(callCluster, indices) {
* @param {Array<String>|String} indices
* @return {Promise<FieldCapsResponse>}
*/
export async function callFieldCapsApi(callCluster, indices) {
export async function callFieldCapsApi(callCluster: APICaller, indices: string[] | string) {
try {
return await callCluster('fieldCaps', {
return (await callCluster('fieldCaps', {
index: indices,
fields: '*',
ignoreUnavailable: true,
allowNoIndices: false
});
allowNoIndices: false,
})) as FieldCapsResponse;
} catch (error) {
throw convertEsError(indices, error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@

import { defaults, indexBy, sortBy } from 'lodash';

import { APICaller } from 'src/core/server';
import { callFieldCapsApi } from '../es_api';
import { readFieldCapsResponse } from './field_caps_response';
import { FieldCapsResponse, readFieldCapsResponse } from './field_caps_response';
import { mergeOverrides } from './overrides';
import { FieldDescriptor } from '../../index_patterns_service';

export const concatIfUniq = (arr, value) => (
arr.includes(value) ? arr : arr.concat(value)
);
export function concatIfUniq<T>(arr: T[], value: T) {
return arr.includes(value) ? arr : arr.concat(value);
}

/**
* Get the field capabilities for field in `indices`, excluding
Expand All @@ -34,24 +36,29 @@ export const concatIfUniq = (arr, value) => (
* @param {Function} callCluster bound function for accessing an es client
* @param {Array} [indices=[]] the list of indexes to check
* @param {Array} [metaFields=[]] the list of internal fields to include
* @return {Promise<Array<FieldInfo>>}
* @return {Promise<Array<FieldDescriptor>>}
*/
export async function getFieldCapabilities(callCluster, indices = [], metaFields = []) {
const esFieldCaps = await callFieldCapsApi(callCluster, indices);
export async function getFieldCapabilities(
callCluster: APICaller,
indices: string | string[] = [],
metaFields: string[] = []
) {
const esFieldCaps: FieldCapsResponse = await callFieldCapsApi(callCluster, indices);
const fieldsFromFieldCapsByName = indexBy(readFieldCapsResponse(esFieldCaps), 'name');

const allFieldsUnsorted = Object
.keys(fieldsFromFieldCapsByName)
const allFieldsUnsorted = Object.keys(fieldsFromFieldCapsByName)
.filter(name => !name.startsWith('_'))
.concat(metaFields)
.reduce(concatIfUniq, [])
.map(name => defaults({}, fieldsFromFieldCapsByName[name], {
name,
type: 'string',
searchable: false,
aggregatable: false,
readFromDocValues: false
}))
.reduce(concatIfUniq, [] as string[])
.map<FieldDescriptor>(name =>
defaults({}, fieldsFromFieldCapsByName[name], {
name,
type: 'string',
searchable: false,
aggregatable: false,
readFromDocValues: false,
})
)
.map(mergeOverrides);

return sortBy(allFieldsUnsorted, 'name');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@
import { uniq } from 'lodash';
import { castEsToKbnFieldTypeName } from '../../../../../utils';
import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values';
import { FieldDescriptor } from '../..';

interface FieldCapObject {
type: string;
searchable: boolean;
aggregatable: boolean;
indices?: string[];
non_searchable_indices?: string[];
non_aggregatable_indices?: string[];
}

export interface FieldCapsResponse {
fields: Record<string, Record<string, FieldCapObject>>;
}

/**
* Read the response from the _field_caps API to determine the type and
Expand Down Expand Up @@ -62,8 +76,7 @@ import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_value
* {
* "name": "<fieldName>",
* "type": "conflict",
* "aggregatable": false,
* "searchable": false,
* "aggregatable": false, * "searchable": false,
wylieconlon marked this conversation as resolved.
Show resolved Hide resolved
* conflictDescriptions: {
* "<esType1>": [
* "<index1>"
Expand All @@ -75,26 +88,31 @@ import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_value
* }
*
* @param {FieldCapsResponse} fieldCapsResponse
* @return {Promise<Array<FieldInfo>>}
lukeelmers marked this conversation as resolved.
Show resolved Hide resolved
* @return {Array<FieldDescriptor>}
*/
export function readFieldCapsResponse(fieldCapsResponse) {
export function readFieldCapsResponse(fieldCapsResponse: FieldCapsResponse): FieldDescriptor[] {
const capsByNameThenType = fieldCapsResponse.fields;
const kibanaFormattedCaps = Object.keys(capsByNameThenType).map(fieldName => {
const kibanaFormattedCaps: FieldDescriptor[] = Object.keys(capsByNameThenType).map(fieldName => {
const capsByType = capsByNameThenType[fieldName];
const types = Object.keys(capsByType);

// If a single type is marked as searchable or aggregatable, all the types are searchable or aggregatable
const isSearchable = types.some(type => {
return !!capsByType[type].searchable ||
(!!capsByType[type].non_searchable_indices && capsByType[type].non_searchable_indices.length > 0);
return (
!!capsByType[type].searchable ||
(!!capsByType[type].non_searchable_indices &&
capsByType[type].non_searchable_indices!.length > 0)
);
});

const isAggregatable = types.some(type => {
return !!capsByType[type].aggregatable ||
(!!capsByType[type].non_aggregatable_indices && capsByType[type].non_aggregatable_indices.length > 0);
return (
!!capsByType[type].aggregatable ||
(!!capsByType[type].non_aggregatable_indices &&
capsByType[type].non_aggregatable_indices!.length > 0)
);
});


// If there are multiple types but they all resolve to the same kibana type
// ignore the conflict and carry on (my wayward son)
const uniqueKibanaTypes = uniq(types.map(castEsToKbnFieldTypeName));
Expand All @@ -106,10 +124,13 @@ export function readFieldCapsResponse(fieldCapsResponse) {
searchable: isSearchable,
aggregatable: isAggregatable,
readFromDocValues: false,
conflictDescriptions: types.reduce((acc, esType) => ({
...acc,
[esType]: capsByType[esType].indices
}), {})
conflictDescriptions: types.reduce(
(acc, esType) => ({
...acc,
[esType]: capsByType[esType].indices,
}),
{}
),
};
}

Expand All @@ -132,7 +153,10 @@ export function readFieldCapsResponse(fieldCapsResponse) {
// Discern which sub fields are multi fields. If the parent field is not an object or nested field
// the child must be a multi field.
subFields.forEach(field => {
const parentFieldName = field.name.split('.').slice(0, -1).join('.');
const parentFieldName = field.name
.split('.')
.slice(0, -1)
.join('.');
const parentFieldCaps = kibanaFormattedCaps.find(caps => caps.name === parentFieldName);

if (parentFieldCaps && !['object', 'nested'].includes(parentFieldCaps.type)) {
Expand Down
Loading