diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index ac21954118e50..c15aa8f414fb1 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -5,9 +5,11 @@ * 2.0. */ -export { HitsTotalRelation, SearchResponse7, HITS_TOTAL_RELATION } from './types/es_client'; +export { ES_CLIENT_TOTAL_HITS_RELATION } from './types/es_client'; export { ChartData } from './types/field_histograms'; export { ANOMALY_SEVERITY, ANOMALY_THRESHOLD, SEVERITY_COLORS } from './constants/anomalies'; export { getSeverityColor, getSeverityType } from './util/anomaly_utils'; +export { isPopulatedObject } from './util/object_utils'; +export { isRuntimeMappings } from './util/runtime_field_utils'; export { composeValidators, patternValidator } from './util/validators'; export { extractErrorMessage } from './util/errors'; diff --git a/x-pack/plugins/ml/common/types/es_client.ts b/x-pack/plugins/ml/common/types/es_client.ts index f6db736db2519..249b3c150a082 100644 --- a/x-pack/plugins/ml/common/types/es_client.ts +++ b/x-pack/plugins/ml/common/types/es_client.ts @@ -5,33 +5,24 @@ * 2.0. */ -import type { SearchResponse, ShardsResponse } from 'elasticsearch'; +import { estypes } from '@elastic/elasticsearch'; + import { buildEsQuery } from '../../../../../src/plugins/data/common/es_query/es_query'; import type { DslQuery } from '../../../../../src/plugins/data/common/es_query/kuery'; import type { JsonObject } from '../../../../../src/plugins/kibana_utils/common'; -export const HITS_TOTAL_RELATION = { +import { isPopulatedObject } from '../util/object_utils'; + +export function isMultiBucketAggregate(arg: unknown): arg is estypes.MultiBucketAggregate { + return isPopulatedObject(arg, ['buckets']); +} + +export const ES_CLIENT_TOTAL_HITS_RELATION: Record< + Uppercase, + estypes.TotalHitsRelation +> = { EQ: 'eq', GTE: 'gte', } as const; -export type HitsTotalRelation = typeof HITS_TOTAL_RELATION[keyof typeof HITS_TOTAL_RELATION]; - -// The types specified in `@types/elasticsearch` are out of date and still have `total: number`. -interface SearchResponse7Hits { - hits: SearchResponse['hits']['hits']; - max_score: number; - total: { - value: number; - relation: HitsTotalRelation; - }; -} -export interface SearchResponse7 { - took: number; - timed_out: boolean; - _scroll_id?: string; - _shards: ShardsResponse; - hits: SearchResponse7Hits; - aggregations?: any; -} export type InfluencersFilterQuery = ReturnType | DslQuery | JsonObject; diff --git a/x-pack/plugins/ml/common/types/feature_importance.ts b/x-pack/plugins/ml/common/types/feature_importance.ts index 964ce8c325783..111c8432dd439 100644 --- a/x-pack/plugins/ml/common/types/feature_importance.ts +++ b/x-pack/plugins/ml/common/types/feature_importance.ts @@ -88,15 +88,11 @@ export function isRegressionTotalFeatureImportance( export function isClassificationFeatureImportanceBaseline( baselineData: any ): baselineData is ClassificationFeatureImportanceBaseline { - return ( - isPopulatedObject(baselineData) && - baselineData.hasOwnProperty('classes') && - Array.isArray(baselineData.classes) - ); + return isPopulatedObject(baselineData, ['classes']) && Array.isArray(baselineData.classes); } export function isRegressionFeatureImportanceBaseline( baselineData: any ): baselineData is RegressionFeatureImportanceBaseline { - return isPopulatedObject(baselineData) && baselineData.hasOwnProperty('baseline'); + return isPopulatedObject(baselineData, ['baseline']); } diff --git a/x-pack/plugins/ml/common/util/object_utils.test.ts b/x-pack/plugins/ml/common/util/object_utils.test.ts new file mode 100644 index 0000000000000..8e4196ed4d826 --- /dev/null +++ b/x-pack/plugins/ml/common/util/object_utils.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isPopulatedObject } from './object_utils'; + +describe('object_utils', () => { + describe('isPopulatedObject()', () => { + it('does not allow numbers', () => { + expect(isPopulatedObject(0)).toBe(false); + }); + it('does not allow strings', () => { + expect(isPopulatedObject('')).toBe(false); + }); + it('does not allow null', () => { + expect(isPopulatedObject(null)).toBe(false); + }); + it('does not allow an empty object', () => { + expect(isPopulatedObject({})).toBe(false); + }); + it('allows an object with an attribute', () => { + expect(isPopulatedObject({ attribute: 'value' })).toBe(true); + }); + it('does not allow an object with a non-existing required attribute', () => { + expect(isPopulatedObject({ attribute: 'value' }, ['otherAttribute'])).toBe(false); + }); + it('allows an object with an existing required attribute', () => { + expect(isPopulatedObject({ attribute: 'value' }, ['attribute'])).toBe(true); + }); + it('allows an object with two existing required attributes', () => { + expect( + isPopulatedObject({ attribute1: 'value1', attribute2: 'value2' }, [ + 'attribute1', + 'attribute2', + ]) + ).toBe(true); + }); + it('does not allow an object with two required attributes where one does not exist', () => { + expect( + isPopulatedObject({ attribute1: 'value1', attribute2: 'value2' }, [ + 'attribute1', + 'otherAttribute', + ]) + ).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/ml/common/util/object_utils.ts b/x-pack/plugins/ml/common/util/object_utils.ts index 4bbd0c1c2810f..537ee9202b4de 100644 --- a/x-pack/plugins/ml/common/util/object_utils.ts +++ b/x-pack/plugins/ml/common/util/object_utils.ts @@ -5,6 +5,32 @@ * 2.0. */ -export const isPopulatedObject = >(arg: any): arg is T => { - return typeof arg === 'object' && arg !== null && Object.keys(arg).length > 0; +/* + * A type guard to check record like object structures. + * + * Examples: + * - `isPopulatedObject({...})` + * Limits type to Record + * + * - `isPopulatedObject({...}, ['attribute'])` + * Limits type to Record<'attribute', unknown> + * + * - `isPopulatedObject({...})` + * Limits type to a record with keys of the given interface. + * Note that you might want to add keys from the interface to the + * array of requiredAttributes to satisfy runtime requirements. + * Otherwise you'd just satisfy TS requirements but might still + * run into runtime issues. + */ +export const isPopulatedObject = ( + arg: unknown, + requiredAttributes: U[] = [] +): arg is Record => { + return ( + typeof arg === 'object' && + arg !== null && + Object.keys(arg).length > 0 && + (requiredAttributes.length === 0 || + requiredAttributes.every((d) => ({}.hasOwnProperty.call(arg, d)))) + ); }; diff --git a/x-pack/plugins/ml/common/util/runtime_field_utils.test.ts b/x-pack/plugins/ml/common/util/runtime_field_utils.test.ts new file mode 100644 index 0000000000000..1b5e3e18b14f6 --- /dev/null +++ b/x-pack/plugins/ml/common/util/runtime_field_utils.test.ts @@ -0,0 +1,102 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isRuntimeField, isRuntimeMappings } from './runtime_field_utils'; + +describe('ML runtime field utils', () => { + describe('isRuntimeField()', () => { + it('does not allow numbers', () => { + expect(isRuntimeField(1)).toBe(false); + }); + it('does not allow null', () => { + expect(isRuntimeField(null)).toBe(false); + }); + it('does not allow arrays', () => { + expect(isRuntimeField([])).toBe(false); + }); + it('does not allow empty objects', () => { + expect(isRuntimeField({})).toBe(false); + }); + it('does not allow objects with non-matching attributes', () => { + expect(isRuntimeField({ someAttribute: 'someValue' })).toBe(false); + expect(isRuntimeField({ type: 'wrong-type' })).toBe(false); + expect(isRuntimeField({ type: 'keyword', someAttribute: 'some value' })).toBe(false); + }); + it('allows objects with type attribute only', () => { + expect(isRuntimeField({ type: 'keyword' })).toBe(true); + }); + it('allows objects with both type and script attributes', () => { + expect(isRuntimeField({ type: 'keyword', script: 'some script' })).toBe(true); + }); + }); + + describe('isRuntimeMappings()', () => { + it('does not allow numbers', () => { + expect(isRuntimeMappings(1)).toBe(false); + }); + it('does not allow null', () => { + expect(isRuntimeMappings(null)).toBe(false); + }); + it('does not allow arrays', () => { + expect(isRuntimeMappings([])).toBe(false); + }); + it('does not allow empty objects', () => { + expect(isRuntimeMappings({})).toBe(false); + }); + it('does not allow objects with non-object inner structure', () => { + expect(isRuntimeMappings({ someAttribute: 'someValue' })).toBe(false); + }); + it('does not allow objects with objects with unsupported inner structure', () => { + expect(isRuntimeMappings({ fieldName1: { type: 'keyword' }, fieldName2: 'someValue' })).toBe( + false + ); + expect( + isRuntimeMappings({ + fieldName1: { type: 'keyword' }, + fieldName2: { type: 'keyword', someAttribute: 'some value' }, + }) + ).toBe(false); + expect( + isRuntimeMappings({ + fieldName: { type: 'long', script: 1234 }, + }) + ).toBe(false); + expect( + isRuntimeMappings({ + fieldName: { type: 'long', script: { someAttribute: 'some value' } }, + }) + ).toBe(false); + expect( + isRuntimeMappings({ + fieldName: { type: 'long', script: { source: 1234 } }, + }) + ).toBe(false); + }); + + it('allows object with most basic runtime mapping', () => { + expect(isRuntimeMappings({ fieldName: { type: 'keyword' } })).toBe(true); + }); + it('allows object with multiple most basic runtime mappings', () => { + expect( + isRuntimeMappings({ fieldName1: { type: 'keyword' }, fieldName2: { type: 'keyword' } }) + ).toBe(true); + }); + it('allows object with runtime mappings including scripts', () => { + expect( + isRuntimeMappings({ + fieldName1: { type: 'keyword' }, + fieldName2: { type: 'keyword', script: 'some script as script' }, + }) + ).toBe(true); + expect( + isRuntimeMappings({ + fieldName: { type: 'long', script: { source: 'some script as source' } }, + }) + ).toBe(true); + }); + }); +}); diff --git a/x-pack/plugins/ml/common/util/runtime_field_utils.ts b/x-pack/plugins/ml/common/util/runtime_field_utils.ts index 06a1960cc943b..60bfc43cbcf06 100644 --- a/x-pack/plugins/ml/common/util/runtime_field_utils.ts +++ b/x-pack/plugins/ml/common/util/runtime_field_utils.ts @@ -9,19 +9,18 @@ import { isPopulatedObject } from './object_utils'; import { RUNTIME_FIELD_TYPES } from '../../../../../src/plugins/data/common/index_patterns'; import type { RuntimeField, RuntimeMappings } from '../types/fields'; +type RuntimeType = typeof RUNTIME_FIELD_TYPES[number]; + export function isRuntimeField(arg: unknown): arg is RuntimeField { return ( - isPopulatedObject(arg) && - ((Object.keys(arg).length === 1 && arg.hasOwnProperty('type')) || - (Object.keys(arg).length === 2 && - arg.hasOwnProperty('type') && - arg.hasOwnProperty('script') && + ((isPopulatedObject(arg, ['type']) && Object.keys(arg).length === 1) || + (isPopulatedObject(arg, ['type', 'script']) && + Object.keys(arg).length === 2 && (typeof arg.script === 'string' || - (isPopulatedObject(arg.script) && + (isPopulatedObject(arg.script, ['source']) && Object.keys(arg.script).length === 1 && - arg.script.hasOwnProperty('source') && typeof arg.script.source === 'string')))) && - RUNTIME_FIELD_TYPES.includes(arg.type) + RUNTIME_FIELD_TYPES.includes(arg.type as RuntimeType) ); } diff --git a/x-pack/plugins/ml/public/application/components/data_grid/types.ts b/x-pack/plugins/ml/public/application/components/data_grid/types.ts index 649968f176e18..0af8972f18558 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/types.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/types.ts @@ -7,6 +7,7 @@ import { Dispatch, SetStateAction } from 'react'; +import { estypes } from '@elastic/elasticsearch'; import { EuiDataGridCellValueElementProps, EuiDataGridPaginationProps, @@ -15,7 +16,6 @@ import { } from '@elastic/eui'; import { Dictionary } from '../../../../common/types/common'; -import { HitsTotalRelation } from '../../../../common/types/es_client'; import { ChartData } from '../../../../common/types/field_histograms'; import { INDEX_STATUS } from '../../data_frame_analytics/common/analytics'; @@ -27,7 +27,7 @@ export type DataGridItem = Record; // `undefined` is used to indicate a non-initialized state. export type ChartsVisible = boolean | undefined; -export type RowCountRelation = HitsTotalRelation | undefined; +export type RowCountRelation = estypes.TotalHitsRelation | undefined; export type IndexPagination = Pick; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx index 31a72a776223e..e62f2eb2f003b 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_data_grid.tsx @@ -9,7 +9,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { EuiDataGridSorting, EuiDataGridColumn } from '@elastic/eui'; -import { HITS_TOTAL_RELATION } from '../../../../common/types/es_client'; +import { ES_CLIENT_TOTAL_HITS_RELATION } from '../../../../common/types/es_client'; import { ChartData } from '../../../../common/types/field_histograms'; import { INDEX_STATUS } from '../../data_frame_analytics/common'; @@ -146,7 +146,7 @@ export const useDataGrid = ( if (chartsVisible === undefined && rowCount > 0 && rowCountRelation !== undefined) { setChartsVisible( rowCount <= COLUMN_CHART_DEFAULT_VISIBILITY_ROWS_THRESHOLED && - rowCountRelation !== HITS_TOTAL_RELATION.GTE + rowCountRelation !== ES_CLIENT_TOTAL_HITS_RELATION.GTE ); } }, [chartsVisible, rowCount, rowCountRelation]); diff --git a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx index 4e9fd3baebe7b..842d5fc1ae87a 100644 --- a/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx +++ b/x-pack/plugins/ml/public/application/components/scatterplot_matrix/scatterplot_matrix.tsx @@ -7,6 +7,8 @@ import React, { useMemo, useEffect, useState, FC } from 'react'; +import { estypes } from '@elastic/elasticsearch'; + import { EuiCallOut, EuiComboBox, @@ -24,7 +26,6 @@ import { i18n } from '@kbn/i18n'; import { extractErrorMessage } from '../../../../common'; import { stringHash } from '../../../../common/util/string_utils'; -import type { SearchResponse7 } from '../../../../common/types/es_client'; import type { ResultsSearchQuery } from '../../data_frame_analytics/common/analytics'; import { useMlApiContext } from '../../contexts/kibana'; @@ -184,7 +185,7 @@ export const ScatterplotMatrix: FC = ({ } : searchQuery; - const resp: SearchResponse7 = await esSearch({ + const resp: estypes.SearchResponse = await esSearch({ index, body: { fields: queryFields, @@ -198,7 +199,7 @@ export const ScatterplotMatrix: FC = ({ if (!options.didCancel) { const items = resp.hits.hits .map((d) => - getProcessedFields(d.fields, (key: string) => + getProcessedFields(d.fields ?? {}, (key: string) => key.startsWith(`${resultsField}.feature_importance`) ) ) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts index d0bcbd2ff63b4..88f403cdf0c44 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/get_index_data.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { SearchResponse7 } from '../../../../common/types/es_client'; +import type { estypes } from '@elastic/elasticsearch'; import { extractErrorMessage } from '../../../../common/util/errors'; import { EsSorting, UseDataGridReturnType, getProcessedFields } from '../../components/data_grid'; @@ -51,7 +51,7 @@ export const getIndexData = async ( const { pageIndex, pageSize } = pagination; // TODO: remove results_field from `fields` when possible - const resp: SearchResponse7 = await ml.esSearch({ + const resp: estypes.SearchResponse = await ml.esSearch({ index: jobConfig.dest.index, body: { fields: ['*'], @@ -64,11 +64,15 @@ export const getIndexData = async ( }); if (!options.didCancel) { - setRowCount(resp.hits.total.value); - setRowCountRelation(resp.hits.total.relation); + setRowCount(typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total.value); + setRowCountRelation( + typeof resp.hits.total === 'number' + ? ('eq' as estypes.TotalHitsRelation) + : resp.hits.total.relation + ); setTableItems( resp.hits.hits.map((d) => - getProcessedFields(d.fields, (key: string) => + getProcessedFields(d.fields ?? {}, (key: string) => key.startsWith(`${jobConfig.dest.results_field}.feature_importance`) ) ) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts index ecda624c71d98..4552ca34ebbae 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/hooks/use_index_data.ts @@ -7,6 +7,7 @@ import { useEffect, useMemo } from 'react'; +import { estypes } from '@elastic/elasticsearch'; import { EuiDataGridColumn } from '@elastic/eui'; import { CoreSetup } from 'src/core/public'; @@ -26,13 +27,12 @@ import { UseIndexDataReturnType, getProcessedFields, } from '../../../../components/data_grid'; -import type { SearchResponse7 } from '../../../../../../common/types/es_client'; import { extractErrorMessage } from '../../../../../../common/util/errors'; import { INDEX_STATUS } from '../../../common/analytics'; import { ml } from '../../../../services/ml_api_service'; import { getRuntimeFieldsMapping } from '../../../../components/data_grid/common'; -type IndexSearchResponse = SearchResponse7; +type IndexSearchResponse = estypes.SearchResponse; export const useIndexData = ( indexPattern: IndexPattern, @@ -95,9 +95,13 @@ export const useIndexData = ( try { const resp: IndexSearchResponse = await ml.esSearch(esSearchRequest); - const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields)); - setRowCount(resp.hits.total.value); - setRowCountRelation(resp.hits.total.relation); + const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); + setRowCount(typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total.value); + setRowCountRelation( + typeof resp.hits.total === 'number' + ? ('eq' as estypes.TotalHitsRelation) + : resp.hits.total.relation + ); setTableItems(docs); setStatus(INDEX_STATUS.LOADED); } catch (e) { diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx index 654af03d102e5..d67473d9d3220 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section_results.tsx @@ -18,7 +18,7 @@ import { isClassificationAnalysis, isRegressionAnalysis, } from '../../../../../../../common/util/analytics_utils'; -import { HITS_TOTAL_RELATION } from '../../../../../../../common/types/es_client'; +import { ES_CLIENT_TOTAL_HITS_RELATION } from '../../../../../../../common/types/es_client'; import { getToastNotifications } from '../../../../../util/dependency_cache'; import { useColorRange, ColorRangeLegend } from '../../../../../components/color_range_legend'; @@ -77,7 +77,7 @@ const getResultsSectionHeaderItems = ( defaultMessage="Total docs" /> ), - value: `${rowCountRelation === HITS_TOTAL_RELATION.GTE ? '>' : ''}${rowCount}`, + value: `${rowCountRelation === ES_CLIENT_TOTAL_HITS_RELATION.GTE ? '>' : ''}${rowCount}`, }, ...(colorRange !== undefined ? [ diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts index bfed2d811e206..5995224ef3254 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/filter_runtime_mappings.ts @@ -22,8 +22,10 @@ interface Response { export function filterRuntimeMappings(job: Job, datafeed: Datafeed): Response { if ( - datafeed.runtime_mappings === undefined || - isPopulatedObject(datafeed.runtime_mappings) === false + !( + isPopulatedObject(datafeed, ['runtime_mappings']) && + isPopulatedObject(datafeed.runtime_mappings) + ) ) { return { runtime_mappings: {}, @@ -83,7 +85,7 @@ function findFieldsInJob(job: Job, datafeed: Datafeed) { return [...usedFields]; } -function findFieldsInAgg(obj: Record) { +function findFieldsInAgg(obj: Record) { const fields: string[] = []; Object.entries(obj).forEach(([key, val]) => { if (isPopulatedObject(val)) { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/datafeed_preview_flyout/datafeed_preview.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/datafeed_preview_flyout/datafeed_preview.tsx index 916a25271c63b..a4d9293e9369d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/datafeed_preview_flyout/datafeed_preview.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/common/datafeed_preview_flyout/datafeed_preview.tsx @@ -22,6 +22,7 @@ import { MLJobEditor } from '../../../../../jobs_list/components/ml_job_editor'; import { mlJobService } from '../../../../../../services/job_service'; import { ML_DATA_PREVIEW_COUNT } from '../../../../../../../../common/util/job_utils'; import { isPopulatedObject } from '../../../../../../../../common/util/object_utils'; +import { isMultiBucketAggregate } from '../../../../../../../../common/types/es_client'; export const DatafeedPreview: FC<{ combinedJob: CombinedJob | null; @@ -67,7 +68,10 @@ export const DatafeedPreview: FC<{ // the first item under aggregations can be any name if (isPopulatedObject(resp.aggregations)) { const accessor = Object.keys(resp.aggregations)[0]; - data = resp.aggregations[accessor].buckets.slice(0, ML_DATA_PREVIEW_COUNT); + const aggregate = resp.aggregations[accessor]; + if (isMultiBucketAggregate(aggregate)) { + data = aggregate.buckets.slice(0, ML_DATA_PREVIEW_COUNT); + } } setPreviewJsonString(JSON.stringify(data, null, 2)); diff --git a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts index 59b6860cb65b7..72de5d003d4b8 100644 --- a/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts +++ b/x-pack/plugins/ml/public/application/services/anomaly_explorer_charts_service.ts @@ -90,11 +90,7 @@ export interface SeriesConfigWithMetadata extends SeriesConfig { } export const isSeriesConfigWithMetadata = (arg: unknown): arg is SeriesConfigWithMetadata => { - return ( - isPopulatedObject(arg) && - {}.hasOwnProperty.call(arg, 'bucketSpanSeconds') && - {}.hasOwnProperty.call(arg, 'detectorLabel') - ); + return isPopulatedObject(arg, ['bucketSpanSeconds', 'detectorLabel']); }; interface ChartRange { diff --git a/x-pack/plugins/ml/public/application/services/job_service.d.ts b/x-pack/plugins/ml/public/application/services/job_service.d.ts index 544d346341591..ceadca12f8757 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.d.ts +++ b/x-pack/plugins/ml/public/application/services/job_service.d.ts @@ -5,7 +5,8 @@ * 2.0. */ -import { SearchResponse } from 'elasticsearch'; +import { estypes } from '@elastic/elasticsearch'; + import { TimeRange } from 'src/plugins/data/common/query/timefilter/types'; import { CombinedJob, Datafeed, Job } from '../../../common/types/anomaly_detection_jobs'; import { Calendar } from '../../../common/types/calendars'; @@ -40,7 +41,7 @@ declare interface JobService { ): Promise; createResultsUrl(jobId: string[], start: number, end: number, location: string): string; getJobAndGroupIds(): Promise; - searchPreview(job: CombinedJob): Promise>; + searchPreview(job: CombinedJob): Promise>; getJob(jobId: string): CombinedJob; loadJobsWrapper(): Promise; } diff --git a/x-pack/plugins/ml/public/embeddables/types.ts b/x-pack/plugins/ml/public/embeddables/types.ts index 05aea1770a415..60355dae5baf4 100644 --- a/x-pack/plugins/ml/public/embeddables/types.ts +++ b/x-pack/plugins/ml/public/embeddables/types.ts @@ -81,8 +81,8 @@ export interface SwimLaneDrilldownContext extends EditSwimlanePanelContext { export function isSwimLaneEmbeddable(arg: unknown): arg is SwimLaneDrilldownContext { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('embeddable') && + isPopulatedObject(arg, ['embeddable']) && + isPopulatedObject(arg.embeddable, ['type']) && arg.embeddable.type === ANOMALY_SWIMLANE_EMBEDDABLE_TYPE ); } @@ -130,8 +130,8 @@ export function isAnomalyExplorerEmbeddable( arg: unknown ): arg is AnomalyChartsFieldSelectionContext { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('embeddable') && + isPopulatedObject(arg, ['embeddable']) && + isPopulatedObject(arg.embeddable, ['type']) && arg.embeddable.type === ANOMALY_EXPLORER_CHARTS_EMBEDDABLE_TYPE ); } diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index 9280f4603b343..56b8ca409ac0b 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -50,7 +50,7 @@ export { getSeverityType, getFormattedSeverityScore, } from '../common/util/anomaly_utils'; -export { HITS_TOTAL_RELATION } from '../common/types/es_client'; +export { ES_CLIENT_TOTAL_HITS_RELATION } from '../common/types/es_client'; export { ANOMALY_SEVERITY } from '../common'; export { useMlHref, ML_PAGES, MlUrlGenerator } from './ml_url_generator'; diff --git a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts index 4c79855f39e89..3f0a02f5eaad8 100644 --- a/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts +++ b/x-pack/plugins/ml/server/models/data_frame_analytics/validation.ts @@ -25,7 +25,6 @@ import { isClassificationAnalysis, } from '../../../common/util/analytics_utils'; import { extractErrorMessage } from '../../../common/util/errors'; -import { SearchResponse7 } from '../../../common'; import { AnalysisConfig, DataFrameAnalyticsConfig, @@ -42,7 +41,7 @@ interface CardinalityAgg { }; } -type ValidationSearchResult = Omit & { +type ValidationSearchResult = Omit & { aggregations: MissingAgg | CardinalityAgg; }; diff --git a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts index b0ee20763f430..5fecb3d9eb1ec 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts @@ -5,9 +5,10 @@ * 2.0. */ +import type { estypes } from '@elastic/elasticsearch'; + import { IScopedClusterClient } from 'kibana/server'; import { chunk } from 'lodash'; -import { SearchResponse } from 'elasticsearch'; import { CATEGORY_EXAMPLES_SAMPLE_SIZE } from '../../../../../common/constants/categorization_job'; import { Token, @@ -61,7 +62,7 @@ export function categorizationExamplesProvider({ } } } - const { body } = await asCurrentUser.search>({ + const { body } = await asCurrentUser.search>({ index: indexPatternTitle, size, body: { diff --git a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/top_categories.ts b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/top_categories.ts index 851336056a7f5..82d6f6ca3e103 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/top_categories.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/top_categories.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { SearchResponse } from 'elasticsearch'; +import type { estypes } from '@elastic/elasticsearch'; + import { CategoryId, Category } from '../../../../../common/types/categories'; import type { MlClient } from '../../../../lib/ml_client'; export function topCategoriesProvider(mlClient: MlClient) { async function getTotalCategories(jobId: string): Promise { - const { body } = await mlClient.anomalySearch>( + const { body } = await mlClient.anomalySearch>( { size: 0, body: { @@ -35,12 +36,11 @@ export function topCategoriesProvider(mlClient: MlClient) { }, [] ); - // @ts-ignore total is an object here - return body?.hits?.total?.value ?? 0; + return typeof body.hits.total === 'number' ? body.hits.total : body.hits.total.value; } async function getTopCategoryCounts(jobId: string, numberOfCategories: number) { - const { body } = await mlClient.anomalySearch>( + const { body } = await mlClient.anomalySearch>( { size: 0, body: { diff --git a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts index 949159b67d33a..64dfb84be8668 100644 --- a/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts +++ b/x-pack/plugins/ml/server/models/job_validation/job_validation.test.ts @@ -8,13 +8,15 @@ import { IScopedClusterClient } from 'kibana/server'; import { validateJob, ValidateJobPayload } from './job_validation'; -import { HITS_TOTAL_RELATION } from '../../../common/types/es_client'; +import { ES_CLIENT_TOTAL_HITS_RELATION } from '../../../common/types/es_client'; import type { MlClient } from '../../lib/ml_client'; const callAs = { fieldCaps: () => Promise.resolve({ body: { fields: [] } }), search: () => - Promise.resolve({ body: { hits: { total: { value: 1, relation: HITS_TOTAL_RELATION.EQ } } } }), + Promise.resolve({ + body: { hits: { total: { value: 1, relation: ES_CLIENT_TOTAL_HITS_RELATION.EQ } } }, + }), }; const mlClusterClient = ({ diff --git a/x-pack/plugins/ml/server/shared_services/providers/system.ts b/x-pack/plugins/ml/server/shared_services/providers/system.ts index 1e3dcd7de5240..85cd73ba010af 100644 --- a/x-pack/plugins/ml/server/shared_services/providers/system.ts +++ b/x-pack/plugins/ml/server/shared_services/providers/system.ts @@ -5,8 +5,9 @@ * 2.0. */ +import type { estypes } from '@elastic/elasticsearch'; + import { KibanaRequest, SavedObjectsClientContract } from 'kibana/server'; -import { SearchResponse } from 'elasticsearch'; import { MlLicense } from '../../../common/license'; import { CloudSetup } from '../../../../cloud/server'; import { spacesUtilsProvider } from '../../lib/spaces_utils'; @@ -23,7 +24,7 @@ export interface MlSystemProvider { ): { mlCapabilities(): Promise; mlInfo(): Promise; - mlAnomalySearch(searchParams: any, jobIds: string[]): Promise>; + mlAnomalySearch(searchParams: any, jobIds: string[]): Promise>; }; } @@ -69,7 +70,10 @@ export function getMlSystemProvider( }; }); }, - async mlAnomalySearch(searchParams: any, jobIds: string[]): Promise> { + async mlAnomalySearch( + searchParams: any, + jobIds: string[] + ): Promise> { return await getGuards(request, savedObjectsClient) .isFullLicense() .hasMlCapabilities(['canAccessML']) diff --git a/x-pack/plugins/transform/common/api_schemas/type_guards.ts b/x-pack/plugins/transform/common/api_schemas/type_guards.ts index 476e2bad853c9..4b66de9be20d2 100644 --- a/x-pack/plugins/transform/common/api_schemas/type_guards.ts +++ b/x-pack/plugins/transform/common/api_schemas/type_guards.ts @@ -5,10 +5,10 @@ * 2.0. */ -import type { SearchResponse7 } from '../../../ml/common'; +import type { estypes } from '@elastic/elasticsearch'; import type { EsIndex } from '../types/es_index'; -import { isPopulatedObject } from '../utils/object_utils'; +import { isPopulatedObject } from '../shared_imports'; // To be able to use the type guards on the client side, we need to make sure we don't import // the code of '@kbn/config-schema' but just its types, otherwise the client side code will @@ -28,20 +28,13 @@ import type { GetTransformsStatsResponseSchema } from './transforms_stats'; import type { PostTransformsUpdateResponseSchema } from './update_transforms'; const isGenericResponseSchema = (arg: any): arg is T => { - return ( - isPopulatedObject(arg) && - {}.hasOwnProperty.call(arg, 'count') && - {}.hasOwnProperty.call(arg, 'transforms') && - Array.isArray(arg.transforms) - ); + return isPopulatedObject(arg, ['count', 'transforms']) && Array.isArray(arg.transforms); }; export const isGetTransformNodesResponseSchema = ( arg: unknown ): arg is GetTransformNodesResponseSchema => { - return ( - isPopulatedObject(arg) && {}.hasOwnProperty.call(arg, 'count') && typeof arg.count === 'number' - ); + return isPopulatedObject(arg, ['count']) && typeof arg.count === 'number'; }; export const isGetTransformsResponseSchema = (arg: unknown): arg is GetTransformsResponseSchema => { @@ -59,7 +52,7 @@ export const isDeleteTransformsResponseSchema = ( ): arg is DeleteTransformsResponseSchema => { return ( isPopulatedObject(arg) && - Object.values(arg).every((d) => ({}.hasOwnProperty.call(d, 'transformDeleted'))) + Object.values(arg).every((d) => isPopulatedObject(d, ['transformDeleted'])) ); }; @@ -67,8 +60,22 @@ export const isEsIndices = (arg: unknown): arg is EsIndex[] => { return Array.isArray(arg); }; -export const isEsSearchResponse = (arg: unknown): arg is SearchResponse7 => { - return isPopulatedObject(arg) && {}.hasOwnProperty.call(arg, 'hits'); +export const isEsSearchResponse = (arg: unknown): arg is estypes.SearchResponse => { + return isPopulatedObject(arg, ['hits']); +}; + +type SearchResponseWithAggregations = Required> & + estypes.SearchResponse; +export const isEsSearchResponseWithAggregations = ( + arg: unknown +): arg is SearchResponseWithAggregations => { + return isEsSearchResponse(arg) && {}.hasOwnProperty.call(arg, 'aggregations'); +}; + +export const isMultiBucketAggregate = ( + arg: unknown +): arg is estypes.MultiBucketAggregate => { + return isPopulatedObject(arg, ['buckets']); }; export const isFieldHistogramsResponseSchema = ( @@ -87,9 +94,7 @@ export const isPostTransformsPreviewResponseSchema = ( arg: unknown ): arg is PostTransformsPreviewResponseSchema => { return ( - isPopulatedObject(arg) && - {}.hasOwnProperty.call(arg, 'generated_dest_index') && - {}.hasOwnProperty.call(arg, 'preview') && + isPopulatedObject(arg, ['generated_dest_index', 'preview']) && typeof arg.generated_dest_index !== undefined && Array.isArray(arg.preview) ); @@ -98,21 +103,19 @@ export const isPostTransformsPreviewResponseSchema = ( export const isPostTransformsUpdateResponseSchema = ( arg: unknown ): arg is PostTransformsUpdateResponseSchema => { - return isPopulatedObject(arg) && {}.hasOwnProperty.call(arg, 'id') && typeof arg.id === 'string'; + return isPopulatedObject(arg, ['id']) && typeof arg.id === 'string'; }; export const isPutTransformsResponseSchema = (arg: unknown): arg is PutTransformsResponseSchema => { return ( - isPopulatedObject(arg) && - {}.hasOwnProperty.call(arg, 'transformsCreated') && - {}.hasOwnProperty.call(arg, 'errors') && + isPopulatedObject(arg, ['transformsCreated', 'errors']) && Array.isArray(arg.transformsCreated) && Array.isArray(arg.errors) ); }; const isGenericSuccessResponseSchema = (arg: unknown) => - isPopulatedObject(arg) && Object.values(arg).every((d) => ({}.hasOwnProperty.call(d, 'success'))); + isPopulatedObject(arg) && Object.values(arg).every((d) => isPopulatedObject(d, ['success'])); export const isStartTransformsResponseSchema = ( arg: unknown diff --git a/x-pack/plugins/transform/common/shared_imports.ts b/x-pack/plugins/transform/common/shared_imports.ts index 3062c7ab8d23c..38cfb6bc457f1 100644 --- a/x-pack/plugins/transform/common/shared_imports.ts +++ b/x-pack/plugins/transform/common/shared_imports.ts @@ -5,10 +5,10 @@ * 2.0. */ -export type { HitsTotalRelation, SearchResponse7 } from '../../ml/common'; export { composeValidators, + isPopulatedObject, + isRuntimeMappings, patternValidator, ChartData, - HITS_TOTAL_RELATION, } from '../../ml/common'; diff --git a/x-pack/plugins/transform/common/types/index_pattern.ts b/x-pack/plugins/transform/common/types/index_pattern.ts index bab31b67b2b61..0485de8982e1a 100644 --- a/x-pack/plugins/transform/common/types/index_pattern.ts +++ b/x-pack/plugins/transform/common/types/index_pattern.ts @@ -7,17 +7,17 @@ import type { IndexPattern } from '../../../../../src/plugins/data/common'; -import { isPopulatedObject } from '../utils/object_utils'; +import { isPopulatedObject } from '../shared_imports'; // Custom minimal type guard for IndexPattern to check against the attributes used in transforms code. export function isIndexPattern(arg: any): arg is IndexPattern { return ( - isPopulatedObject(arg) && - 'getComputedFields' in arg && - typeof arg.getComputedFields === 'function' && - {}.hasOwnProperty.call(arg, 'title') && + isPopulatedObject(arg, ['title', 'fields']) && + // `getComputedFields` is inherited, so it's not possible to + // check with `hasOwnProperty` which is used by isPopulatedObject() + 'getComputedFields' in (arg as IndexPattern) && + typeof (arg as IndexPattern).getComputedFields === 'function' && typeof arg.title === 'string' && - {}.hasOwnProperty.call(arg, 'fields') && Array.isArray(arg.fields) ); } diff --git a/x-pack/plugins/transform/common/types/transform.ts b/x-pack/plugins/transform/common/types/transform.ts index 600808c475fd1..f1e7efdadca9d 100644 --- a/x-pack/plugins/transform/common/types/transform.ts +++ b/x-pack/plugins/transform/common/types/transform.ts @@ -7,7 +7,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui/src/components/combo_box/types'; import type { LatestFunctionConfig, PutTransformsRequestSchema } from '../api_schemas/transforms'; -import { isPopulatedObject } from '../utils/object_utils'; +import { isPopulatedObject } from '../shared_imports'; import { PivotGroupByDict } from './pivot_group_by'; import { PivotAggDict } from './pivot_aggs'; @@ -46,11 +46,11 @@ export type TransformLatestConfig = Omit & { export type TransformConfigUnion = TransformPivotConfig | TransformLatestConfig; export function isPivotTransform(transform: unknown): transform is TransformPivotConfig { - return isPopulatedObject(transform) && transform.hasOwnProperty('pivot'); + return isPopulatedObject(transform, ['pivot']); } export function isLatestTransform(transform: unknown): transform is TransformLatestConfig { - return isPopulatedObject(transform) && transform.hasOwnProperty('latest'); + return isPopulatedObject(transform, ['latest']); } export interface LatestFunctionConfigUI { diff --git a/x-pack/plugins/transform/common/types/transform_stats.ts b/x-pack/plugins/transform/common/types/transform_stats.ts index 03e6b2e403b69..00ffa40b84d3b 100644 --- a/x-pack/plugins/transform/common/types/transform_stats.ts +++ b/x-pack/plugins/transform/common/types/transform_stats.ts @@ -6,7 +6,7 @@ */ import { TransformState, TRANSFORM_STATE } from '../constants'; -import { isPopulatedObject } from '../utils/object_utils'; +import { isPopulatedObject } from '../shared_imports'; import { TransformId } from './transform'; export interface TransformStats { @@ -61,7 +61,5 @@ function isTransformState(arg: unknown): arg is TransformState { } export function isTransformStats(arg: unknown): arg is TransformStats { - return ( - isPopulatedObject(arg) && {}.hasOwnProperty.call(arg, 'state') && isTransformState(arg.state) - ); + return isPopulatedObject(arg, ['state']) && isTransformState(arg.state); } diff --git a/x-pack/plugins/transform/common/utils/errors.ts b/x-pack/plugins/transform/common/utils/errors.ts index 46ff3f9165c00..2aff8f332b130 100644 --- a/x-pack/plugins/transform/common/utils/errors.ts +++ b/x-pack/plugins/transform/common/utils/errors.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isPopulatedObject } from './object_utils'; +import { isPopulatedObject } from '../shared_imports'; export interface ErrorResponse { body: { @@ -18,7 +18,11 @@ export interface ErrorResponse { } export function isErrorResponse(arg: unknown): arg is ErrorResponse { - return isPopulatedObject(arg) && isPopulatedObject(arg.body) && arg?.body?.message !== undefined; + return ( + isPopulatedObject(arg, ['body']) && + isPopulatedObject(arg.body, ['message']) && + arg.body.message !== undefined + ); } export function getErrorMessage(error: unknown) { @@ -26,7 +30,7 @@ export function getErrorMessage(error: unknown) { return `${error.body.error}: ${error.body.message}`; } - if (isPopulatedObject(error) && typeof error.message === 'string') { + if (isPopulatedObject(error, ['message']) && typeof error.message === 'string') { return error.message; } diff --git a/x-pack/plugins/transform/common/utils/object_utils.test.ts b/x-pack/plugins/transform/common/utils/object_utils.test.ts index 5b354b9b27589..c99adf6b6d189 100644 --- a/x-pack/plugins/transform/common/utils/object_utils.test.ts +++ b/x-pack/plugins/transform/common/utils/object_utils.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getNestedProperty, isPopulatedObject } from './object_utils'; +import { getNestedProperty } from './object_utils'; describe('object_utils', () => { test('getNestedProperty()', () => { @@ -68,12 +68,4 @@ describe('object_utils', () => { expect(typeof test11).toBe('number'); expect(test11).toBe(0); }); - - test('isPopulatedObject()', () => { - expect(isPopulatedObject(0)).toBe(false); - expect(isPopulatedObject('')).toBe(false); - expect(isPopulatedObject(null)).toBe(false); - expect(isPopulatedObject({})).toBe(false); - expect(isPopulatedObject({ attribute: 'value' })).toBe(true); - }); }); diff --git a/x-pack/plugins/transform/common/utils/object_utils.ts b/x-pack/plugins/transform/common/utils/object_utils.ts index a573535da6b4f..605af48914360 100644 --- a/x-pack/plugins/transform/common/utils/object_utils.ts +++ b/x-pack/plugins/transform/common/utils/object_utils.ts @@ -51,7 +51,3 @@ export const setNestedProperty = (obj: Record, accessor: string, va return obj; }; - -export const isPopulatedObject = >(arg: unknown): arg is T => { - return typeof arg === 'object' && arg !== null && Object.keys(arg).length > 0; -}; diff --git a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts index 00a92865789ff..ae072e6666e4a 100644 --- a/x-pack/plugins/transform/public/__mocks__/shared_imports.ts +++ b/x-pack/plugins/transform/public/__mocks__/shared_imports.ts @@ -16,4 +16,4 @@ export const useRequest = jest.fn(() => ({ export const createSavedSearchesLoader = jest.fn(); // just passing through the reimports -export { getMlSharedImports, HITS_TOTAL_RELATION } from '../../../ml/public'; +export { getMlSharedImports, ES_CLIENT_TOTAL_HITS_RELATION } from '../../../ml/public'; diff --git a/x-pack/plugins/transform/public/app/common/pivot_aggs.ts b/x-pack/plugins/transform/public/app/common/pivot_aggs.ts index 905b40f16f7fb..03e06d36f9319 100644 --- a/x-pack/plugins/transform/public/app/common/pivot_aggs.ts +++ b/x-pack/plugins/transform/public/app/common/pivot_aggs.ts @@ -14,7 +14,7 @@ import type { Dictionary } from '../../../common/types/common'; import type { EsFieldName } from '../../../common/types/fields'; import type { PivotAgg, PivotSupportedAggs } from '../../../common/types/pivot_aggs'; import { PIVOT_SUPPORTED_AGGS } from '../../../common/types/pivot_aggs'; -import { isPopulatedObject } from '../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../common/shared_imports'; import { getAggFormConfig } from '../sections/create_transform/components/step_define/common/get_agg_form_config'; import { PivotAggsConfigFilter } from '../sections/create_transform/components/step_define/common/filter_agg/types'; @@ -166,11 +166,7 @@ export type PivotAggsConfigWithUiSupport = export function isPivotAggsConfigWithUiSupport(arg: unknown): arg is PivotAggsConfigWithUiSupport { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('agg') && - arg.hasOwnProperty('aggName') && - arg.hasOwnProperty('dropDownName') && - arg.hasOwnProperty('field') && + isPopulatedObject(arg, ['agg', 'aggName', 'dropDownName', 'field']) && isPivotSupportedAggs(arg.agg) ); } @@ -181,15 +177,12 @@ export function isPivotAggsConfigWithUiSupport(arg: unknown): arg is PivotAggsCo type PivotAggsConfigWithExtendedForm = PivotAggsConfigFilter; export function isPivotAggsWithExtendedForm(arg: unknown): arg is PivotAggsConfigWithExtendedForm { - return isPopulatedObject(arg) && arg.hasOwnProperty('AggFormComponent'); + return isPopulatedObject(arg, ['AggFormComponent']); } export function isPivotAggsConfigPercentiles(arg: unknown): arg is PivotAggsConfigPercentiles { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('agg') && - arg.hasOwnProperty('field') && - arg.hasOwnProperty('percents') && + isPopulatedObject(arg, ['agg', 'field', 'percents']) && arg.agg === PIVOT_SUPPORTED_AGGS.PERCENTILES ); } diff --git a/x-pack/plugins/transform/public/app/common/pivot_group_by.ts b/x-pack/plugins/transform/public/app/common/pivot_group_by.ts index fac0d88a84df7..0ad059fd29950 100644 --- a/x-pack/plugins/transform/public/app/common/pivot_group_by.ts +++ b/x-pack/plugins/transform/public/app/common/pivot_group_by.ts @@ -9,7 +9,7 @@ import { AggName } from '../../../common/types/aggregations'; import { Dictionary } from '../../../common/types/common'; import { EsFieldName } from '../../../common/types/fields'; import { GenericAgg } from '../../../common/types/pivot_group_by'; -import { isPopulatedObject } from '../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../common/shared_imports'; import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/common'; import { PivotAggsConfigWithUiSupport } from './pivot_aggs'; @@ -84,30 +84,21 @@ export type PivotGroupByConfigDict = Dictionary; export function isGroupByDateHistogram(arg: unknown): arg is GroupByDateHistogram { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('agg') && - arg.hasOwnProperty('field') && - arg.hasOwnProperty('calendar_interval') && + isPopulatedObject(arg, ['agg', 'field', 'calendar_interval']) && arg.agg === PIVOT_SUPPORTED_GROUP_BY_AGGS.DATE_HISTOGRAM ); } export function isGroupByHistogram(arg: unknown): arg is GroupByHistogram { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('agg') && - arg.hasOwnProperty('field') && - arg.hasOwnProperty('interval') && + isPopulatedObject(arg, ['agg', 'field', 'interval']) && arg.agg === PIVOT_SUPPORTED_GROUP_BY_AGGS.HISTOGRAM ); } export function isGroupByTerms(arg: unknown): arg is GroupByTerms { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('agg') && - arg.hasOwnProperty('field') && - arg.agg === PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS + isPopulatedObject(arg, ['agg', 'field']) && arg.agg === PIVOT_SUPPORTED_GROUP_BY_AGGS.TERMS ); } @@ -124,5 +115,5 @@ export function getEsAggFromGroupByConfig(groupByConfig: GroupByConfigBase): Gen } export function isPivotAggConfigWithUiSupport(arg: unknown): arg is PivotAggsConfigWithUiSupport { - return isPopulatedObject(arg) && arg.hasOwnProperty('agg') && arg.hasOwnProperty('field'); + return isPopulatedObject(arg, ['agg', 'field']); } diff --git a/x-pack/plugins/transform/public/app/common/request.ts b/x-pack/plugins/transform/public/app/common/request.ts index 647d511cc4dcf..a7a3a91f9429b 100644 --- a/x-pack/plugins/transform/public/app/common/request.ts +++ b/x-pack/plugins/transform/public/app/common/request.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { DefaultOperator } from 'elasticsearch'; +import type { estypes } from '@elastic/elasticsearch'; import { HttpFetchError } from '../../../../../../src/core/public'; import type { IndexPattern } from '../../../../../../src/plugins/data/public'; @@ -17,7 +17,7 @@ import type { PutTransformsPivotRequestSchema, PutTransformsRequestSchema, } from '../../../common/api_schemas/transforms'; -import { isPopulatedObject } from '../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../common/shared_imports'; import { DateHistogramAgg, HistogramAgg, TermsAgg } from '../../../common/types/pivot_group_by'; import { isIndexPattern } from '../../../common/types/index_pattern'; @@ -39,7 +39,7 @@ import { export interface SimpleQuery { query_string: { query: string; - default_operator?: DefaultOperator; + default_operator?: estypes.DefaultOperator; }; } @@ -59,14 +59,13 @@ export function getPivotQuery(search: string | SavedSearchQuery): PivotQuery { } export function isSimpleQuery(arg: unknown): arg is SimpleQuery { - return isPopulatedObject(arg) && arg.hasOwnProperty('query_string'); + return isPopulatedObject(arg, ['query_string']); } export const matchAllQuery = { match_all: {} }; export function isMatchAllQuery(query: unknown): boolean { return ( - isPopulatedObject(query) && - query.hasOwnProperty('match_all') && + isPopulatedObject(query, ['match_all']) && typeof query.match_all === 'object' && query.match_all !== null && Object.keys(query.match_all).length === 0 @@ -101,7 +100,7 @@ export function getCombinedRuntimeMappings( combinedRuntimeMappings = { ...combinedRuntimeMappings, ...runtimeMappings }; } - if (isPopulatedObject(combinedRuntimeMappings)) { + if (isPopulatedObject(combinedRuntimeMappings)) { return combinedRuntimeMappings; } return undefined; diff --git a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts index 7aaca793c2a1f..a9455877be429 100644 --- a/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/__mocks__/use_api.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { estypes } from '@elastic/elasticsearch'; + import { HttpFetchError } from 'kibana/public'; import { KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; @@ -37,7 +39,6 @@ import type { PostTransformsUpdateResponseSchema, } from '../../../../common/api_schemas/update_transforms'; -import type { SearchResponse7 } from '../../../../common/shared_imports'; import { EsIndex } from '../../../../common/types/es_index'; import type { SavedSearchQuery } from '../use_search_items'; @@ -134,7 +135,7 @@ const apiFactory = () => ({ ): Promise { return Promise.resolve([]); }, - async esSearch(payload: any): Promise { + async esSearch(payload: any): Promise { return Promise.resolve({ hits: { hits: [], diff --git a/x-pack/plugins/transform/public/app/hooks/use_api.ts b/x-pack/plugins/transform/public/app/hooks/use_api.ts index f3c90a688453d..1abe2ed09444e 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_api.ts @@ -7,6 +7,8 @@ import { useMemo } from 'react'; +import { estypes } from '@elastic/elasticsearch'; + import { HttpFetchError } from 'kibana/public'; import { KBN_FIELD_TYPES } from '../../../../../../src/plugins/data/public'; @@ -44,7 +46,6 @@ import type { GetTransformsStatsResponseSchema } from '../../../common/api_schem import { TransformId } from '../../../common/types/transform'; import { API_BASE_PATH } from '../../../common/constants'; import { EsIndex } from '../../../common/types/es_index'; -import type { SearchResponse7 } from '../../../common/shared_imports'; import { useAppDependencies } from '../app_dependencies'; @@ -187,7 +188,7 @@ export const useApi = () => { return e; } }, - async esSearch(payload: any): Promise { + async esSearch(payload: any): Promise { try { return await http.post(`${API_BASE_PATH}es_search`, { body: JSON.stringify(payload) }); } catch (e) { diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts index e12aa78e33622..bb83de8e12004 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.ts @@ -7,7 +7,8 @@ import { useEffect, useMemo } from 'react'; -import { EuiDataGridColumn } from '@elastic/eui'; +import type { estypes } from '@elastic/elasticsearch'; +import type { EuiDataGridColumn } from '@elastic/eui'; import { isEsSearchResponse, @@ -133,10 +134,14 @@ export const useIndexData = ( return; } - const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields)); + const docs = resp.hits.hits.map((d) => getProcessedFields(d.fields ?? {})); - setRowCount(resp.hits.total.value); - setRowCountRelation(resp.hits.total.relation); + setRowCount(typeof resp.hits.total === 'number' ? resp.hits.total : resp.hits.total.value); + setRowCountRelation( + typeof resp.hits.total === 'number' + ? ('eq' as estypes.TotalHitsRelation) + : resp.hits.total.relation + ); setTableItems(docs); setStatus(INDEX_STATUS.LOADED); }; diff --git a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts index 2477c005c936d..24c28effd12bc 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_pivot_data.ts @@ -18,7 +18,11 @@ import type { PreviewMappingsProperties } from '../../../common/api_schemas/tran import { isPostTransformsPreviewResponseSchema } from '../../../common/api_schemas/type_guards'; import { getNestedProperty } from '../../../common/utils/object_utils'; -import { RenderCellValue, UseIndexDataReturnType, HITS_TOTAL_RELATION } from '../../shared_imports'; +import { + RenderCellValue, + UseIndexDataReturnType, + ES_CLIENT_TOTAL_HITS_RELATION, +} from '../../shared_imports'; import { getErrorMessage } from '../../../common/utils/errors'; import { useAppDependencies } from '../app_dependencies'; @@ -128,7 +132,7 @@ export const usePivotData = ( if (!validationStatus.isValid) { setTableItems([]); setRowCount(0); - setRowCountRelation(HITS_TOTAL_RELATION.EQ); + setRowCountRelation(ES_CLIENT_TOTAL_HITS_RELATION.EQ); setNoDataMessage(validationStatus.errorMessage!); return; } @@ -149,7 +153,7 @@ export const usePivotData = ( setErrorMessage(getErrorMessage(resp)); setTableItems([]); setRowCount(0); - setRowCountRelation(HITS_TOTAL_RELATION.EQ); + setRowCountRelation(ES_CLIENT_TOTAL_HITS_RELATION.EQ); setPreviewMappingsProperties({}); setStatus(INDEX_STATUS.ERROR); return; @@ -157,7 +161,7 @@ export const usePivotData = ( setTableItems(resp.preview); setRowCount(resp.preview.length); - setRowCountRelation(HITS_TOTAL_RELATION.EQ); + setRowCountRelation(ES_CLIENT_TOTAL_HITS_RELATION.EQ); setPreviewMappingsProperties(resp.generated_dest_index.mappings.properties); setStatus(INDEX_STATUS.LOADED); diff --git a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts index 28e9f190a9108..5599e3f423277 100644 --- a/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts +++ b/x-pack/plugins/transform/public/app/lib/authorization/components/common.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { Privileges } from '../../../../../common/types/privileges'; -import { isPopulatedObject } from '../../../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../../../common/shared_imports'; export interface Capabilities { canGetTransform: boolean; @@ -22,10 +22,8 @@ export type Privilege = [string, string]; function isPrivileges(arg: unknown): arg is Privileges { return ( - isPopulatedObject(arg) && - arg.hasOwnProperty('hasAllPrivileges') && + isPopulatedObject(arg, ['hasAllPrivileges', 'missingPrivileges']) && typeof arg.hasAllPrivileges === 'boolean' && - arg.hasOwnProperty('missingPrivileges') && typeof arg.missingPrivileges === 'object' && arg.missingPrivileges !== null ); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/advanced_runtime_mappings_editor/advanced_runtime_mappings_editor.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/advanced_runtime_mappings_editor/advanced_runtime_mappings_editor.tsx index 1e8397a4d9cc3..1e6e6a971a81a 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/advanced_runtime_mappings_editor/advanced_runtime_mappings_editor.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/advanced_runtime_mappings_editor/advanced_runtime_mappings_editor.tsx @@ -12,8 +12,9 @@ import { EuiCodeEditor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { isRuntimeMappings } from '../../../../../../common/shared_imports'; + import { StepDefineFormHook } from '../step_define'; -import { isRuntimeMappings } from '../step_define/common/types'; export const AdvancedRuntimeMappingsEditor: FC = memo( ({ diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx index a7f2a3cd7178d..36bdca7921622 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx @@ -47,7 +47,7 @@ import { PutTransformsPivotRequestSchema, } from '../../../../../../common/api_schemas/transforms'; import type { RuntimeField } from '../../../../../../../../../src/plugins/data/common/index_patterns'; -import { isPopulatedObject } from '../../../../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../../../../common/shared_imports'; import { isLatestTransform } from '../../../../../../common/types/transform'; export interface StepDetailsExposedState { diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_agg_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_agg_form.tsx index 3b5d6e0e50497..9b349541a78a3 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_agg_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_agg_form.tsx @@ -16,7 +16,7 @@ import { getFilterAggTypeConfig } from '../config'; import type { FilterAggType, PivotAggsConfigFilter } from '../types'; import type { RuntimeMappings } from '../../types'; import { getKibanaFieldTypeFromEsType } from '../../get_pivot_dropdown_options'; -import { isPopulatedObject } from '../../../../../../../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../../../../../../../common/shared_imports'; /** * Resolves supported filters for provided field. diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx index f2db6167c163c..358bb9dcafa96 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/filter_agg/components/filter_term_form.tsx @@ -6,12 +6,16 @@ */ import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { EuiComboBox, EuiFormRow } from '@elastic/eui'; +import { estypes } from '@elastic/elasticsearch'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { debounce } from 'lodash'; import useUpdateEffect from 'react-use/lib/useUpdateEffect'; import { i18n } from '@kbn/i18n'; -import { isEsSearchResponse } from '../../../../../../../../../common/api_schemas/type_guards'; +import { + isEsSearchResponseWithAggregations, + isMultiBucketAggregate, +} from '../../../../../../../../../common/api_schemas/type_guards'; import { useApi } from '../../../../../../../hooks'; import { CreateTransformWizardContext } from '../../../../wizard/wizard'; import { FilterAggConfigTerm } from '../types'; @@ -29,7 +33,7 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm const { indexPattern, runtimeMappings } = useContext(CreateTransformWizardContext); const toastNotifications = useToastNotifications(); - const [options, setOptions] = useState([]); + const [options, setOptions] = useState([]); const [isLoading, setIsLoading] = useState(true); /* eslint-disable-next-line react-hooks/exhaustive-deps */ @@ -62,7 +66,12 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm setIsLoading(false); - if (!isEsSearchResponse(response)) { + if ( + !( + isEsSearchResponseWithAggregations(response) && + isMultiBucketAggregate(response.aggregations.field_values) + ) + ) { toastNotifications.addWarning( i18n.translate('xpack.transform.agg.popoverForm.filerAgg.term.errorFetchSuggestions', { defaultMessage: 'Unable to fetch suggestions', @@ -72,9 +81,7 @@ export const FilterTermForm: FilterAggConfigTerm['aggTypeConfig']['FilterAggForm } setOptions( - response.aggregations.field_values.buckets.map( - (value: { key: string; doc_count: number }) => ({ label: value.key }) - ) + response.aggregations.field_values.buckets.map((value) => ({ label: value.key + '' })) ); }, 600), [selectedField] diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts index 8d85988424e27..957439810adc7 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/get_pivot_dropdown_options.ts @@ -13,6 +13,7 @@ import { } from '../../../../../../../../../../src/plugins/data/public'; import { getNestedProperty } from '../../../../../../../common/utils/object_utils'; +import { isRuntimeMappings } from '../../../../../../../common/shared_imports'; import { DropDownLabel, @@ -26,7 +27,6 @@ import { import { getDefaultAggregationConfig } from './get_default_aggregation_config'; import { getDefaultGroupByConfig } from './get_default_group_by_config'; import type { Field, StepDefineExposedState } from './types'; -import { isRuntimeMappings } from './types'; const illegalEsAggNameChars = /[[\]>]/g; diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.test.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.test.ts deleted file mode 100644 index ec90d31a0d169..0000000000000 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { isRuntimeField, isRuntimeMappings } from './types'; - -describe('Transform: step_define type guards', () => { - it('isRuntimeField()', () => { - expect(isRuntimeField(1)).toBe(false); - expect(isRuntimeField(null)).toBe(false); - expect(isRuntimeField([])).toBe(false); - expect(isRuntimeField({})).toBe(false); - expect(isRuntimeField({ someAttribute: 'someValue' })).toBe(false); - expect(isRuntimeField({ type: 'wrong-type' })).toBe(false); - expect(isRuntimeField({ type: 'keyword', someAttribute: 'some value' })).toBe(false); - - expect(isRuntimeField({ type: 'keyword' })).toBe(true); - expect(isRuntimeField({ type: 'keyword', script: 'some script' })).toBe(true); - }); - - it('isRuntimeMappings()', () => { - expect(isRuntimeMappings(1)).toBe(false); - expect(isRuntimeMappings(null)).toBe(false); - expect(isRuntimeMappings([])).toBe(false); - expect(isRuntimeMappings({})).toBe(false); - expect(isRuntimeMappings({ someAttribute: 'someValue' })).toBe(false); - expect(isRuntimeMappings({ fieldName1: { type: 'keyword' }, fieldName2: 'someValue' })).toBe( - false - ); - expect( - isRuntimeMappings({ - fieldName1: { type: 'keyword' }, - fieldName2: { type: 'keyword', someAttribute: 'some value' }, - }) - ).toBe(false); - expect( - isRuntimeMappings({ - fieldName: { type: 'long', script: 1234 }, - }) - ).toBe(false); - expect( - isRuntimeMappings({ - fieldName: { type: 'long', script: { someAttribute: 'some value' } }, - }) - ).toBe(false); - expect( - isRuntimeMappings({ - fieldName: { type: 'long', script: { source: 1234 } }, - }) - ).toBe(false); - - expect(isRuntimeMappings({ fieldName: { type: 'keyword' } })).toBe(true); - expect( - isRuntimeMappings({ fieldName1: { type: 'keyword' }, fieldName2: { type: 'keyword' } }) - ).toBe(true); - expect( - isRuntimeMappings({ - fieldName1: { type: 'keyword' }, - fieldName2: { type: 'keyword', script: 'some script as script' }, - }) - ).toBe(true); - expect( - isRuntimeMappings({ - fieldName: { type: 'long', script: { source: 'some script as source' } }, - }) - ).toBe(true); - }); -}); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts index 6b4ff0090a497..8b3b33fdde3ef 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.ts @@ -24,7 +24,7 @@ import { } from '../../../../../../../common/types/transform'; import { LatestFunctionConfig } from '../../../../../../../common/api_schemas/transforms'; -import { isPopulatedObject } from '../../../../../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../../../../../common/shared_imports'; export interface ErrorMessage { query: string; @@ -72,30 +72,10 @@ export interface StepDefineExposedState { isRuntimeMappingsEditorEnabled: boolean; } -export function isRuntimeField(arg: unknown): arg is RuntimeField { - return ( - isPopulatedObject(arg) && - ((Object.keys(arg).length === 1 && arg.hasOwnProperty('type')) || - (Object.keys(arg).length === 2 && - arg.hasOwnProperty('type') && - arg.hasOwnProperty('script') && - (typeof arg.script === 'string' || - (isPopulatedObject(arg.script) && - Object.keys(arg.script).length === 1 && - arg.script.hasOwnProperty('source') && - typeof arg.script.source === 'string')))) && - RUNTIME_FIELD_TYPES.includes(arg.type as RuntimeType) - ); -} - -export function isRuntimeMappings(arg: unknown): arg is RuntimeMappings { - return isPopulatedObject(arg) && Object.values(arg).every((d) => isRuntimeField(d)); -} - export function isPivotPartialRequest(arg: unknown): arg is { pivot: PivotConfigDefinition } { - return isPopulatedObject(arg) && arg.hasOwnProperty('pivot'); + return isPopulatedObject(arg, ['pivot']); } export function isLatestPartialRequest(arg: unknown): arg is { latest: LatestFunctionConfig } { - return isPopulatedObject(arg) && arg.hasOwnProperty('latest'); + return isPopulatedObject(arg, ['latest']); } diff --git a/x-pack/plugins/transform/public/shared_imports.ts b/x-pack/plugins/transform/public/shared_imports.ts index ddf5cf7cb5cb1..edd27fd43c2af 100644 --- a/x-pack/plugins/transform/public/shared_imports.ts +++ b/x-pack/plugins/transform/public/shared_imports.ts @@ -15,7 +15,7 @@ export { UseIndexDataReturnType, EsSorting, RenderCellValue, - HITS_TOTAL_RELATION, + ES_CLIENT_TOTAL_HITS_RELATION, } from '../../ml/public'; import { XJson } from '../../../../src/plugins/es_ui_shared/public'; diff --git a/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts b/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts index afdcc93998303..c9a0795c32210 100644 --- a/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts +++ b/x-pack/plugins/transform/server/routes/api/transforms_nodes.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { isPopulatedObject } from '../../../common/utils/object_utils'; +import { isPopulatedObject } from '../../../common/shared_imports'; import { RouteDependencies } from '../../types'; @@ -24,10 +24,7 @@ export const isNodes = (arg: unknown): arg is Nodes => { return ( isPopulatedObject(arg) && Object.values(arg).every( - (node) => - isPopulatedObject(node) && - {}.hasOwnProperty.call(node, NODE_ROLES) && - Array.isArray(node.roles) + (node) => isPopulatedObject(node, [NODE_ROLES]) && Array.isArray(node.roles) ) ); };