diff --git a/x-pack/plugins/ml/common/index.ts b/x-pack/plugins/ml/common/index.ts index c049b68990d2d..ac21954118e50 100644 --- a/x-pack/plugins/ml/common/index.ts +++ b/x-pack/plugins/ml/common/index.ts @@ -6,6 +6,7 @@ */ export { HitsTotalRelation, SearchResponse7, HITS_TOTAL_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 { composeValidators, patternValidator } from './util/validators'; diff --git a/x-pack/plugins/ml/common/types/field_histograms.ts b/x-pack/plugins/ml/common/types/field_histograms.ts new file mode 100644 index 0000000000000..22b0195a579ac --- /dev/null +++ b/x-pack/plugins/ml/common/types/field_histograms.ts @@ -0,0 +1,68 @@ +/* + * 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. + */ + +export interface NumericDataItem { + key: number; + key_as_string?: string | number; + doc_count: number; +} + +export interface NumericChartData { + data: NumericDataItem[]; + id: string; + interval: number; + stats: [number, number]; + type: 'numeric'; +} + +export const isNumericChartData = (arg: any): arg is NumericChartData => { + return ( + typeof arg === 'object' && + arg.hasOwnProperty('data') && + arg.hasOwnProperty('id') && + arg.hasOwnProperty('interval') && + arg.hasOwnProperty('stats') && + arg.hasOwnProperty('type') && + arg.type === 'numeric' + ); +}; + +export interface OrdinalDataItem { + key: string; + key_as_string?: string; + doc_count: number; +} + +export interface OrdinalChartData { + cardinality: number; + data: OrdinalDataItem[]; + id: string; + type: 'ordinal' | 'boolean'; +} + +export const isOrdinalChartData = (arg: any): arg is OrdinalChartData => { + return ( + typeof arg === 'object' && + arg.hasOwnProperty('data') && + arg.hasOwnProperty('cardinality') && + arg.hasOwnProperty('id') && + arg.hasOwnProperty('type') && + (arg.type === 'ordinal' || arg.type === 'boolean') + ); +}; + +export interface UnsupportedChartData { + id: string; + type: 'unsupported'; +} + +export const isUnsupportedChartData = (arg: any): arg is UnsupportedChartData => { + return typeof arg === 'object' && arg.hasOwnProperty('type') && arg.type === 'unsupported'; +}; + +export type ChartDataItem = NumericDataItem | OrdinalDataItem; +export type ChartData = NumericChartData | OrdinalChartData | UnsupportedChartData; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx index 102ccc560ba93..3800256927d54 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/column_chart.tsx @@ -13,7 +13,9 @@ import { EuiDataGridColumn } from '@elastic/eui'; import './column_chart.scss'; -import { isUnsupportedChartData, useColumnChart, ChartData } from './use_column_chart'; +import { isUnsupportedChartData, ChartData } from '../../../../common/types/field_histograms'; + +import { useColumnChart } from './use_column_chart'; interface Props { chartData: ChartData; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/index.ts b/x-pack/plugins/ml/public/application/components/data_grid/index.ts index a3f1995736624..be37e381d1bae 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/index.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/index.ts @@ -16,7 +16,7 @@ export { useRenderCellValue, getProcessedFields, } from './common'; -export { getFieldType, ChartData } from './use_column_chart'; +export { getFieldType } from './use_column_chart'; export { useDataGrid } from './use_data_grid'; export { DataGrid } from './data_grid'; export { 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 77c7bdb385469..2fb47a59284a3 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 @@ -11,10 +11,10 @@ import { EuiDataGridPaginationProps, EuiDataGridSorting, EuiDataGridColumn } fro 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'; -import { ChartData } from './use_column_chart'; import { FeatureImportanceBaseline } from '../../../../common/types/feature_importance'; export type ColumnId = string; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.test.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.test.tsx index 0154d43a06865..631c214dd751c 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.test.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.test.tsx @@ -13,17 +13,15 @@ import '@testing-library/jest-dom/extend-expect'; import { KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; import { - getFieldType, - getLegendText, - getXScaleType, isNumericChartData, isOrdinalChartData, isUnsupportedChartData, - useColumnChart, NumericChartData, OrdinalChartData, UnsupportedChartData, -} from './use_column_chart'; +} from '../../../../common/types/field_histograms'; + +import { getFieldType, getLegendText, getXScaleType, useColumnChart } from './use_column_chart'; describe('getFieldType()', () => { it('should return the Kibana field type for a given EUI data grid schema', () => { diff --git a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx index fcb8a20f558fd..4764a1674df2f 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/use_column_chart.tsx @@ -17,6 +17,15 @@ import { i18n } from '@kbn/i18n'; import { KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; +import { + isNumericChartData, + isOrdinalChartData, + ChartData, + ChartDataItem, + NumericDataItem, + OrdinalDataItem, +} from '../../../../common/types/field_histograms'; + import { NON_AGGREGATABLE } from './common'; export const hoveredRow$ = new BehaviorSubject(null); @@ -66,68 +75,6 @@ export const getFieldType = (schema: EuiDataGridColumn['schema']): KBN_FIELD_TYP return fieldType; }; -interface NumericDataItem { - key: number; - key_as_string?: string | number; - doc_count: number; -} - -export interface NumericChartData { - data: NumericDataItem[]; - id: string; - interval: number; - stats: [number, number]; - type: 'numeric'; -} - -export const isNumericChartData = (arg: any): arg is NumericChartData => { - return ( - typeof arg === 'object' && - arg.hasOwnProperty('data') && - arg.hasOwnProperty('id') && - arg.hasOwnProperty('interval') && - arg.hasOwnProperty('stats') && - arg.hasOwnProperty('type') && - arg.type === 'numeric' - ); -}; - -export interface OrdinalDataItem { - key: string; - key_as_string?: string; - doc_count: number; -} - -export interface OrdinalChartData { - cardinality: number; - data: OrdinalDataItem[]; - id: string; - type: 'ordinal' | 'boolean'; -} - -export const isOrdinalChartData = (arg: any): arg is OrdinalChartData => { - return ( - typeof arg === 'object' && - arg.hasOwnProperty('data') && - arg.hasOwnProperty('cardinality') && - arg.hasOwnProperty('id') && - arg.hasOwnProperty('type') && - (arg.type === 'ordinal' || arg.type === 'boolean') - ); -}; - -export interface UnsupportedChartData { - id: string; - type: 'unsupported'; -} - -export const isUnsupportedChartData = (arg: any): arg is UnsupportedChartData => { - return typeof arg === 'object' && arg.hasOwnProperty('type') && arg.type === 'unsupported'; -}; - -export type ChartDataItem = NumericDataItem | OrdinalDataItem; -export type ChartData = NumericChartData | OrdinalChartData | UnsupportedChartData; - type LegendText = string | JSX.Element; export const getLegendText = ( chartData: ChartData, 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 4129f3a01bce9..228ee86a7f800 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 @@ -10,6 +10,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 { ChartData } from '../../../../common/types/field_histograms'; import { INDEX_STATUS } from '../../data_frame_analytics/common'; @@ -26,7 +27,6 @@ import { RowCountRelation, UseDataGridReturnType, } from './types'; -import { ChartData } from './use_column_chart'; export const useDataGrid = ( columns: EuiDataGridColumn[], diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.tsx index 17b2e04256098..70adbbe85bc58 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/boolean_content_preview.tsx @@ -7,10 +7,10 @@ import React, { FC, useMemo } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; +import { OrdinalChartData } from '../../../../../../common/types/field_histograms'; +import { ColumnChart } from '../../../../components/data_grid/column_chart'; import { FieldDataRowProps } from '../../types'; import { getTFPercentage } from '../../utils'; -import { ColumnChart } from '../../../../components/data_grid/column_chart'; -import { OrdinalChartData } from '../../../../components/data_grid/use_column_chart'; export const BooleanContentPreview: FC = ({ config }) => { const chartData = useMemo(() => { diff --git a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx index 122663b304024..07a2eae95c890 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/stats_table/components/field_data_row/top_values_preview.tsx @@ -7,10 +7,9 @@ import React, { FC } from 'react'; import { EuiDataGridColumn } from '@elastic/eui'; -import type { FieldDataRowProps } from '../../types/field_data_row'; +import { ChartData, OrdinalDataItem } from '../../../../../../common/types/field_histograms'; import { ColumnChart } from '../../../../components/data_grid/column_chart'; -import { ChartData } from '../../../../components/data_grid'; -import { OrdinalDataItem } from '../../../../components/data_grid/use_column_chart'; +import type { FieldDataRowProps } from '../../types/field_data_row'; export const TopValuesPreview: FC = ({ config }) => { const { stats } = config; diff --git a/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts b/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts index 69ebfe5f0bc11..4db8295d93997 100644 --- a/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts +++ b/x-pack/plugins/ml/server/models/data_visualizer/data_visualizer.ts @@ -16,7 +16,7 @@ import { buildSamplerAggregation, getSamplerAggregationsResponsePath, } from '../../lib/query_utils'; -import { AggCardinality } from '../../../common/types/fields'; +import { AggCardinality, RuntimeMappings } from '../../../common/types/fields'; import { getDatafeedAggregations } from '../../../common/util/datafeed_utils'; import { Datafeed } from '../../../common/types/anomaly_detection_jobs'; import { isPopulatedObject } from '../../../common/util/object_utils'; @@ -183,7 +183,8 @@ const getAggIntervals = async ( indexPatternTitle: string, query: any, fields: HistogramField[], - samplerShardSize: number + samplerShardSize: number, + runtimeMappings?: RuntimeMappings ): Promise => { const numericColumns = fields.filter((field) => { return field.type === KBN_FIELD_TYPES.NUMBER || field.type === KBN_FIELD_TYPES.DATE; @@ -210,6 +211,7 @@ const getAggIntervals = async ( query, aggs: buildSamplerAggregation(minMaxAggs, samplerShardSize), size: 0, + ...(runtimeMappings !== undefined ? { runtime_mappings: runtimeMappings } : {}), }, }); @@ -240,7 +242,8 @@ export const getHistogramsForFields = async ( indexPatternTitle: string, query: any, fields: HistogramField[], - samplerShardSize: number + samplerShardSize: number, + runtimeMappings?: RuntimeMappings ) => { const { asCurrentUser } = client; const aggIntervals = await getAggIntervals( @@ -248,7 +251,8 @@ export const getHistogramsForFields = async ( indexPatternTitle, query, fields, - samplerShardSize + samplerShardSize, + runtimeMappings ); const chartDataAggs = fields.reduce((aggs, field) => { @@ -293,6 +297,7 @@ export const getHistogramsForFields = async ( query, aggs: buildSamplerAggregation(chartDataAggs, samplerShardSize), size: 0, + ...(runtimeMappings !== undefined ? { runtime_mappings: runtimeMappings } : {}), }, }); @@ -607,7 +612,7 @@ export class DataVisualizer { // Value count aggregation faster way of checking if field exists than using // filter aggregation with exists query. const aggs: Aggs = datafeedAggregations !== undefined ? { ...datafeedAggregations } : {}; - const runtimeMappings: any = {}; + const runtimeMappings: { runtime_mappings?: RuntimeMappings } = {}; aggregatableFields.forEach((field, i) => { const safeFieldName = getSafeAggregationName(field, i); diff --git a/x-pack/plugins/transform/common/api_schemas/common.ts b/x-pack/plugins/transform/common/api_schemas/common.ts index 1841724c16ef9..3651af69359a9 100644 --- a/x-pack/plugins/transform/common/api_schemas/common.ts +++ b/x-pack/plugins/transform/common/api_schemas/common.ts @@ -53,3 +53,27 @@ export interface ResponseStatus { export interface CommonResponseStatusSchema { [key: string]: ResponseStatus; } + +export const runtimeMappingsSchema = schema.maybe( + schema.recordOf( + schema.string(), + schema.object({ + type: schema.oneOf([ + schema.literal('keyword'), + schema.literal('long'), + schema.literal('double'), + schema.literal('date'), + schema.literal('ip'), + schema.literal('boolean'), + ]), + script: schema.maybe( + schema.oneOf([ + schema.string(), + schema.object({ + source: schema.string(), + }), + ]) + ), + }) + ) +); diff --git a/x-pack/plugins/transform/common/api_schemas/field_histograms.ts b/x-pack/plugins/transform/common/api_schemas/field_histograms.ts index fc5a95590c7c6..9f6f4c15d803a 100644 --- a/x-pack/plugins/transform/common/api_schemas/field_histograms.ts +++ b/x-pack/plugins/transform/common/api_schemas/field_histograms.ts @@ -7,14 +7,20 @@ import { schema, TypeOf } from '@kbn/config-schema'; +import { ChartData } from '../shared_imports'; + +import { runtimeMappingsSchema } from './common'; + export const fieldHistogramsRequestSchema = schema.object({ /** Query to match documents in the index. */ query: schema.any(), /** The fields to return histogram data. */ fields: schema.arrayOf(schema.any()), + /** Optional runtime mappings */ + runtimeMappings: runtimeMappingsSchema, /** Number of documents to be collected in the sample processed on each shard, or -1 for no sampling. */ samplerShardSize: schema.number(), }); export type FieldHistogramsRequestSchema = TypeOf; -export type FieldHistogramsResponseSchema = any[]; +export type FieldHistogramsResponseSchema = ChartData[]; diff --git a/x-pack/plugins/transform/common/api_schemas/transforms.ts b/x-pack/plugins/transform/common/api_schemas/transforms.ts index d86df3af3e1d0..4d25bd74f4e74 100644 --- a/x-pack/plugins/transform/common/api_schemas/transforms.ts +++ b/x-pack/plugins/transform/common/api_schemas/transforms.ts @@ -14,7 +14,7 @@ import type { PivotAggDict } from '../types/pivot_aggs'; import type { PivotGroupByDict } from '../types/pivot_group_by'; import type { TransformId, TransformPivotConfig } from '../types/transform'; -import { transformStateSchema } from './common'; +import { transformStateSchema, runtimeMappingsSchema } from './common'; // GET transforms export const getTransformsRequestSchema = schema.arrayOf( @@ -64,30 +64,6 @@ export const settingsSchema = schema.object({ docs_per_second: schema.maybe(schema.nullable(schema.number())), }); -export const runtimeMappingsSchema = schema.maybe( - schema.recordOf( - schema.string(), - schema.object({ - type: schema.oneOf([ - schema.literal('keyword'), - schema.literal('long'), - schema.literal('double'), - schema.literal('date'), - schema.literal('ip'), - schema.literal('boolean'), - ]), - script: schema.maybe( - schema.oneOf([ - schema.string(), - schema.object({ - source: schema.string(), - }), - ]) - ), - }) - ) -); - export const sourceSchema = schema.object({ runtime_mappings: runtimeMappingsSchema, index: schema.oneOf([schema.string(), schema.arrayOf(schema.string())]), diff --git a/x-pack/plugins/transform/common/shared_imports.ts b/x-pack/plugins/transform/common/shared_imports.ts index 4506083a1876f..3062c7ab8d23c 100644 --- a/x-pack/plugins/transform/common/shared_imports.ts +++ b/x-pack/plugins/transform/common/shared_imports.ts @@ -6,5 +6,9 @@ */ export type { HitsTotalRelation, SearchResponse7 } from '../../ml/common'; -export { HITS_TOTAL_RELATION } from '../../ml/common'; -export { composeValidators, patternValidator } from '../../ml/common'; +export { + composeValidators, + patternValidator, + ChartData, + HITS_TOTAL_RELATION, +} from '../../ml/common'; 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 388bc8b432fc4..7afbc5e403b78 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_api.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_api.ts @@ -16,7 +16,10 @@ import type { DeleteTransformsRequestSchema, DeleteTransformsResponseSchema, } from '../../../common/api_schemas/delete_transforms'; -import type { FieldHistogramsResponseSchema } from '../../../common/api_schemas/field_histograms'; +import type { + FieldHistogramsRequestSchema, + FieldHistogramsResponseSchema, +} from '../../../common/api_schemas/field_histograms'; import type { StartTransformsRequestSchema, StartTransformsResponseSchema, @@ -194,6 +197,7 @@ export const useApi = () => { indexPatternTitle: string, fields: FieldHistogramRequestConfig[], query: string | SavedSearchQuery, + runtimeMappings?: FieldHistogramsRequestSchema['runtimeMappings'], samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE ): Promise { try { @@ -202,6 +206,7 @@ export const useApi = () => { query, fields, samplerShardSize, + ...(runtimeMappings !== undefined ? { runtimeMappings } : {}), }), }); } 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 dde4c7eb0f3a0..e12aa78e33622 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 @@ -150,7 +150,8 @@ export const useIndexData = ( fieldName: cT.id, type: getFieldType(cT.schema), })), - isDefaultQuery(query) ? matchAllQuery : query + isDefaultQuery(query) ? matchAllQuery : query, + combinedRuntimeMappings ); if (!isFieldHistogramsResponseSchema(columnChartsData)) { 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 087bae97e287e..23c4cfcefe8ef 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 @@ -13,6 +13,7 @@ import { EuiCodeEditor } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { StepDefineFormHook } from '../step_define'; +import { isRuntimeMappings } from '../step_define/common/types'; export const AdvancedRuntimeMappingsEditor: FC = memo( ({ @@ -43,8 +44,8 @@ export const AdvancedRuntimeMappingsEditor: FC]/g; @@ -77,7 +77,7 @@ export function getPivotDropdownOptions( // Support for runtime_mappings that are defined by queries let runtimeFields: Field[] = []; - if (isPopulatedObject(runtimeMappings)) { + if (isRuntimeMappings(runtimeMappings)) { runtimeFields = Object.keys(runtimeMappings).map((fieldName) => { const field = runtimeMappings[fieldName]; return { name: fieldName, type: getKibanaFieldTypeFromEsType(field.type) }; 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 new file mode 100644 index 0000000000000..ec90d31a0d169 --- /dev/null +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/common/types.test.ts @@ -0,0 +1,71 @@ +/* + * 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 6c9293a6d13cf..d0218996b2e4f 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,6 +24,8 @@ import { } from '../../../../../../../common/types/transform'; import { LatestFunctionConfig } from '../../../../../../../common/api_schemas/transforms'; +import { isPopulatedObject } from '../../../../../common/utils/object_utils'; + export interface ErrorMessage { query: string; message: string; @@ -70,10 +72,30 @@ export interface StepDefineExposedState { isRuntimeMappingsEditorEnabled: boolean; } +export function isRuntimeField(arg: any): 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) + ); +} + +export function isRuntimeMappings(arg: any): arg is RuntimeMappings { + return isPopulatedObject(arg) && Object.values(arg).every((d) => isRuntimeField(d)); +} + export function isPivotPartialRequest(arg: any): arg is { pivot: PivotConfigDefinition } { - return typeof arg === 'object' && arg.hasOwnProperty('pivot'); + return isPopulatedObject(arg) && arg.hasOwnProperty('pivot'); } export function isLatestPartialRequest(arg: any): arg is { latest: LatestFunctionConfig } { - return typeof arg === 'object' && arg.hasOwnProperty('latest'); + return isPopulatedObject(arg) && arg.hasOwnProperty('latest'); } diff --git a/x-pack/plugins/transform/server/routes/api/field_histograms.ts b/x-pack/plugins/transform/server/routes/api/field_histograms.ts index d3269bf14322a..bfe2f47078569 100644 --- a/x-pack/plugins/transform/server/routes/api/field_histograms.ts +++ b/x-pack/plugins/transform/server/routes/api/field_histograms.ts @@ -32,7 +32,7 @@ export function registerFieldHistogramsRoutes({ router, license }: RouteDependen license.guardApiRoute( async (ctx, req, res) => { const { indexPatternTitle } = req.params; - const { query, fields, samplerShardSize } = req.body; + const { query, fields, runtimeMappings, samplerShardSize } = req.body; try { const resp = await getHistogramsForFields( @@ -40,7 +40,8 @@ export function registerFieldHistogramsRoutes({ router, license }: RouteDependen indexPatternTitle, query, fields, - samplerShardSize + samplerShardSize, + runtimeMappings ); return res.ok({ body: resp });