From deead9c47e9df46424ea04fb1f68e965106edb30 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 10:20:56 -0700 Subject: [PATCH 01/26] fix some any --- .../data_view_field_editor/server/plugin.ts | 2 +- .../data_views/server/fetcher/lib/errors.ts | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/plugins/data_view_field_editor/server/plugin.ts b/src/plugins/data_view_field_editor/server/plugin.ts index c5bbe9bab7572e..deb516ab25012b 100644 --- a/src/plugins/data_view_field_editor/server/plugin.ts +++ b/src/plugins/data_view_field_editor/server/plugin.ts @@ -9,7 +9,7 @@ import { PluginInitializerContext, CoreSetup, Plugin, Logger } from '@kbn/core/s import { ApiRoutes } from './routes'; -export class IndexPatternPlugin implements Plugin { +export class IndexPatternPlugin implements Plugin { private readonly logger: Logger; private readonly apiRoutes: ApiRoutes; diff --git a/src/plugins/data_views/server/fetcher/lib/errors.ts b/src/plugins/data_views/server/fetcher/lib/errors.ts index c69e177535b76f..811b48f129138b 100644 --- a/src/plugins/data_views/server/fetcher/lib/errors.ts +++ b/src/plugins/data_views/server/fetcher/lib/errors.ts @@ -7,6 +7,7 @@ */ import Boom from '@hapi/boom'; +import { CustomHttpResponseOptions } from '@kbn/core/server'; import { get } from 'lodash'; const ERR_ES_INDEX_NOT_FOUND = 'index_not_found_exception'; @@ -48,18 +49,23 @@ export function isNoMatchingIndicesError(err: any) { * Wrap "index_not_found_exception" errors in custom Boom errors * automatically * @param {Array|String} indices + * @param {Boom.Boom|CustomHttpResponseOptions} error * @return {Boom} */ -export function convertEsError(indices: string[] | string, error: any) { +export function convertEsError(indices: string[] | string, error: unknown) { if (isEsIndexNotFoundError(error)) { return createNoMatchingIndicesError(indices); } - if (error.isBoom) { + if ((error as Boom.Boom).isBoom) { return error; } - const statusCode = error.statusCode; - const message = error.body ? error.body.error : undefined; - return Boom.boomify(error, { statusCode, message }); + const custom = error as CustomHttpResponseOptions<{ error: string; message: string }>; + const options = { + statusCode: custom.statusCode, + message: custom.body?.error ?? undefined, + }; + + return Boom.boomify(error as Error, options); } From 8ff1b1040815050d3c504b9baa8de4b529845ed7 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 11:07:56 -0700 Subject: [PATCH 02/26] remove some any --- .../server/fetcher/index_patterns_fetcher.ts | 36 ++++++++++++------- .../server/fetcher/lib/jobs_compatibility.ts | 21 ++++++----- .../server/fetcher/lib/map_capabilities.ts | 11 ++++-- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts index 9d3b8a180d91eb..962788097525ff 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts @@ -79,22 +79,32 @@ export class IndexPatternsFetcher { }); if (type === 'rollup' && rollupIndex) { const rollupFields: FieldDescriptor[] = []; - const rollupIndexCapabilities = getCapabilitiesForRollupIndices( + const capabilityCheck = getCapabilitiesForRollupIndices( await this.elasticsearchClient.rollup.getRollupIndexCaps({ index: rollupIndex, }) - )[rollupIndex].aggs; - const fieldCapsResponseObj = keyBy(fieldCapsResponse, 'name'); - // Keep meta fields - metaFields!.forEach( - (field: string) => - fieldCapsResponseObj[field] && rollupFields.push(fieldCapsResponseObj[field]) - ); - return mergeCapabilitiesWithFields( - rollupIndexCapabilities, - fieldCapsResponseObj, - rollupFields - ); + )[rollupIndex]; + + if (capabilityCheck.error) { + throw new Error(capabilityCheck.error); + } + + const rollupIndexCapabilities = capabilityCheck.aggs; + + if (rollupIndexCapabilities) { + const fieldCapsResponseObj = keyBy(fieldCapsResponse, 'name'); + // Keep meta fields + metaFields!.forEach( + (field: string) => + fieldCapsResponseObj[field] && rollupFields.push(fieldCapsResponseObj[field]) + ); + + return mergeCapabilitiesWithFields( + rollupIndexCapabilities, + fieldCapsResponseObj, + rollupFields + ); + } } return fieldCapsResponse; } diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index adc60774fae37e..814dccf0196226 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -6,7 +6,9 @@ * Side Public License, v 1. */ -import { isEqual } from 'lodash'; +import { RollupGetRollupIndexCapsRollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; +import { SerializableRecord } from '@kbn/utility-types'; +import { get, isEqual, set } from 'lodash'; /** * Checks if given job configs are compatible by attempting to merge them @@ -34,12 +36,12 @@ export function areJobsCompatible(jobs = []) { * @param jobs * @returns {{}} */ -export function mergeJobConfigurations(jobs = []) { +export function mergeJobConfigurations(jobs: RollupGetRollupIndexCapsRollupJobSummary[] = []) { if (!jobs || !Array.isArray(jobs) || !jobs.length) { throw new Error('No capabilities available'); } - const allAggs: { [key: string]: any } = {}; + const allAggs: { [key: string]: SerializableRecord } = {}; // For each job, look through all of its fields jobs.forEach((job: { fields: { [key: string]: any } }) => { @@ -66,16 +68,17 @@ export function mergeJobConfigurations(jobs = []) { } // If aggregation already exists, attempt to merge it else { - const fieldAgg = allAggs[aggName][fieldName]; - + const fieldAgg = allAggs[aggName][fieldName] as object | null; switch (aggName) { // For histograms, calculate the least common multiple between the // new interval and existing interval case 'histogram': - // TODO: Fix this with LCD algorithm - const intervals = [fieldAgg.interval, agg.interval].sort((a, b) => a - b); - const isMultiple = intervals[1] % intervals[0] === 0; - fieldAgg.interval = isMultiple ? intervals[1] : intervals[0] * intervals[1]; + if (fieldAgg) { + // TODO: Fix this with LCD algorithm + const intervals = [get(fieldAgg, 'interval'), agg.interval].sort((a, b) => a - b); + const isMultiple = intervals[1] % intervals[0] === 0; + set(fieldAgg, 'interval', isMultiple ? intervals[1] : intervals[0] * intervals[1]); + } break; // For date histograms, if it is on the same field, check that the configuration is identical, diff --git a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts index e2076a373d489a..38f8968c929ad6 100644 --- a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts @@ -6,17 +6,24 @@ * Side Public License, v 1. */ +import { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; import { mergeJobConfigurations } from './jobs_compatibility'; +export interface RollupIndexCapability { + [key: string]: { aggs?: object; error?: string }; +} + /** * Get rollup job capabilities * @public * @param indices rollup job index capabilites */ -export function getCapabilitiesForRollupIndices(indices: Record) { +export function getCapabilitiesForRollupIndices( + indices: RollupGetRollupIndexCapsResponse +): RollupIndexCapability { const indexNames = Object.keys(indices); - const capabilities = {} as { [key: string]: any }; + const capabilities: RollupIndexCapability = {}; indexNames.forEach((index) => { try { From f1eed724bca22b837f67cc84fd1b4ecbeb05bb5e Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 11:24:13 -0700 Subject: [PATCH 03/26] fix some any in saved_objects_client_wrapper --- src/plugins/data_views/common/types.ts | 8 ++++---- .../server/saved_objects_client_wrapper.ts | 15 +++++++-------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 11b0982446bfec..13ccc6a71d9573 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -281,8 +281,8 @@ export interface SavedObjectsClientCommon { update: ( type: string, id: string, - attributes: Record, - options: Record + attributes: DataViewAttributes, + options: { version?: string } ) => Promise; /** * Create a saved object @@ -292,8 +292,8 @@ export interface SavedObjectsClientCommon { */ create: ( type: string, - attributes: Record, - options: Record + attributes: DataViewAttributes, + options: { id?: string } ) => Promise; /** * Delete a saved object by id diff --git a/src/plugins/data_views/server/saved_objects_client_wrapper.ts b/src/plugins/data_views/server/saved_objects_client_wrapper.ts index d8755b9ff1be16..9ac5e5203a10e8 100644 --- a/src/plugins/data_views/server/saved_objects_client_wrapper.ts +++ b/src/plugins/data_views/server/saved_objects_client_wrapper.ts @@ -7,7 +7,11 @@ */ import { SavedObjectsClientContract, SavedObject } from '@kbn/core/server'; -import { SavedObjectsClientCommon, SavedObjectsClientCommonFindArgs } from '../common/types'; +import { + DataViewAttributes, + SavedObjectsClientCommon, + SavedObjectsClientCommonFindArgs, +} from '../common/types'; import { DataViewSavedObjectConflictError } from '../common/errors'; export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommon { @@ -27,15 +31,10 @@ export class SavedObjectsClientServerToCommon implements SavedObjectsClientCommo } return response.saved_object; } - async update( - type: string, - id: string, - attributes: Record, - options: Record - ) { + async update(type: string, id: string, attributes: DataViewAttributes, options: {}) { return (await this.savedObjectClient.update(type, id, attributes, options)) as SavedObject; } - async create(type: string, attributes: Record, options: Record) { + async create(type: string, attributes: DataViewAttributes, options: {}) { return await this.savedObjectClient.create(type, attributes, options); } delete(type: string, id: string) { From ad9b69dc8a6f8f69c7bb22c7c82c8ce664efb232 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 11:24:51 -0700 Subject: [PATCH 04/26] fix some any in common/types --- src/plugins/data_views/common/data_views/data_views.ts | 8 ++++---- src/plugins/data_views/common/types.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index a92f18029bd4a8..887579b6d44a36 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -435,7 +435,7 @@ export class DataViewsService { * Get default index pattern id */ getDefaultId = async (): Promise => { - const defaultIndexPatternId = await this.config.get('defaultIndex'); + const defaultIndexPatternId = await this.config.get('defaultIndex'); return defaultIndexPatternId ?? null; }; @@ -463,7 +463,7 @@ export class DataViewsService { * @returns FieldSpec[] */ getFieldsForWildcard = async (options: GetFieldsOptions): Promise => { - const metaFields = await this.config.get(META_FIELDS); + const metaFields = await this.config.get(META_FIELDS); return this.apiClient.getFieldsForWildcard({ pattern: options.pattern, metaFields, @@ -773,8 +773,8 @@ export class DataViewsService { * @returns DataView */ async create({ id, ...restOfSpec }: DataViewSpec, skipFetchFields = false): Promise { - const shortDotsEnable = await this.config.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE); - const metaFields = await this.config.get(META_FIELDS); + const shortDotsEnable = await this.config.get(FORMATS_UI_SETTINGS.SHORT_DOTS_ENABLE); + const metaFields = await this.config.get(META_FIELDS); const spec = { id: id ?? uuid.v4(), diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 13ccc6a71d9573..cfd7aa09c88420 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -210,7 +210,7 @@ export interface UiSettingsCommon { * Get a setting value * @param key name of value */ - get: (key: string) => Promise; + get: (key: string) => Promise; /** * Get all settings values */ @@ -220,7 +220,7 @@ export interface UiSettingsCommon { * @param key name of value * @param value value to set */ - set: (key: string, value: T) => Promise; + set: (key: string, value: T) => Promise; /** * Remove a setting value * @param key name of value From 700bdea220a07235db4e2e63dce58ca416896665 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 11:40:50 -0700 Subject: [PATCH 05/26] remove some any from flattenHit and getFields* --- .../index_pattern_table/delete_modal_msg.tsx | 2 +- .../common/data_views/flatten_hit.ts | 20 ++++++++++++++----- src/plugins/data_views/common/types.ts | 2 +- .../data_views/public/ui_settings_wrapper.ts | 8 +++++--- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/plugins/data_view_management/public/components/index_pattern_table/delete_modal_msg.tsx b/src/plugins/data_view_management/public/components/index_pattern_table/delete_modal_msg.tsx index e675f4be59b454..080b3b8bd8ea04 100644 --- a/src/plugins/data_view_management/public/components/index_pattern_table/delete_modal_msg.tsx +++ b/src/plugins/data_view_management/public/components/index_pattern_table/delete_modal_msg.tsx @@ -32,7 +32,7 @@ const tableTitle = i18n.translate('indexPatternManagement.dataViewTable.tableTit }); export const deleteModalMsg = (views: RemoveDataViewProps[], hasSpaces: boolean) => { - const columns: Array> = [ + const columns: Array> = [ { field: 'name', name: dataViewColumnName, diff --git a/src/plugins/data_views/common/data_views/flatten_hit.ts b/src/plugins/data_views/common/data_views/flatten_hit.ts index 0a6388f0914b15..0d0311a4285977 100644 --- a/src/plugins/data_views/common/data_views/flatten_hit.ts +++ b/src/plugins/data_views/common/data_views/flatten_hit.ts @@ -14,11 +14,21 @@ import _ from 'lodash'; import { DataView } from './data_view'; -// Takes a hit, merges it with any stored/scripted fields, and with the metaFields -// returns a flattened version - -function flattenHit(indexPattern: DataView, hit: Record, deep: boolean) { - const flat = {} as Record; +/** + * Takes a hit, merges it with whatever stored/scripted fields, and with the metaFields + * returns a flattened version + * + * @param {DataView} indexPattern + * @param {Record} hit - userland data + * @param {boolean} deep - whether to look into objects within arrays + * @returns {Record} + */ +function flattenHit( + indexPattern: DataView, + hit: Record, + deep: boolean +): Record { + const flat = {} as Record; // recursively merge _source const fields = indexPattern.fields.getByName; diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index cfd7aa09c88420..8a5c310621a689 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -314,7 +314,7 @@ export interface GetFieldsOptions { } export interface IDataViewsApiClient { - getFieldsForWildcard: (options: GetFieldsOptions) => Promise; + getFieldsForWildcard: (options: GetFieldsOptions) => Promise; hasUserIndexPattern: () => Promise; } diff --git a/src/plugins/data_views/public/ui_settings_wrapper.ts b/src/plugins/data_views/public/ui_settings_wrapper.ts index 3474451ad720f8..ad160ee39373ef 100644 --- a/src/plugins/data_views/public/ui_settings_wrapper.ts +++ b/src/plugins/data_views/public/ui_settings_wrapper.ts @@ -14,15 +14,17 @@ export class UiSettingsPublicToCommon implements UiSettingsCommon { constructor(uiSettings: IUiSettingsClient) { this.uiSettings = uiSettings; } - get(key: string): Promise { + get(key: string): Promise { return Promise.resolve(this.uiSettings.get(key)); } - getAll(): Promise>> { + getAll(): Promise< + Record) | undefined> + > { return Promise.resolve(this.uiSettings.getAll()); } - set(key: string, value: any) { + set(key: string, value: unknown) { this.uiSettings.set(key, value); return Promise.resolve(); } From b3a071a86c53c4265ec73718cd523aa8eb25838e Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 12:35:07 -0700 Subject: [PATCH 06/26] fix more any --- .../data_view_field_editor/public/types.ts | 2 +- .../edit_index_pattern/edit_index_pattern.tsx | 4 +- .../data_views/common/data_views/data_view.ts | 38 ++++--- .../common/data_views/flatten_hit.test.ts | 2 +- .../common/data_views/flatten_hit.ts | 11 +- src/plugins/data_views/common/fields/utils.ts | 6 +- src/plugins/data_views/common/types.ts | 23 ++-- .../data_views/data_views_api_client.ts | 28 +++-- .../public/saved_objects_client_wrapper.ts | 20 ++-- .../data_views/server/fetcher/lib/errors.ts | 8 +- .../server/fetcher/lib/jobs_compatibility.ts | 106 ++++++++++-------- .../lib/merge_capabilities_with_fields.ts | 2 +- .../data_views/server/ui_settings_wrapper.ts | 6 +- 13 files changed, 144 insertions(+), 112 deletions(-) diff --git a/src/plugins/data_view_field_editor/public/types.ts b/src/plugins/data_view_field_editor/public/types.ts index 25f97e6737bf21..dbc92b9d7aea21 100644 --- a/src/plugins/data_view_field_editor/public/types.ts +++ b/src/plugins/data_view_field_editor/public/types.ts @@ -58,7 +58,7 @@ export interface Field { export interface FieldFormatConfig { id: string; - params?: { [key: string]: any }; + params?: { [key: string]: unknown }; } export interface EsRuntimeField { diff --git a/src/plugins/data_view_management/public/components/edit_index_pattern/edit_index_pattern.tsx b/src/plugins/data_view_management/public/components/edit_index_pattern/edit_index_pattern.tsx index d502038a1018d4..74e1eb77cf5035 100644 --- a/src/plugins/data_view_management/public/components/edit_index_pattern/edit_index_pattern.tsx +++ b/src/plugins/data_view_management/public/components/edit_index_pattern/edit_index_pattern.tsx @@ -70,7 +70,7 @@ export const EditIndexPattern = withRouter( indexPattern.fields.getAll().filter((field) => field.type === 'conflict') ); const [defaultIndex, setDefaultIndex] = useState(uiSettings.get('defaultIndex')); - const [tags, setTags] = useState([]); + const [tags, setTags] = useState>([]); const [showEditDialog, setShowEditDialog] = useState(false); const [relationships, setRelationships] = useState([]); const [allowedTypes, setAllowedTypes] = useState([]); @@ -220,7 +220,7 @@ export const EditIndexPattern = withRouter( {securityDataView} )} - {tags.map((tag: any) => ( + {tags.map((tag) => ( {tag.key === 'default' ? ( diff --git a/src/plugins/data_views/common/data_views/data_view.ts b/src/plugins/data_views/common/data_views/data_view.ts index 6f01f61c98fe8e..88e1810debe291 100644 --- a/src/plugins/data_views/common/data_views/data_view.ts +++ b/src/plugins/data_views/common/data_views/data_view.ts @@ -6,21 +6,31 @@ * Side Public License, v 1. */ -import _, { cloneDeep, each, reject } from 'lodash'; -import { castEsToKbnFieldTypeName, ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { CharacterNotAllowedInField } from '@kbn/kibana-utils-plugin/common'; -import { - FieldFormatsStartCommon, +import type { DataViewBase } from '@kbn/es-query'; +import type { FieldFormat, + FieldFormatsStartCommon, SerializedFieldFormat, } from '@kbn/field-formats-plugin/common'; -import type { DataViewBase } from '@kbn/es-query'; -import { FieldAttrs, FieldAttrSet, DataViewAttributes } from '..'; -import type { RuntimeField, RuntimeFieldSpec, RuntimeType, FieldConfiguration } from '../types'; -import { DataViewField, IIndexPatternFieldList, fieldList } from '../fields'; +import { castEsToKbnFieldTypeName, ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; +import { CharacterNotAllowedInField } from '@kbn/kibana-utils-plugin/common'; +import type { SerializableRecord } from '@kbn/utility-types'; +import _, { cloneDeep, each, reject } from 'lodash'; +import type { DataViewAttributes, FieldAttrs, FieldAttrSet } from '..'; +import type { DataViewField, IIndexPatternFieldList } from '../fields'; +import { fieldList } from '../fields'; +import type { + DataViewFieldMap, + DataViewSpec, + FieldConfiguration, + RuntimeField, + RuntimeFieldSpec, + RuntimeType, + SourceFilter, + TypeMeta, +} from '../types'; import { flattenHitWrapper } from './flatten_hit'; -import { DataViewSpec, TypeMeta, SourceFilter, DataViewFieldMap } from '../types'; import { removeFieldAttrs } from './utils'; interface DataViewDeps { @@ -70,7 +80,7 @@ export class DataView implements DataViewBase { /** * Map of field formats by field name */ - public fieldFormatMap: Record; + public fieldFormatMap: Record>; /** * Only used by rollup indices, used by rollup specific endpoint to load field list. */ @@ -90,7 +100,7 @@ export class DataView implements DataViewBase { /** * @deprecated Use `flattenHit` utility method exported from data plugin instead. */ - public flattenHit: (hit: Record, deep?: boolean) => Record; + public flattenHit: (hit: Record, deep?: boolean) => Record; /** * List of meta fields by name */ @@ -233,7 +243,7 @@ export class DataView implements DataViewBase { }; } - // Date value returned in "_source" could be in any number of formats + // Date value returned in "_source" could be in a number of formats // Use a docvalue for each date field to ensure standardized formats when working with date fields // dataView.flattenHit will override "_source" values when the same field is also defined in "fields" const docvalueFields = reject(this.fields.getByType('date'), 'scripted').map((dateField) => { @@ -296,7 +306,7 @@ export class DataView implements DataViewBase { name: this.name, }; - // Filter any undefined values from the spec + // Filter undefined values from the spec return Object.fromEntries(Object.entries(spec).filter(([, v]) => typeof v !== 'undefined')); } diff --git a/src/plugins/data_views/common/data_views/flatten_hit.test.ts b/src/plugins/data_views/common/data_views/flatten_hit.test.ts index 24d5ee12885d2b..ab7e7d14df4ecd 100644 --- a/src/plugins/data_views/common/data_views/flatten_hit.test.ts +++ b/src/plugins/data_views/common/data_views/flatten_hit.test.ts @@ -59,7 +59,7 @@ describe('flattenHit', () => { zzz: ['z'], _abc: ['a'], }, - }); + } as {}); const expectedOrder = ['_abc', 'date', 'name', 'zzz', '_id', '_routing', '_score', '_type']; expect(Object.keys(response)).toEqual(expectedOrder); expect(Object.entries(response).map(([key]) => key)).toEqual(expectedOrder); diff --git a/src/plugins/data_views/common/data_views/flatten_hit.ts b/src/plugins/data_views/common/data_views/flatten_hit.ts index 0d0311a4285977..af1d9dff683bdf 100644 --- a/src/plugins/data_views/common/data_views/flatten_hit.ts +++ b/src/plugins/data_views/common/data_views/flatten_hit.ts @@ -71,8 +71,11 @@ function flattenHit( return flat; } -function decorateFlattenedWrapper(hit: Record, metaFields: Record) { - return function (flattened: Record) { +function decorateFlattenedWrapper( + hit: Record, + metaFields: Record +) { + return function (flattened: Record) { // assign the meta fields _.each(metaFields, function (meta) { if (meta === '_source') return; @@ -80,7 +83,7 @@ function decorateFlattenedWrapper(hit: Record, metaFields: Record, metaFields: Record, deep = false) { + return function cachedFlatten(hit: Record, deep = false) { const decorateFlattened = decorateFlattenedWrapper(hit, metaFields); const cached = cache.get(hit); const flattened = cached || flattenHit(dataView, hit, deep); diff --git a/src/plugins/data_views/common/fields/utils.ts b/src/plugins/data_views/common/fields/utils.ts index 1dc7e7f698995a..c42b6d23d5ad26 100644 --- a/src/plugins/data_views/common/fields/utils.ts +++ b/src/plugins/data_views/common/fields/utils.ts @@ -30,11 +30,9 @@ const DOT_PREFIX_RE = /(.).+?\./g; /** * Convert a dot.notated.string into a short * version (d.n.string) - * - * @return {any} */ -export function shortenDottedString(input: any) { - return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.'); +export function shortenDottedString(input: unknown): string { + return typeof input !== 'string' ? (input as string) : input.replace(DOT_PREFIX_RE, '$1.'); } // Note - this code is duplicated from @kbn/es-query diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 8a5c310621a689..51addbefdc1a26 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -6,15 +6,22 @@ * Side Public License, v 1. */ +import type { + QueryDslQueryContainer, + QueryDslTypeQuery, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { + SavedObject, + SavedObjectsCreateOptions, + SavedObjectsUpdateOptions, +} from '@kbn/core/public'; +import type { ErrorToastOptions, ToastInputFields } from '@kbn/core/public/notifications'; import type { DataViewFieldBase } from '@kbn/es-query'; -import { ToastInputFields, ErrorToastOptions } from '@kbn/core/public/notifications'; -// eslint-disable-next-line -import type { SavedObject } from 'src/core/server'; -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SerializedFieldFormat } from '@kbn/field-formats-plugin/common'; import { RUNTIME_FIELD_TYPES } from './constants'; export type { QueryDslQueryContainer }; +export type { SavedObject }; export type FieldFormatMap = Record; @@ -282,7 +289,7 @@ export interface SavedObjectsClientCommon { type: string, id: string, attributes: DataViewAttributes, - options: { version?: string } + options: SavedObjectsUpdateOptions ) => Promise; /** * Create a saved object @@ -293,7 +300,7 @@ export interface SavedObjectsClientCommon { create: ( type: string, attributes: DataViewAttributes, - options: { id?: string } + options: SavedObjectsCreateOptions ) => Promise; /** * Delete a saved object by id @@ -305,7 +312,7 @@ export interface SavedObjectsClientCommon { export interface GetFieldsOptions { pattern: string; - type?: string; + type?: QueryDslTypeQuery; lookBack?: boolean; metaFields?: string[]; rollupIndex?: string; @@ -318,8 +325,6 @@ export interface IDataViewsApiClient { hasUserIndexPattern: () => Promise; } -export type { SavedObject }; - export type AggregationRestrictions = Record< string, { diff --git a/src/plugins/data_views/public/data_views/data_views_api_client.ts b/src/plugins/data_views/public/data_views/data_views_api_client.ts index e1b7ee1590acf5..c9386f47ee7f55 100644 --- a/src/plugins/data_views/public/data_views/data_views_api_client.ts +++ b/src/plugins/data_views/public/data_views/data_views_api_client.ts @@ -8,7 +8,7 @@ import { HttpSetup } from '@kbn/core/public'; import { DataViewMissingIndices } from '../../common/lib'; -import { GetFieldsOptions, IDataViewsApiClient } from '../../common'; +import { FieldSpec, GetFieldsOptions, IDataViewsApiClient } from '../../common'; const API_BASE_URL: string = `/api/index_patterns/`; @@ -26,18 +26,16 @@ export class DataViewsApiClient implements IDataViewsApiClient { this.http = http; } - private _request(url: string, query?: any) { - return this.http - .fetch(url, { - query, - }) - .catch((resp: any) => { - if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { - throw new DataViewMissingIndices(resp.body.message); - } - - throw new Error(resp.body.message || resp.body.error || `${resp.body.statusCode} Response`); - }); + private _request( + url: string, + query?: {} + ): Promise<{ fields?: FieldSpec[]; result?: boolean }> { + return this.http.fetch(url, { query }).catch((resp) => { + if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { + throw new DataViewMissingIndices(resp.body.message); + } + throw new Error(resp.body.message || resp.body.error || `${resp.body.statusCode} Response`); + }); } private _getUrl(path: string[]) { @@ -57,7 +55,7 @@ export class DataViewsApiClient implements IDataViewsApiClient { rollup_index: rollupIndex, allow_no_index: allowNoIndex, filter, - }).then((resp: any) => resp.fields || []); + }).then((resp) => resp.fields || []); } /** @@ -67,6 +65,6 @@ export class DataViewsApiClient implements IDataViewsApiClient { const response = await this._request<{ result: boolean }>( this._getUrl(['has_user_index_pattern']) ); - return response.result; + return response.result ?? false; } } diff --git a/src/plugins/data_views/public/saved_objects_client_wrapper.ts b/src/plugins/data_views/public/saved_objects_client_wrapper.ts index 9dab5ec46f5488..e8d008e130323b 100644 --- a/src/plugins/data_views/public/saved_objects_client_wrapper.ts +++ b/src/plugins/data_views/public/saved_objects_client_wrapper.ts @@ -6,14 +6,20 @@ * Side Public License, v 1. */ +import { + SavedObjectsClientContract, + SavedObjectsCreateOptions, + SavedObjectsUpdateOptions, + SimpleSavedObject, +} from '@kbn/core/public'; import { omit } from 'lodash'; -import { SavedObjectsClientContract, SimpleSavedObject } from '@kbn/core/public'; +import { DataViewSavedObjectConflictError } from '../common/errors'; import { + DataViewAttributes, + SavedObject, SavedObjectsClientCommon, SavedObjectsClientCommonFindArgs, - SavedObject, } from '../common/types'; -import { DataViewSavedObjectConflictError } from '../common/errors'; type SOClient = Pick< SavedObjectsClientContract, @@ -24,7 +30,7 @@ const simpleSavedObjectToSavedObject = (simpleSavedObject: SimpleSavedObject) ({ version: simpleSavedObject._version, ...omit(simpleSavedObject, '_version'), - } as any); + } as SavedObject); export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommon { private savedObjectClient: SOClient; @@ -49,14 +55,14 @@ export class SavedObjectsClientPublicToCommon implements SavedObjectsClientCommo async update( type: string, id: string, - attributes: Record, - options: Record + attributes: DataViewAttributes, + options: SavedObjectsUpdateOptions ) { const response = await this.savedObjectClient.update(type, id, attributes, options); return simpleSavedObjectToSavedObject(response); } - async create(type: string, attributes: Record, options: Record) { + async create(type: string, attributes: DataViewAttributes, options?: SavedObjectsCreateOptions) { const response = await this.savedObjectClient.create(type, attributes, options); return simpleSavedObjectToSavedObject(response); } diff --git a/src/plugins/data_views/server/fetcher/lib/errors.ts b/src/plugins/data_views/server/fetcher/lib/errors.ts index 811b48f129138b..b1674840af11f4 100644 --- a/src/plugins/data_views/server/fetcher/lib/errors.ts +++ b/src/plugins/data_views/server/fetcher/lib/errors.ts @@ -16,10 +16,10 @@ const ERR_NO_MATCHING_INDICES = 'no_matching_indices'; /** * Determines if an error is an elasticsearch error that's * describing a failure caused by missing index/indices - * @param {Any} err + * @param err * @return {Boolean} */ -export function isEsIndexNotFoundError(err: any) { +export function isEsIndexNotFoundError(err: unknown) { return get(err, ['body', 'error', 'type']) === ERR_ES_INDEX_NOT_FOUND; } @@ -38,10 +38,10 @@ export function createNoMatchingIndicesError(pattern: string[] | string) { /** * Determines if an error is produced by `createNoMatchingIndicesError()` * - * @param {Any} err + * @param err * @return {Boolean} */ -export function isNoMatchingIndicesError(err: any) { +export function isNoMatchingIndicesError(err: unknown) { return get(err, ['output', 'payload', 'code']) === ERR_NO_MATCHING_INDICES; } diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index 814dccf0196226..caedf39d6d30d6 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -6,7 +6,11 @@ * Side Public License, v 1. */ -import { RollupGetRollupIndexCapsRollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; +import { + Field, + RollupGetRollupIndexCapsRollupJobSummary, + RollupGetRollupIndexCapsRollupJobSummaryField, +} from '@elastic/elasticsearch/lib/api/types'; import { SerializableRecord } from '@kbn/utility-types'; import { get, isEqual, set } from 'lodash'; @@ -44,59 +48,67 @@ export function mergeJobConfigurations(jobs: RollupGetRollupIndexCapsRollupJobSu const allAggs: { [key: string]: SerializableRecord } = {}; // For each job, look through all of its fields - jobs.forEach((job: { fields: { [key: string]: any } }) => { - const fields = job.fields; - const fieldNames = Object.keys(fields); + jobs.forEach( + (job: { fields: { [key: Field]: RollupGetRollupIndexCapsRollupJobSummaryField[] } }) => { + const fields = job.fields; + const fieldNames = Object.keys(fields); - // Check each field - fieldNames.forEach((fieldName) => { - const fieldAggs = fields[fieldName]; + // Check each field + fieldNames.forEach((fieldName) => { + const fieldAggs = fields[fieldName]; - // Look through each field's capabilities (aggregations) - fieldAggs.forEach((agg: { agg: string; interval: string }) => { - const aggName = agg.agg; - const aggDoesntExist = !allAggs[aggName]; - const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; - const isDateHistogramAgg = aggName === 'date_histogram'; + // Look through each field's capabilities (aggregations) + fieldAggs.forEach((agg) => { + const aggName = agg.agg; + const aggDoesntExist = !allAggs[aggName]; + const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; + const isDateHistogramAgg = aggName === 'date_histogram'; - // If we currently don't have this aggregation, add it. - // Special case for date histogram, since there can only be one - // date histogram field. - if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { - allAggs[aggName] = allAggs[aggName] || {}; - allAggs[aggName][fieldName] = { ...agg }; - } - // If aggregation already exists, attempt to merge it - else { - const fieldAgg = allAggs[aggName][fieldName] as object | null; - switch (aggName) { - // For histograms, calculate the least common multiple between the - // new interval and existing interval - case 'histogram': - if (fieldAgg) { - // TODO: Fix this with LCD algorithm - const intervals = [get(fieldAgg, 'interval'), agg.interval].sort((a, b) => a - b); - const isMultiple = intervals[1] % intervals[0] === 0; - set(fieldAgg, 'interval', isMultiple ? intervals[1] : intervals[0] * intervals[1]); - } - break; + // If we currently don't have this aggregation, add it. + // Special case for date histogram, since there can only be one + // date histogram field. + if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { + allAggs[aggName] = allAggs[aggName] || {}; + allAggs[aggName][fieldName] = { ...agg }; + } + // If aggregation already exists, attempt to merge it + else { + const fieldAgg = allAggs[aggName][fieldName] as object | null; + switch (aggName) { + // For histograms, calculate the least common multiple between the + // new interval and existing interval + case 'histogram': + if (fieldAgg) { + // FIXME: Property 'interval' does not exist on type 'RollupGetRollupIndexCapsRollupJobSummaryField' + const aggInterval = (agg as any).interval; + // TODO: Fix this with LCD algorithm + const intervals = [get(fieldAgg, 'interval'), aggInterval].sort((a, b) => a - b); + const isMultiple = intervals[1] % intervals[0] === 0; + set( + fieldAgg, + 'interval', + isMultiple ? intervals[1] : intervals[0] * intervals[1] + ); + } + break; - // For date histograms, if it is on the same field, check that the configuration is identical, - // otherwise reject. If not the same field, reject; - case 'date_histogram': - if (fieldDoesntExist || !isEqual(fieldAgg, agg)) { - throw new Error('Multiple date histograms configured'); - } - break; + // For date histograms, if it is on the same field, check that the configuration is identical, + // otherwise reject. If not the same field, reject; + case 'date_histogram': + if (fieldDoesntExist || !isEqual(fieldAgg, agg)) { + throw new Error('Multiple date histograms configured'); + } + break; - // For other aggs (terms, metric aggs), no merging is necessary - default: - break; + // For other aggs (terms, metric aggs), no merging is necessary + default: + break; + } } - } + }); }); - }); - }); + } + ); return { aggs: allAggs, diff --git a/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts b/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts index f80ae108fb6819..fda4bccacca7f0 100644 --- a/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts +++ b/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts @@ -11,7 +11,7 @@ import { FieldDescriptor } from '../index_patterns_fetcher'; export const mergeCapabilitiesWithFields = ( - rollupIndexCapabilities: { [key: string]: any }, + rollupIndexCapabilities: { [key: string]: {} }, fieldsFromFieldCapsApi: Record, previousFields: FieldDescriptor[] = [] ) => { diff --git a/src/plugins/data_views/server/ui_settings_wrapper.ts b/src/plugins/data_views/server/ui_settings_wrapper.ts index 51f23d7de4cd6d..e9968d500fca83 100644 --- a/src/plugins/data_views/server/ui_settings_wrapper.ts +++ b/src/plugins/data_views/server/ui_settings_wrapper.ts @@ -14,15 +14,15 @@ export class UiSettingsServerToCommon implements UiSettingsCommon { constructor(uiSettings: IUiSettingsClient) { this.uiSettings = uiSettings; } - get(key: string): Promise { + get(key: string): Promise { return this.uiSettings.get(key); } - getAll(): Promise> { + getAll(): Promise> { return this.uiSettings.getAll(); } - set(key: string, value: any) { + set(key: string, value: unknown) { return this.uiSettings.set(key, value); } From 7604fe23140c01f8ec2ff5263d1447de529f7a88 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 12:40:25 -0700 Subject: [PATCH 07/26] remove "any" in code comments --- src/plugins/data_view_editor/kibana.json | 2 +- .../public/components/preview/field_preview_context.tsx | 8 ++++---- .../source_filters_table/source_filters_table.tsx | 2 +- src/plugins/data_views/README.mdx | 2 +- src/plugins/data_views/common/constants.ts | 2 +- src/plugins/data_views/public/services/has_data.ts | 2 +- .../fetcher/lib/field_capabilities/field_caps_response.ts | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/plugins/data_view_editor/kibana.json b/src/plugins/data_view_editor/kibana.json index 8e83cb1be046b5..26bff485037e2d 100644 --- a/src/plugins/data_view_editor/kibana.json +++ b/src/plugins/data_view_editor/kibana.json @@ -9,5 +9,5 @@ "name": "App Services", "githubTeam": "kibana-app-services" }, - "description": "This plugin provides the ability to create data views via a modal flyout from any kibana app" + "description": "This plugin provides the ability to create data views via a modal flyout inside Kibana apps" } diff --git a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx index 6e21cc00e5dd56..127badffc826d0 100644 --- a/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx +++ b/src/plugins/data_view_field_editor/public/components/preview/field_preview_context.tsx @@ -121,7 +121,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { // If no documents could be fetched from the cluster (and we are not trying to load // a custom doc ID) then we disable preview as the script field validation expect the result // of the preview to before resolving. If there are no documents we can't have a preview - // (the _execute API expects one) and thus the validation should not expect any value. + // (the _execute API expects one) and thus the validation should not expect a value. if (!isFetchingDocument && !isCustomDocId && documents.length === 0) { isPreviewAvailable = false; } @@ -341,7 +341,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { if (currentApiCall !== previewCount.current) { // Discard this response as there is another one inflight - // or we have called reset() and don't need the response anymore. + // or we have called reset() and no longer need the response. return; } @@ -410,7 +410,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { }, [currentIdx, totalDocs]); const reset = useCallback(() => { - // By resetting the previewCount we will discard any inflight + // By resetting the previewCount we will discard previous inflight // API call response coming in after calling reset() was called previewCount.current = 0; @@ -603,7 +603,7 @@ export const FieldPreviewProvider: FunctionComponent = ({ children }) => { }, [scriptEditorValidation, script?.source, setPreviewError, clearPreviewError]); /** - * Whenever updatePreview() changes (meaning whenever any of the params changes) + * Whenever updatePreview() changes (meaning whenever a param changes) * we call it to update the preview response with the field(s) value or possible error. */ useDebounce(updatePreview, 500, [updatePreview]); diff --git a/src/plugins/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx b/src/plugins/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx index 455371d6c5e2bf..f2fd9ce2d2140f 100644 --- a/src/plugins/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx +++ b/src/plugins/data_view_management/public/components/edit_index_pattern/source_filters_table/source_filters_table.tsx @@ -33,7 +33,7 @@ export class SourceFiltersTable extends Component< SourceFiltersTableProps, SourceFiltersTableState > { - // Source filters do not have any unique ids, only the value is stored. + // Source filters do not have unique ids, only the value is stored. // To ensure we can create a consistent and expected UX when managing // source filters, we are assigning a unique id to each filter on the // client side only diff --git a/src/plugins/data_views/README.mdx b/src/plugins/data_views/README.mdx index 2d7bf08d785862..7fdd435db746ef 100644 --- a/src/plugins/data_views/README.mdx +++ b/src/plugins/data_views/README.mdx @@ -20,5 +20,5 @@ and field lists across the various Kibana apps. Its typically used in conjunctio **hasData:** A standardized way to check the empty state for indices and data views. - `hasESData: () => Promise; // Check to see if ES data exists` -- `hasDataView: () => Promise; // Check to see if any data view exists (managed or user created)` +- `hasDataView: () => Promise; // Check to see if data view exists (managed or user created)` - `hasUserDataView: () => Promise; // Check to see if user created data views exists` diff --git a/src/plugins/data_views/common/constants.ts b/src/plugins/data_views/common/constants.ts index 81a424d291bec3..b79eef45e7fc86 100644 --- a/src/plugins/data_views/common/constants.ts +++ b/src/plugins/data_views/common/constants.ts @@ -22,7 +22,7 @@ export const RUNTIME_FIELD_TYPES = [ ] as const; /** - * Used to determine if the instance has any user created index patterns by filtering index patterns + * Used to determine if the instance has some user created index patterns by filtering index patterns * that are created and backed only by Fleet server data * Should be revised after https://github.com/elastic/kibana/issues/82851 is fixed * For more background see: https://github.com/elastic/kibana/issues/107020 diff --git a/src/plugins/data_views/public/services/has_data.ts b/src/plugins/data_views/public/services/has_data.ts index 4c641e0e17f136..45f44e04e23a8a 100644 --- a/src/plugins/data_views/public/services/has_data.ts +++ b/src/plugins/data_views/public/services/has_data.ts @@ -41,7 +41,7 @@ export class HasData { return hasLocalESData; }, /** - * Check to see if any data view exists + * Check to see if a data view exists */ hasDataView: async (): Promise => { const dataViewsCheck = await this.findDataViews(http); diff --git a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts index 2581b2b78f5d80..c1bfc4c6c7e180 100644 --- a/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts +++ b/src/plugins/data_views/server/fetcher/lib/field_capabilities/field_caps_response.ts @@ -171,7 +171,7 @@ export function readFieldCapsResponse( subType = { ...subType, multi: { parent: firstParent.name } }; } - // We need to know if any parent field is nested + // We need to know if some parent field is nested const nestedParentCaps = parentFieldCapsAscending.find( (parentCaps) => parentCaps && parentCaps.type === 'nested' ); From 266f750ebc41e8f3917e083ece20ba412148374b Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 12:58:30 -0700 Subject: [PATCH 08/26] fix types --- .../data_view_field_editor/public/types.ts | 14 +++++++------- .../field_editor/field_editor.test.tsx | 3 ++- .../data_views/common/data_views/data_view.ts | 4 ++-- src/plugins/data_views/common/types.ts | 7 ++----- .../server/fetcher/lib/map_capabilities.ts | 2 +- .../lib/merge_capabilities_with_fields.ts | 2 +- .../timeseries/common/fields_utils.ts | 3 ++- .../public/indexpattern_datasource/loader.ts | 19 ++++++++++--------- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/plugins/data_view_field_editor/public/types.ts b/src/plugins/data_view_field_editor/public/types.ts index dbc92b9d7aea21..504a97eb7f329d 100644 --- a/src/plugins/data_view_field_editor/public/types.ts +++ b/src/plugins/data_view_field_editor/public/types.ts @@ -6,20 +6,20 @@ * Side Public License, v 1. */ +import { SerializableRecord } from '@kbn/utility-types'; import { FunctionComponent } from 'react'; - +import { DeleteFieldProviderProps } from './components'; +import { OpenFieldDeleteModalOptions } from './open_delete_modal'; +import { OpenFieldEditorOptions } from './open_editor'; +import { FormatEditorServiceSetup, FormatEditorServiceStart } from './service'; import { DataPublicPluginStart, DataViewsPublicPluginStart, + FieldFormatsStart, RuntimeField, RuntimeType, UsageCollectionStart, - FieldFormatsStart, } from './shared_imports'; -import { OpenFieldEditorOptions } from './open_editor'; -import { OpenFieldDeleteModalOptions } from './open_delete_modal'; -import { FormatEditorServiceSetup, FormatEditorServiceStart } from './service'; -import { DeleteFieldProviderProps } from './components'; export interface PluginSetup { fieldFormatEditors: FormatEditorServiceSetup['fieldFormatEditors']; @@ -58,7 +58,7 @@ export interface Field { export interface FieldFormatConfig { id: string; - params?: { [key: string]: unknown }; + params?: SerializableRecord; } export interface EsRuntimeField { diff --git a/src/plugins/data_view_management/public/components/field_editor/field_editor.test.tsx b/src/plugins/data_view_management/public/components/field_editor/field_editor.test.tsx index 89f41e8b269ad4..f3b533b7d8290c 100644 --- a/src/plugins/data_view_management/public/components/field_editor/field_editor.test.tsx +++ b/src/plugins/data_view_management/public/components/field_editor/field_editor.test.tsx @@ -85,6 +85,7 @@ class Format { params() {} } +// FIXME: which interface is this? const field = { scripted: true, type: 'number', @@ -190,7 +191,7 @@ describe('FieldEditor', () => { add: jest.fn(), }, }; - indexPattern.fieldFormatMap = { test: field }; + indexPattern.fieldFormatMap = { test: field } as {}; (indexPattern.deleteFieldFormat as any) = jest.fn(); const component = createComponentWithContext( diff --git a/src/plugins/data_views/common/data_views/data_view.ts b/src/plugins/data_views/common/data_views/data_view.ts index 88e1810debe291..c8cafd36fb7429 100644 --- a/src/plugins/data_views/common/data_views/data_view.ts +++ b/src/plugins/data_views/common/data_views/data_view.ts @@ -15,7 +15,6 @@ import type { } from '@kbn/field-formats-plugin/common'; import { castEsToKbnFieldTypeName, ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { CharacterNotAllowedInField } from '@kbn/kibana-utils-plugin/common'; -import type { SerializableRecord } from '@kbn/utility-types'; import _, { cloneDeep, each, reject } from 'lodash'; import type { DataViewAttributes, FieldAttrs, FieldAttrSet } from '..'; import type { DataViewField, IIndexPatternFieldList } from '../fields'; @@ -24,6 +23,7 @@ import type { DataViewFieldMap, DataViewSpec, FieldConfiguration, + FieldFormatMap, RuntimeField, RuntimeFieldSpec, RuntimeType, @@ -80,7 +80,7 @@ export class DataView implements DataViewBase { /** * Map of field formats by field name */ - public fieldFormatMap: Record>; + public fieldFormatMap: FieldFormatMap; /** * Only used by rollup indices, used by rollup specific endpoint to load field list. */ diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 51addbefdc1a26..424c3f8e02e450 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -6,10 +6,7 @@ * Side Public License, v 1. */ -import type { - QueryDslQueryContainer, - QueryDslTypeQuery, -} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SavedObject, SavedObjectsCreateOptions, @@ -312,7 +309,7 @@ export interface SavedObjectsClientCommon { export interface GetFieldsOptions { pattern: string; - type?: QueryDslTypeQuery; + type?: string; lookBack?: boolean; metaFields?: string[]; rollupIndex?: string; diff --git a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts index 38f8968c929ad6..6fadc2e92f12a4 100644 --- a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts @@ -10,7 +10,7 @@ import { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api import { mergeJobConfigurations } from './jobs_compatibility'; export interface RollupIndexCapability { - [key: string]: { aggs?: object; error?: string }; + [key: string]: { aggs?: Record; error?: string }; } /** diff --git a/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts b/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts index fda4bccacca7f0..922fde5347cfbe 100644 --- a/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts +++ b/src/plugins/data_views/server/fetcher/lib/merge_capabilities_with_fields.ts @@ -11,7 +11,7 @@ import { FieldDescriptor } from '../index_patterns_fetcher'; export const mergeCapabilitiesWithFields = ( - rollupIndexCapabilities: { [key: string]: {} }, + rollupIndexCapabilities: Record, fieldsFromFieldCapsApi: Record, previousFields: FieldDescriptor[] = [] ) => { diff --git a/src/plugins/vis_types/timeseries/common/fields_utils.ts b/src/plugins/vis_types/timeseries/common/fields_utils.ts index 65125f616155e8..b7d9ed55a01032 100644 --- a/src/plugins/vis_types/timeseries/common/fields_utils.ts +++ b/src/plugins/vis_types/timeseries/common/fields_utils.ts @@ -94,7 +94,8 @@ export const createCachedFieldValueFormatter = ( return convert(cachedFormatter); } - if (dataView && !excludedFieldFormatsIds.includes(dataView.fieldFormatMap?.[fieldName]?.id)) { + const dataViewId = dataView?.fieldFormatMap?.[fieldName]?.id as FIELD_FORMAT_IDS | undefined; + if (dataView && dataViewId && !excludedFieldFormatsIds.includes(dataViewId)) { const field = dataView.fields.getByName(fieldName); if (field) { const formatter = dataView.getFormatterForField(field); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index d6acf5958bd2ba..a1919a24a9fe04 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -38,7 +38,7 @@ type IndexPatternsService = Pick; type ErrorHandler = (err: Error) => void; export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPattern { - const newFields = dataView.fields + const newFields: IndexPattern['fields'] = dataView.fields .filter((field) => !isNestedField(field) && (!!field.aggregatable || !!field.scripted)) .map((field): IndexPatternField => { // Convert the getters on the index pattern service into plain JSON @@ -82,20 +82,21 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa }); } + const newFieldFormatMap: IndexPattern['fieldFormatMap'] = + fieldFormatMap && + Object.fromEntries( + Object.entries(fieldFormatMap).map(([id, format]) => [ + id, + 'toJSON' in format ? format.toJSON() : format, + ]) + ); return { id: dataView.id!, // id exists for sure because we got index patterns by id title, name: name ? name : title, timeFieldName, - fieldFormatMap: - fieldFormatMap && - Object.fromEntries( - Object.entries(fieldFormatMap).map(([id, format]) => [ - id, - 'toJSON' in format ? format.toJSON() : format, - ]) - ), fields: newFields, + fieldFormatMap: newFieldFormatMap, getFieldByName: getFieldByNameFactory(newFields), hasRestrictions: !!typeMeta?.aggs, }; From bf3d887a200d0ecea25c62eebbfc0c5b42d8a475 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 13:58:34 -0700 Subject: [PATCH 09/26] test commit --- x-pack/plugins/lens/public/indexpattern_datasource/loader.ts | 5 +---- x-pack/plugins/lens/public/indexpattern_datasource/types.ts | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index a1919a24a9fe04..aab3a731428c6f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -85,10 +85,7 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa const newFieldFormatMap: IndexPattern['fieldFormatMap'] = fieldFormatMap && Object.fromEntries( - Object.entries(fieldFormatMap).map(([id, format]) => [ - id, - 'toJSON' in format ? format.toJSON() : format, - ]) + Object.entries(fieldFormatMap).map(([id, format]) => [id, 'toJSON' in format ? {} : format]) ); return { id: dataView.id!, // id exists for sure because we got index patterns by id diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index cbf02bddb8814a..12e30f1c419b64 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -59,8 +59,8 @@ export interface IndexPattern { fieldFormatMap?: Record< string, { - id: string; - params: FieldFormatParams; + id?: string; + params?: FieldFormatParams; } >; hasRestrictions: boolean; From 366c2f853097258db2914c1b36c0277bcf90fe6f Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 14:10:54 -0700 Subject: [PATCH 10/26] fix ts --- .../plugins/lens/public/indexpattern_datasource/field_item.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 27c774ed2963ec..8f168c45ebf15d 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -432,7 +432,8 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { let formatter: { convert: (data: unknown) => string }; if (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[field.name]) { - const FormatType = fieldFormats.getType(indexPattern.fieldFormatMap[field.name].id); + const formatId = indexPattern.fieldFormatMap[field.name].id as string; + const FormatType = fieldFormats.getType(formatId); if (FormatType) { formatter = new FormatType( indexPattern.fieldFormatMap[field.name].params, From 40a62ed7c2dadf52ec312a135edba75648742c86 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 14:18:13 -0700 Subject: [PATCH 11/26] fix types --- src/plugins/data_views/public/debounce_by_key.ts | 2 +- src/plugins/data_views/server/fetcher/lib/errors.ts | 2 +- .../data_views/server/rest_api_routes/util/handle_errors.ts | 4 +++- src/plugins/data_views/server/saved_objects/data_views.ts | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_views/public/debounce_by_key.ts b/src/plugins/data_views/public/debounce_by_key.ts index c8ae7094a6437b..2c5029a4e2e2b6 100644 --- a/src/plugins/data_views/public/debounce_by_key.ts +++ b/src/plugins/data_views/public/debounce_by_key.ts @@ -8,7 +8,7 @@ import { debounce } from 'lodash'; -export const debounceByKey = any>( +export const debounceByKey = T>( fn: F, waitInMs: number ): ((key: string) => F) => { diff --git a/src/plugins/data_views/server/fetcher/lib/errors.ts b/src/plugins/data_views/server/fetcher/lib/errors.ts index b1674840af11f4..eb510798c759fe 100644 --- a/src/plugins/data_views/server/fetcher/lib/errors.ts +++ b/src/plugins/data_views/server/fetcher/lib/errors.ts @@ -31,7 +31,7 @@ export function isEsIndexNotFoundError(err: unknown) { */ export function createNoMatchingIndicesError(pattern: string[] | string) { const err = Boom.notFound(`No indices match "${pattern}"`); - (err.output.payload as any).code = ERR_NO_MATCHING_INDICES; + (err as Boom.Boom).output.payload.code = ERR_NO_MATCHING_INDICES; return err; } diff --git a/src/plugins/data_views/server/rest_api_routes/util/handle_errors.ts b/src/plugins/data_views/server/rest_api_routes/util/handle_errors.ts index c828e064fce844..a01890c34a43d0 100644 --- a/src/plugins/data_views/server/rest_api_routes/util/handle_errors.ts +++ b/src/plugins/data_views/server/rest_api_routes/util/handle_errors.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import Boom from '@hapi/boom'; import type { RequestHandler, RouteMethod, RequestHandlerContext } from '@kbn/core/server'; import { ErrorIndexPatternNotFound } from '../../error'; @@ -47,7 +48,8 @@ export const handleErrors = } const is404 = - (error as ErrorIndexPatternNotFound).is404 || (error as any)?.output?.statusCode === 404; + (error as ErrorIndexPatternNotFound).is404 || + (error as Boom.Boom)?.output?.statusCode === 404; if (is404) { return response.notFound({ diff --git a/src/plugins/data_views/server/saved_objects/data_views.ts b/src/plugins/data_views/server/saved_objects/data_views.ts index 6319911f3bfd5b..3be5c967dc9feb 100644 --- a/src/plugins/data_views/server/saved_objects/data_views.ts +++ b/src/plugins/data_views/server/saved_objects/data_views.ts @@ -40,5 +40,5 @@ export const dataViewSavedObjectType: SavedObjectsType = { type: { type: 'keyword' }, }, }, - migrations: indexPatternSavedObjectTypeMigrations as any, + migrations: indexPatternSavedObjectTypeMigrations, }; From 5ee6bd6db0509c3f24274cc200c85902c57686a3 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 14:29:22 -0700 Subject: [PATCH 12/26] remove more any --- src/plugins/data_views/common/data_views/data_views.ts | 7 +++++-- .../classes/sources/es_search_source/es_search_source.tsx | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 887579b6d44a36..a9c85e09079ddd 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -870,7 +870,8 @@ export class DataViewsService { // get changed keys const originalChangedKeys: string[] = []; Object.entries(body).forEach(([key, value]) => { - if (value !== (originalBody as any)[key]) { + const realKey = key as keyof typeof originalBody; + if (value !== originalBody[realKey]) { originalChangedKeys.push(key); } }); @@ -897,7 +898,8 @@ export class DataViewsService { const serverChangedKeys: string[] = []; Object.entries(updatedBody).forEach(([key, value]) => { - if (value !== (body as any)[key] && value !== (originalBody as any)[key]) { + const realKey = key as keyof typeof originalBody; + if (value !== body[realKey] && value !== originalBody[realKey]) { serverChangedKeys.push(key); } }); @@ -930,6 +932,7 @@ export class DataViewsService { // Set the updated response on this object serverChangedKeys.forEach((key) => { + // FIXME: this overwrites read-only properties (indexPattern as any)[key] = (samePattern as any)[key]; }); indexPattern.version = samePattern.version; diff --git a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx index 078369ea3c32e0..62c05c1e5c5633 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/es_search_source/es_search_source.tsx @@ -640,7 +640,7 @@ export class ESSearchSource extends AbstractESSource implements IMvtVectorSource ); } - const properties = indexPattern.flattenHit(hit); + const properties = indexPattern.flattenHit(hit) as Record; indexPattern.metaFields.forEach((metaField: string) => { if (!this._getTooltipPropertyNames().includes(metaField)) { delete properties[metaField]; From daf68f96a0a2e790935c6c0fda033751579fbcad Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 15:37:46 -0700 Subject: [PATCH 13/26] simplify / comment --- .../data_views/common/data_views/flatten_hit.ts | 2 +- .../indexpattern_datasource/field_item.tsx | 3 +-- .../public/indexpattern_datasource/loader.ts | 17 ++++++++++------- .../public/indexpattern_datasource/types.ts | 10 ++-------- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/plugins/data_views/common/data_views/flatten_hit.ts b/src/plugins/data_views/common/data_views/flatten_hit.ts index af1d9dff683bdf..b9932c0de950dd 100644 --- a/src/plugins/data_views/common/data_views/flatten_hit.ts +++ b/src/plugins/data_views/common/data_views/flatten_hit.ts @@ -121,7 +121,7 @@ function decorateFlattenedWrapper( * * @internal */ -export function flattenHitWrapper(dataView: DataView, metaFields = {}, cache = new WeakMap()) { +export function flattenHitWrapper(dataView: DataView, metaFields = {}, cache = new WeakMap()) { return function cachedFlatten(hit: Record, deep = false) { const decorateFlattened = decorateFlattenedWrapper(hit, metaFields); const cached = cache.get(hit); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx index 8f168c45ebf15d..a0c2a182bee628 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -432,8 +432,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { let formatter: { convert: (data: unknown) => string }; if (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[field.name]) { - const formatId = indexPattern.fieldFormatMap[field.name].id as string; - const FormatType = fieldFormats.getType(formatId); + const FormatType = fieldFormats.getType(indexPattern.fieldFormatMap[field.name].id as string); if (FormatType) { formatter = new FormatType( indexPattern.fieldFormatMap[field.name].params, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index aab3a731428c6f..1ea50d8b7934a5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -7,6 +7,7 @@ import { uniq, mapValues, difference } from 'lodash'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; +import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { HttpSetup, SavedObjectReference } from '@kbn/core/public'; import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; import { isNestedField } from '@kbn/data-views-plugin/common'; @@ -38,7 +39,7 @@ type IndexPatternsService = Pick; type ErrorHandler = (err: Error) => void; export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPattern { - const newFields: IndexPattern['fields'] = dataView.fields + const newFields = dataView.fields .filter((field) => !isNestedField(field) && (!!field.aggregatable || !!field.scripted)) .map((field): IndexPatternField => { // Convert the getters on the index pattern service into plain JSON @@ -82,18 +83,20 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa }); } - const newFieldFormatMap: IndexPattern['fieldFormatMap'] = - fieldFormatMap && - Object.fromEntries( - Object.entries(fieldFormatMap).map(([id, format]) => [id, 'toJSON' in format ? {} : format]) - ); return { id: dataView.id!, // id exists for sure because we got index patterns by id title, name: name ? name : title, timeFieldName, + fieldFormatMap: + fieldFormatMap && + Object.fromEntries( + Object.entries(fieldFormatMap).map(([id, format]) => [ + id, + 'toJSON' in format ? (format as unknown as FieldFormat).toJSON() : format, // FIXME should this work with SerializedFieldFormat or FieldFormat? + ]) + ), fields: newFields, - fieldFormatMap: newFieldFormatMap, getFieldByName: getFieldByNameFactory(newFields), hasRestrictions: !!typeMeta?.aggs, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts index 12e30f1c419b64..f441e69ac4e755 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/types.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/types.ts @@ -7,7 +7,7 @@ import type { IndexPatternAggRestrictions } from '@kbn/data-plugin/public'; import type { FieldSpec } from '@kbn/data-plugin/common'; -import type { FieldFormatParams } from '@kbn/field-formats-plugin/common'; +import type { FieldFormatMap } from '@kbn/data-views-plugin/common'; import type { DragDropIdentifier } from '../drag_drop/providers'; import type { IncompleteColumn, GenericIndexPatternColumn } from './operations'; import { DragDropOperation } from '../types'; @@ -56,13 +56,7 @@ export interface IndexPattern { title: string; name?: string; timeFieldName?: string; - fieldFormatMap?: Record< - string, - { - id?: string; - params?: FieldFormatParams; - } - >; + fieldFormatMap?: FieldFormatMap; hasRestrictions: boolean; } From 7712b060b186955b280925047ab57e6d38119291 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 15:56:41 -0700 Subject: [PATCH 14/26] more simplification --- .../data_views/data_views_api_client.ts | 28 ++++++++++--------- .../public/indexpattern_datasource/loader.ts | 2 +- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/plugins/data_views/public/data_views/data_views_api_client.ts b/src/plugins/data_views/public/data_views/data_views_api_client.ts index c9386f47ee7f55..b02894d1afb633 100644 --- a/src/plugins/data_views/public/data_views/data_views_api_client.ts +++ b/src/plugins/data_views/public/data_views/data_views_api_client.ts @@ -26,16 +26,18 @@ export class DataViewsApiClient implements IDataViewsApiClient { this.http = http; } - private _request( - url: string, - query?: {} - ): Promise<{ fields?: FieldSpec[]; result?: boolean }> { - return this.http.fetch(url, { query }).catch((resp) => { - if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { - throw new DataViewMissingIndices(resp.body.message); - } - throw new Error(resp.body.message || resp.body.error || `${resp.body.statusCode} Response`); - }); + private _request(url: string, query?: {}): Promise { + return this.http + .fetch(url, { + query, + }) + .catch((resp) => { + if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { + throw new DataViewMissingIndices(resp.body.message); + } + + throw new Error(resp.body.message || resp.body.error || `${resp.body.statusCode} Response`); + }); } private _getUrl(path: string[]) { @@ -48,14 +50,14 @@ export class DataViewsApiClient implements IDataViewsApiClient { */ getFieldsForWildcard(options: GetFieldsOptions) { const { pattern, metaFields, type, rollupIndex, allowNoIndex, filter } = options; - return this._request(this._getUrl(['_fields_for_wildcard']), { + return this._request<{ fields: FieldSpec[] }>(this._getUrl(['_fields_for_wildcard']), { pattern, meta_fields: metaFields, type, rollup_index: rollupIndex, allow_no_index: allowNoIndex, filter, - }).then((resp) => resp.fields || []); + }).then((resp) => resp?.fields || []); } /** @@ -65,6 +67,6 @@ export class DataViewsApiClient implements IDataViewsApiClient { const response = await this._request<{ result: boolean }>( this._getUrl(['has_user_index_pattern']) ); - return response.result ?? false; + return response?.result ?? false; } } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 1ea50d8b7934a5..901153aa330552 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -93,7 +93,7 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa Object.fromEntries( Object.entries(fieldFormatMap).map(([id, format]) => [ id, - 'toJSON' in format ? (format as unknown as FieldFormat).toJSON() : format, // FIXME should this work with SerializedFieldFormat or FieldFormat? + 'toJSON' in format ? (format as unknown as FieldFormat).toJSON() : format, // FIXME SerializedFieldFormat was inferred... was this intended to work with FieldFormat instead? ]) ), fields: newFields, From b7d44a6762e051e6d83256102a64a3484036a7f9 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 16:28:34 -0700 Subject: [PATCH 15/26] fix test --- src/plugins/vis_types/timeseries/common/fields_utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/vis_types/timeseries/common/fields_utils.ts b/src/plugins/vis_types/timeseries/common/fields_utils.ts index b7d9ed55a01032..d5c2dfdc68811a 100644 --- a/src/plugins/vis_types/timeseries/common/fields_utils.ts +++ b/src/plugins/vis_types/timeseries/common/fields_utils.ts @@ -94,8 +94,8 @@ export const createCachedFieldValueFormatter = ( return convert(cachedFormatter); } - const dataViewId = dataView?.fieldFormatMap?.[fieldName]?.id as FIELD_FORMAT_IDS | undefined; - if (dataView && dataViewId && !excludedFieldFormatsIds.includes(dataViewId)) { + const formatId = dataView?.fieldFormatMap?.[fieldName]?.id as FIELD_FORMAT_IDS; + if (dataView && !excludedFieldFormatsIds.includes(formatId)) { const field = dataView.fields.getByName(fieldName); if (field) { const formatter = dataView.getFormatterForField(field); From d3dbc009fcb0b5b0f76a7edec4ead08ae8400ed7 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 19:14:38 -0700 Subject: [PATCH 16/26] export public APIs --- src/plugins/data_views/common/index.ts | 6 +++++- src/plugins/data_views/server/fetcher/index.ts | 5 +++-- src/plugins/data_views/server/index.ts | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_views/common/index.ts b/src/plugins/data_views/common/index.ts index aeaca476a8abdd..ffeeb069d19121 100644 --- a/src/plugins/data_views/common/index.ts +++ b/src/plugins/data_views/common/index.ts @@ -55,7 +55,11 @@ export type { } from './types'; export { DataViewType } from './types'; -export type { DataViewsContract, DataViewsServiceDeps } from './data_views'; +export type { + DataViewsContract, + DataViewsServiceDeps, + DataViewSavedObjectAttrs, +} from './data_views'; export { DataViewsService, DataViewPersistableStateService } from './data_views'; export type { DataViewListItem, diff --git a/src/plugins/data_views/server/fetcher/index.ts b/src/plugins/data_views/server/fetcher/index.ts index 8161f187a5e6a7..2fbb7c8f3f3cb7 100644 --- a/src/plugins/data_views/server/fetcher/index.ts +++ b/src/plugins/data_views/server/fetcher/index.ts @@ -8,7 +8,8 @@ export * from './index_patterns_fetcher'; export { - shouldReadFieldFromDocValues, - mergeCapabilitiesWithFields, getCapabilitiesForRollupIndices, + mergeCapabilitiesWithFields, + shouldReadFieldFromDocValues, } from './lib'; +export type { RollupIndexCapability } from './lib'; diff --git a/src/plugins/data_views/server/index.ts b/src/plugins/data_views/server/index.ts index e9eb7f0b50a3f2..d7860e0bed4731 100644 --- a/src/plugins/data_views/server/index.ts +++ b/src/plugins/data_views/server/index.ts @@ -7,7 +7,7 @@ */ export { getFieldByName, findIndexPatternById } from './utils'; -export type { FieldDescriptor } from './fetcher'; +export type { FieldDescriptor, RollupIndexCapability } from './fetcher'; export { IndexPatternsFetcher, getCapabilitiesForRollupIndices } from './fetcher'; export type { DataViewsServerPluginStart, From 5db73e7fcf76194e5a7df4dc38e25848f435ef7c Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 22:13:53 -0700 Subject: [PATCH 17/26] add fixme comment --- .../data_views/public/debounce_by_key.ts | 7 +- src/plugins/data_views/public/plugin.ts | 4 +- .../server/fetcher/lib/jobs_compatibility.ts | 107 ++++++++---------- 3 files changed, 52 insertions(+), 66 deletions(-) diff --git a/src/plugins/data_views/public/debounce_by_key.ts b/src/plugins/data_views/public/debounce_by_key.ts index 2c5029a4e2e2b6..83b4715d96d8c7 100644 --- a/src/plugins/data_views/public/debounce_by_key.ts +++ b/src/plugins/data_views/public/debounce_by_key.ts @@ -8,11 +8,8 @@ import { debounce } from 'lodash'; -export const debounceByKey = T>( - fn: F, - waitInMs: number -): ((key: string) => F) => { - const debouncerCollector: Record = {}; +export const debounceByKey = unknown>(fn: F, waitInMs: number) => { + const debouncerCollector: Record = {}; return (key: string) => { if (!debouncerCollector[key]) { debouncerCollector[key] = debounce(fn, waitInMs, { diff --git a/src/plugins/data_views/public/plugin.ts b/src/plugins/data_views/public/plugin.ts index df379ab18964ea..ee37830da6143b 100644 --- a/src/plugins/data_views/public/plugin.ts +++ b/src/plugins/data_views/public/plugin.ts @@ -51,11 +51,11 @@ export class DataViewsPublicPlugin ): DataViewsPublicPluginStart { const { uiSettings, http, notifications, savedObjects, application } = core; - const onNotifDebounced = debounceByKey( + const onNotifDebounced = debounceByKey( notifications.toasts.add.bind(notifications.toasts), 10000 ); - const onErrorDebounced = debounceByKey( + const onErrorDebounced = debounceByKey( notifications.toasts.addError.bind(notifications.toasts), 10000 ); diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index caedf39d6d30d6..bb2f72cf43d38c 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -6,11 +6,7 @@ * Side Public License, v 1. */ -import { - Field, - RollupGetRollupIndexCapsRollupJobSummary, - RollupGetRollupIndexCapsRollupJobSummaryField, -} from '@elastic/elasticsearch/lib/api/types'; +import { RollupGetRollupIndexCapsRollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; import { SerializableRecord } from '@kbn/utility-types'; import { get, isEqual, set } from 'lodash'; @@ -48,67 +44,60 @@ export function mergeJobConfigurations(jobs: RollupGetRollupIndexCapsRollupJobSu const allAggs: { [key: string]: SerializableRecord } = {}; // For each job, look through all of its fields - jobs.forEach( - (job: { fields: { [key: Field]: RollupGetRollupIndexCapsRollupJobSummaryField[] } }) => { - const fields = job.fields; - const fieldNames = Object.keys(fields); + jobs.forEach((job) => { + const fields = job.fields; + const fieldNames = Object.keys(fields); - // Check each field - fieldNames.forEach((fieldName) => { - const fieldAggs = fields[fieldName]; + // Check each field + fieldNames.forEach((fieldName) => { + const fieldAggs = fields[fieldName]; - // Look through each field's capabilities (aggregations) - fieldAggs.forEach((agg) => { - const aggName = agg.agg; - const aggDoesntExist = !allAggs[aggName]; - const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; - const isDateHistogramAgg = aggName === 'date_histogram'; + // Look through each field's capabilities (aggregations) + fieldAggs.forEach((agg) => { + const aggName = agg.agg; + const aggDoesntExist = !allAggs[aggName]; + const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; + const isDateHistogramAgg = aggName === 'date_histogram'; - // If we currently don't have this aggregation, add it. - // Special case for date histogram, since there can only be one - // date histogram field. - if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { - allAggs[aggName] = allAggs[aggName] || {}; - allAggs[aggName][fieldName] = { ...agg }; - } - // If aggregation already exists, attempt to merge it - else { - const fieldAgg = allAggs[aggName][fieldName] as object | null; - switch (aggName) { - // For histograms, calculate the least common multiple between the - // new interval and existing interval - case 'histogram': - if (fieldAgg) { - // FIXME: Property 'interval' does not exist on type 'RollupGetRollupIndexCapsRollupJobSummaryField' - const aggInterval = (agg as any).interval; - // TODO: Fix this with LCD algorithm - const intervals = [get(fieldAgg, 'interval'), aggInterval].sort((a, b) => a - b); - const isMultiple = intervals[1] % intervals[0] === 0; - set( - fieldAgg, - 'interval', - isMultiple ? intervals[1] : intervals[0] * intervals[1] - ); - } - break; + // If we currently don't have this aggregation, add it. + // Special case for date histogram, since there can only be one + // date histogram field. + if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { + allAggs[aggName] = allAggs[aggName] || {}; + allAggs[aggName][fieldName] = { ...agg }; + } + // If aggregation already exists, attempt to merge it + else { + const fieldAgg = allAggs[aggName][fieldName] as object | null; + switch (aggName) { + // For histograms, calculate the least common multiple between the + // new interval and existing interval + case 'histogram': + if (fieldAgg) { + const aggInterval = (agg as any).interval ?? agg.calendar_interval; // FIXME the interface infers only that calendar_interval property is valid + // TODO: Fix this with LCD algorithm + const intervals = [get(fieldAgg, 'interval'), aggInterval].sort((a, b) => a - b); + const isMultiple = intervals[1] % intervals[0] === 0; + set(fieldAgg, 'interval', isMultiple ? intervals[1] : intervals[0] * intervals[1]); + } + break; - // For date histograms, if it is on the same field, check that the configuration is identical, - // otherwise reject. If not the same field, reject; - case 'date_histogram': - if (fieldDoesntExist || !isEqual(fieldAgg, agg)) { - throw new Error('Multiple date histograms configured'); - } - break; + // For date histograms, if it is on the same field, check that the configuration is identical, + // otherwise reject. If not the same field, reject; + case 'date_histogram': + if (fieldDoesntExist || !isEqual(fieldAgg, agg)) { + throw new Error('Multiple date histograms configured'); + } + break; - // For other aggs (terms, metric aggs), no merging is necessary - default: - break; - } + // For other aggs (terms, metric aggs), no merging is necessary + default: + break; } - }); + } }); - } - ); + }); + }); return { aggs: allAggs, From ef9e85d19b3fc7f86c54c26efd33fa68ce9b7256 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Wed, 13 Jul 2022 22:17:05 -0700 Subject: [PATCH 18/26] cleanup --- .../server/fetcher/index_patterns_fetcher.ts | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts index 962788097525ff..dd02c9b7f9a12d 100644 --- a/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts +++ b/src/plugins/data_views/server/fetcher/index_patterns_fetcher.ts @@ -90,21 +90,17 @@ export class IndexPatternsFetcher { } const rollupIndexCapabilities = capabilityCheck.aggs; - - if (rollupIndexCapabilities) { - const fieldCapsResponseObj = keyBy(fieldCapsResponse, 'name'); - // Keep meta fields - metaFields!.forEach( - (field: string) => - fieldCapsResponseObj[field] && rollupFields.push(fieldCapsResponseObj[field]) - ); - - return mergeCapabilitiesWithFields( - rollupIndexCapabilities, - fieldCapsResponseObj, - rollupFields - ); - } + const fieldCapsResponseObj = keyBy(fieldCapsResponse, 'name'); + // Keep meta fields + metaFields!.forEach( + (field: string) => + fieldCapsResponseObj[field] && rollupFields.push(fieldCapsResponseObj[field]) + ); + return mergeCapabilitiesWithFields( + rollupIndexCapabilities!, + fieldCapsResponseObj, + rollupFields + ); } return fieldCapsResponse; } From fbad73ad2d543b7eaa9713c9e7e13cb2148e70ad Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 09:31:44 -0700 Subject: [PATCH 19/26] improve documentation and types for RollupIndexCapability --- .../server/fetcher/lib/jobs_compatibility.ts | 34 ++++++++++++------- .../server/fetcher/lib/map_capabilities.ts | 8 +++-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index bb2f72cf43d38c..c0267b18875adb 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -6,9 +6,12 @@ * Side Public License, v 1. */ -import { RollupGetRollupIndexCapsRollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; -import { SerializableRecord } from '@kbn/utility-types'; -import { get, isEqual, set } from 'lodash'; +import { + RollupGetRollupIndexCapsRollupJobSummary as RollupJobSummary, + RollupGetRollupIndexCapsRollupJobSummaryField as RollupJobSummaryField, +} from '@elastic/elasticsearch/lib/api/types'; +import { Dictionary, get, isEqual, set } from 'lodash'; +import { FieldDescriptor } from '../index_patterns_fetcher'; /** * Checks if given job configs are compatible by attempting to merge them @@ -34,14 +37,16 @@ export function areJobsCompatible(jobs = []) { * by aggregation, then by field * * @param jobs - * @returns {{}} + * @returns {{ aggs?: Dictionary }} */ -export function mergeJobConfigurations(jobs: RollupGetRollupIndexCapsRollupJobSummary[] = []) { +export function mergeJobConfigurations(jobs: RollupJobSummary[] = []): { + aggs?: Dictionary; +} { if (!jobs || !Array.isArray(jobs) || !jobs.length) { throw new Error('No capabilities available'); } - const allAggs: { [key: string]: SerializableRecord } = {}; + const allAggs: Dictionary = {}; // For each job, look through all of its fields jobs.forEach((job) => { @@ -50,31 +55,36 @@ export function mergeJobConfigurations(jobs: RollupGetRollupIndexCapsRollupJobSu // Check each field fieldNames.forEach((fieldName) => { + const typedFieldName = fieldName as keyof FieldDescriptor; const fieldAggs = fields[fieldName]; // Look through each field's capabilities (aggregations) fieldAggs.forEach((agg) => { const aggName = agg.agg; - const aggDoesntExist = !allAggs[aggName]; - const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; + const aggregation = allAggs[aggName]; + const aggDoesntExist = !aggregation; + const aggregationField = aggregation[typedFieldName]; + const fieldDoesntExist = aggregation && !aggregationField; const isDateHistogramAgg = aggName === 'date_histogram'; // If we currently don't have this aggregation, add it. // Special case for date histogram, since there can only be one // date histogram field. if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { - allAggs[aggName] = allAggs[aggName] || {}; - allAggs[aggName][fieldName] = { ...agg }; + allAggs[aggName] = aggregation || {}; + (aggregation[typedFieldName] as RollupJobSummaryField) = { ...agg }; } // If aggregation already exists, attempt to merge it else { - const fieldAgg = allAggs[aggName][fieldName] as object | null; + const fieldAgg = aggregationField as object | null; switch (aggName) { // For histograms, calculate the least common multiple between the // new interval and existing interval case 'histogram': if (fieldAgg) { - const aggInterval = (agg as any).interval ?? agg.calendar_interval; // FIXME the interface infers only that calendar_interval property is valid + // FIXME the interface infers only that calendar_interval property is valid + const aggInterval = (agg as any).interval ?? agg.calendar_interval; + // TODO: Fix this with LCD algorithm const intervals = [get(fieldAgg, 'interval'), aggInterval].sort((a, b) => a - b); const isMultiple = intervals[1] % intervals[0] === 0; diff --git a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts index 6fadc2e92f12a4..ff5951f9c95908 100644 --- a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts @@ -7,10 +7,15 @@ */ import { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; +import { Dictionary } from 'lodash'; +import { FieldDescriptor } from '../index_patterns_fetcher'; import { mergeJobConfigurations } from './jobs_compatibility'; +/** + * A record of capabilities (aggregations) for an index rollup job + */ export interface RollupIndexCapability { - [key: string]: { aggs?: Record; error?: string }; + [key: string]: { aggs?: Dictionary; error?: string }; } /** @@ -18,7 +23,6 @@ export interface RollupIndexCapability { * @public * @param indices rollup job index capabilites */ - export function getCapabilitiesForRollupIndices( indices: RollupGetRollupIndexCapsResponse ): RollupIndexCapability { From 524027e3fe5cb664f4490f7ba1cfa14c951ba5b0 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 10:45:44 -0700 Subject: [PATCH 20/26] simplify shortenDottedString --- src/plugins/data_views/common/fields/utils.test.ts | 6 +++--- src/plugins/data_views/common/fields/utils.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_views/common/fields/utils.test.ts b/src/plugins/data_views/common/fields/utils.test.ts index 0f2ff280eb61b8..e7d84f4dc6f0a3 100644 --- a/src/plugins/data_views/common/fields/utils.test.ts +++ b/src/plugins/data_views/common/fields/utils.test.ts @@ -16,8 +16,8 @@ describe('shortenDottedString', () => { test('should ignore non-string values', () => { const obj = { key: 'val' }; - expect(shortenDottedString(true)).toBe(true); - expect(shortenDottedString(123)).toBe(123); - expect(shortenDottedString(obj)).toBe(obj); + expect(shortenDottedString(true as unknown as string)).toBe(true); + expect(shortenDottedString(123 as unknown as string)).toBe(123); + expect(shortenDottedString(obj as unknown as string)).toBe(obj); }); }); diff --git a/src/plugins/data_views/common/fields/utils.ts b/src/plugins/data_views/common/fields/utils.ts index c42b6d23d5ad26..1b3be37e23f48a 100644 --- a/src/plugins/data_views/common/fields/utils.ts +++ b/src/plugins/data_views/common/fields/utils.ts @@ -31,7 +31,7 @@ const DOT_PREFIX_RE = /(.).+?\./g; * Convert a dot.notated.string into a short * version (d.n.string) */ -export function shortenDottedString(input: unknown): string { +export function shortenDottedString(input: string): string { return typeof input !== 'string' ? (input as string) : input.replace(DOT_PREFIX_RE, '$1.'); } From b7a43ec189c8c6bcdf905a4f7ae38c5022ef11c3 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 11:50:20 -0700 Subject: [PATCH 21/26] fix jest test --- .../server/fetcher/lib/jobs_compatibility.ts | 51 +++++++++---------- .../server/fetcher/lib/map_capabilities.ts | 4 +- 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index c0267b18875adb..bb8900198fbf54 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -6,12 +6,10 @@ * Side Public License, v 1. */ -import { - RollupGetRollupIndexCapsRollupJobSummary as RollupJobSummary, - RollupGetRollupIndexCapsRollupJobSummaryField as RollupJobSummaryField, -} from '@elastic/elasticsearch/lib/api/types'; -import { Dictionary, get, isEqual, set } from 'lodash'; +import { RollupGetRollupIndexCapsRollupJobSummary as RollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; +import { isEqual } from 'lodash'; import { FieldDescriptor } from '../index_patterns_fetcher'; +import { RollupIndexCapability } from './map_capabilities'; /** * Checks if given job configs are compatible by attempting to merge them @@ -37,16 +35,16 @@ export function areJobsCompatible(jobs = []) { * by aggregation, then by field * * @param jobs - * @returns {{ aggs?: Dictionary }} + * @returns {{ aggs: Dictionary }} */ -export function mergeJobConfigurations(jobs: RollupJobSummary[] = []): { - aggs?: Dictionary; -} { +export function mergeJobConfigurations( + jobs: RollupJobSummary[] = [] +): RollupIndexCapability[string] { if (!jobs || !Array.isArray(jobs) || !jobs.length) { throw new Error('No capabilities available'); } - const allAggs: Dictionary = {}; + const allAggs: RollupIndexCapability[string]['aggs'] = {}; // For each job, look through all of its fields jobs.forEach((job) => { @@ -55,41 +53,42 @@ export function mergeJobConfigurations(jobs: RollupJobSummary[] = []): { // Check each field fieldNames.forEach((fieldName) => { - const typedFieldName = fieldName as keyof FieldDescriptor; const fieldAggs = fields[fieldName]; // Look through each field's capabilities (aggregations) fieldAggs.forEach((agg) => { const aggName = agg.agg; - const aggregation = allAggs[aggName]; - const aggDoesntExist = !aggregation; - const aggregationField = aggregation[typedFieldName]; - const fieldDoesntExist = aggregation && !aggregationField; + const fieldDescriptor = allAggs[aggName]; + const aggDoesntExist = !fieldDescriptor; + const fieldDoesntExist = + fieldDescriptor && !fieldDescriptor[fieldName as keyof FieldDescriptor]; const isDateHistogramAgg = aggName === 'date_histogram'; // If we currently don't have this aggregation, add it. // Special case for date histogram, since there can only be one // date histogram field. if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { - allAggs[aggName] = aggregation || {}; - (aggregation[typedFieldName] as RollupJobSummaryField) = { ...agg }; + allAggs[aggName] = fieldDescriptor || {}; + allAggs[aggName][fieldName] = { ...agg }; } // If aggregation already exists, attempt to merge it else { - const fieldAgg = aggregationField as object | null; + const fieldAgg = fieldDescriptor[fieldName as keyof FieldDescriptor]; + switch (aggName) { // For histograms, calculate the least common multiple between the // new interval and existing interval case 'histogram': - if (fieldAgg) { - // FIXME the interface infers only that calendar_interval property is valid - const aggInterval = (agg as any).interval ?? agg.calendar_interval; + // FIXME the interface infers `calendar_interval` is valid, not `interval` + // @ts-expect-error + const aggInterval: number = agg.interval ?? agg.calendar_interval; + // @ts-expect-error + const fieldAggInterval: number = fieldAgg.interval ?? fieldAgg.calendar_interval; - // TODO: Fix this with LCD algorithm - const intervals = [get(fieldAgg, 'interval'), aggInterval].sort((a, b) => a - b); - const isMultiple = intervals[1] % intervals[0] === 0; - set(fieldAgg, 'interval', isMultiple ? intervals[1] : intervals[0] * intervals[1]); - } + // TODO: Fix this with LCD algorithm + const intervals = [fieldAggInterval, aggInterval].sort((a, b) => a - b); + const isMultiple = intervals[1] % intervals[0] === 0; + fieldAgg.interval = isMultiple ? intervals[1] : intervals[0] * intervals[1]; break; // For date histograms, if it is on the same field, check that the configuration is identical, diff --git a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts index ff5951f9c95908..8ad5e7c535a2a7 100644 --- a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts @@ -7,15 +7,15 @@ */ import { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; +import { SerializableRecord } from '@kbn/utility-types'; import { Dictionary } from 'lodash'; -import { FieldDescriptor } from '../index_patterns_fetcher'; import { mergeJobConfigurations } from './jobs_compatibility'; /** * A record of capabilities (aggregations) for an index rollup job */ export interface RollupIndexCapability { - [key: string]: { aggs?: Dictionary; error?: string }; + [index: string]: { aggs?: Dictionary>; error?: string }; } /** From 90b995c6d5e55bfa0d4e0a166c5087f4310d54ed Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 16:30:47 -0700 Subject: [PATCH 22/26] use AggregationRestrictions --- src/plugins/data_views/common/types.ts | 6 +++--- .../server/fetcher/lib/jobs_compatibility.ts | 20 ++++++------------- .../server/fetcher/lib/map_capabilities.ts | 4 ++-- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 424c3f8e02e450..41c7d6f7a4337c 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { QueryDslQueryContainer, Time } from '@elastic/elasticsearch/lib/api/types'; import type { SavedObject, SavedObjectsCreateOptions, @@ -326,9 +326,9 @@ export type AggregationRestrictions = Record< string, { agg?: string; - interval?: number; fixed_interval?: string; - calendar_interval?: string; + interval?: Time; + calendar_interval?: Time; delay?: string; time_zone?: string; } diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index bb8900198fbf54..09797930a5bd7b 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -8,7 +8,6 @@ import { RollupGetRollupIndexCapsRollupJobSummary as RollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; import { isEqual } from 'lodash'; -import { FieldDescriptor } from '../index_patterns_fetcher'; import { RollupIndexCapability } from './map_capabilities'; /** @@ -58,35 +57,28 @@ export function mergeJobConfigurations( // Look through each field's capabilities (aggregations) fieldAggs.forEach((agg) => { const aggName = agg.agg; - const fieldDescriptor = allAggs[aggName]; - const aggDoesntExist = !fieldDescriptor; - const fieldDoesntExist = - fieldDescriptor && !fieldDescriptor[fieldName as keyof FieldDescriptor]; + const aggDoesntExist = !allAggs[aggName]; + const fieldDoesntExist = allAggs[aggName] && !allAggs[aggName][fieldName]; const isDateHistogramAgg = aggName === 'date_histogram'; // If we currently don't have this aggregation, add it. // Special case for date histogram, since there can only be one // date histogram field. if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { - allAggs[aggName] = fieldDescriptor || {}; + allAggs[aggName] = allAggs[aggName] || {}; allAggs[aggName][fieldName] = { ...agg }; } // If aggregation already exists, attempt to merge it else { - const fieldAgg = fieldDescriptor[fieldName as keyof FieldDescriptor]; + const fieldAgg = allAggs[aggName][fieldName]; switch (aggName) { // For histograms, calculate the least common multiple between the // new interval and existing interval case 'histogram': - // FIXME the interface infers `calendar_interval` is valid, not `interval` - // @ts-expect-error - const aggInterval: number = agg.interval ?? agg.calendar_interval; - // @ts-expect-error - const fieldAggInterval: number = fieldAgg.interval ?? fieldAgg.calendar_interval; - // TODO: Fix this with LCD algorithm - const intervals = [fieldAggInterval, aggInterval].sort((a, b) => a - b); + // @ts-expect-error - Property 'interval' does not exist on type 'RollupGetRollupIndexCapsRollupJobSummaryField' + const intervals = [fieldAgg.interval, agg.interval].sort((a, b) => a - b); const isMultiple = intervals[1] % intervals[0] === 0; fieldAgg.interval = isMultiple ? intervals[1] : intervals[0] * intervals[1]; break; diff --git a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts index 8ad5e7c535a2a7..147272b74284dc 100644 --- a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts @@ -7,15 +7,15 @@ */ import { RollupGetRollupIndexCapsResponse } from '@elastic/elasticsearch/lib/api/types'; -import { SerializableRecord } from '@kbn/utility-types'; import { Dictionary } from 'lodash'; +import { AggregationRestrictions } from '../../../common'; import { mergeJobConfigurations } from './jobs_compatibility'; /** * A record of capabilities (aggregations) for an index rollup job */ export interface RollupIndexCapability { - [index: string]: { aggs?: Dictionary>; error?: string }; + [index: string]: { aggs?: Dictionary; error?: string }; } /** From 275e7a3d81804ab3a52e38c416af98fd55b2cea7 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 16:41:16 -0700 Subject: [PATCH 23/26] fix downstream types --- src/plugins/data_views/common/types.ts | 6 +++--- .../data_views/server/fetcher/lib/jobs_compatibility.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_views/common/types.ts b/src/plugins/data_views/common/types.ts index 41c7d6f7a4337c..40a70e3c1f5470 100644 --- a/src/plugins/data_views/common/types.ts +++ b/src/plugins/data_views/common/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import type { QueryDslQueryContainer, Time } from '@elastic/elasticsearch/lib/api/types'; +import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import type { SavedObject, SavedObjectsCreateOptions, @@ -326,9 +326,9 @@ export type AggregationRestrictions = Record< string, { agg?: string; + interval?: number; fixed_interval?: string; - interval?: Time; - calendar_interval?: Time; + calendar_interval?: string; delay?: string; time_zone?: string; } diff --git a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts index 09797930a5bd7b..2c104b03cf5c14 100644 --- a/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts +++ b/src/plugins/data_views/server/fetcher/lib/jobs_compatibility.ts @@ -8,6 +8,7 @@ import { RollupGetRollupIndexCapsRollupJobSummary as RollupJobSummary } from '@elastic/elasticsearch/lib/api/types'; import { isEqual } from 'lodash'; +import { AggregationRestrictions } from '../../../common'; import { RollupIndexCapability } from './map_capabilities'; /** @@ -66,7 +67,7 @@ export function mergeJobConfigurations( // date histogram field. if (aggDoesntExist || (fieldDoesntExist && !isDateHistogramAgg)) { allAggs[aggName] = allAggs[aggName] || {}; - allAggs[aggName][fieldName] = { ...agg }; + allAggs[aggName][fieldName] = { ...agg } as AggregationRestrictions[string]; } // If aggregation already exists, attempt to merge it else { From 35c4dcffe85fdcffeb16bd0ea90d4cce7710db25 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 17:04:02 -0700 Subject: [PATCH 24/26] simplify --- src/plugins/data_views/common/fields/utils.ts | 2 +- src/plugins/data_views/public/debounce_by_key.ts | 13 ++++++++++++- src/plugins/data_views/public/plugin.ts | 4 ++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/plugins/data_views/common/fields/utils.ts b/src/plugins/data_views/common/fields/utils.ts index 1b3be37e23f48a..a6418bec744f0f 100644 --- a/src/plugins/data_views/common/fields/utils.ts +++ b/src/plugins/data_views/common/fields/utils.ts @@ -32,7 +32,7 @@ const DOT_PREFIX_RE = /(.).+?\./g; * version (d.n.string) */ export function shortenDottedString(input: string): string { - return typeof input !== 'string' ? (input as string) : input.replace(DOT_PREFIX_RE, '$1.'); + return typeof input !== 'string' ? input : input.replace(DOT_PREFIX_RE, '$1.'); } // Note - this code is duplicated from @kbn/es-query diff --git a/src/plugins/data_views/public/debounce_by_key.ts b/src/plugins/data_views/public/debounce_by_key.ts index 83b4715d96d8c7..402fd5baf9fc78 100644 --- a/src/plugins/data_views/public/debounce_by_key.ts +++ b/src/plugins/data_views/public/debounce_by_key.ts @@ -8,7 +8,18 @@ import { debounce } from 'lodash'; -export const debounceByKey = unknown>(fn: F, waitInMs: number) => { +/** + * Uses a debouncer collector behind a debouncing factory to work on a set of functions + * + * @template F - function type + * @param {F} fn - function to debounce + * @param {number} waitInMs + * @returns {(key: string) => Function} + */ +export const debounceByKey = unknown>( + fn: F, + waitInMs: number +): ((key: string) => Function) => { const debouncerCollector: Record = {}; return (key: string) => { if (!debouncerCollector[key]) { diff --git a/src/plugins/data_views/public/plugin.ts b/src/plugins/data_views/public/plugin.ts index ee37830da6143b..df379ab18964ea 100644 --- a/src/plugins/data_views/public/plugin.ts +++ b/src/plugins/data_views/public/plugin.ts @@ -51,11 +51,11 @@ export class DataViewsPublicPlugin ): DataViewsPublicPluginStart { const { uiSettings, http, notifications, savedObjects, application } = core; - const onNotifDebounced = debounceByKey( + const onNotifDebounced = debounceByKey( notifications.toasts.add.bind(notifications.toasts), 10000 ); - const onErrorDebounced = debounceByKey( + const onErrorDebounced = debounceByKey( notifications.toasts.addError.bind(notifications.toasts), 10000 ); From 250d0345af73a70e6fbb799b0fcd11c7e783c429 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Thu, 14 Jul 2022 17:48:22 -0700 Subject: [PATCH 25/26] add comments for public api items --- src/plugins/data_views/common/data_views/data_views.ts | 4 ++++ .../data_views/server/fetcher/lib/map_capabilities.ts | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index 1a3ad60a20d63e..258a02c8dcdf31 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -38,6 +38,10 @@ import { DuplicateDataViewError, DataViewInsufficientAccessError } from '../erro const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; +/* + * Attributes of the data view saved object + * @public + */ export type DataViewSavedObjectAttrs = Pick< DataViewAttributes, 'title' | 'type' | 'typeMeta' | 'name' diff --git a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts index 147272b74284dc..a5fdf98bd03e9d 100644 --- a/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts +++ b/src/plugins/data_views/server/fetcher/lib/map_capabilities.ts @@ -12,9 +12,12 @@ import { AggregationRestrictions } from '../../../common'; import { mergeJobConfigurations } from './jobs_compatibility'; /** - * A record of capabilities (aggregations) for an index rollup job + * A record of capabilities (aggregations) for index rollup jobs */ export interface RollupIndexCapability { + /** + * A record of capabilities (aggregations) for an index rollup job + */ [index: string]: { aggs?: Dictionary; error?: string }; } From 9db36901ab5754225af002a9164212710aa1a5a4 Mon Sep 17 00:00:00 2001 From: Timothy Sullivan Date: Fri, 15 Jul 2022 13:53:11 -0700 Subject: [PATCH 26/26] add ts-expect-error in lens integration toJSON --- x-pack/plugins/lens/public/indexpattern_datasource/loader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 901153aa330552..6118c3ea9996dd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -7,7 +7,6 @@ import { uniq, mapValues, difference } from 'lodash'; import type { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; -import type { FieldFormat } from '@kbn/field-formats-plugin/common'; import type { HttpSetup, SavedObjectReference } from '@kbn/core/public'; import type { DataViewsContract, DataView } from '@kbn/data-views-plugin/public'; import { isNestedField } from '@kbn/data-views-plugin/common'; @@ -93,7 +92,8 @@ export function convertDataViewIntoLensIndexPattern(dataView: DataView): IndexPa Object.fromEntries( Object.entries(fieldFormatMap).map(([id, format]) => [ id, - 'toJSON' in format ? (format as unknown as FieldFormat).toJSON() : format, // FIXME SerializedFieldFormat was inferred... was this intended to work with FieldFormat instead? + // @ts-expect-error FIXME Property 'toJSON' does not exist on type 'SerializedFieldFormat' + 'toJSON' in format ? format.toJSON() : format, ]) ), fields: newFields,