From d264710bc8b5e23c898e1020f3a866d3986fea35 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 29 Oct 2025 15:39:48 -0400 Subject: [PATCH 01/12] Define schemas for simple filter interface --- .../shared/kbn-es-query-server/index.ts | 11 +- .../src/filter/simplified_filter.ts | 194 ++++++++++++++++++ .../src/filter/stored_filter.ts | 62 ++++++ .../shared/kbn-es-query-server/src/types.ts | 30 ++- .../shared/kbn-es-query-server/tsconfig.json | 1 - 5 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts create mode 100644 src/platform/packages/shared/kbn-es-query-server/src/filter/stored_filter.ts diff --git a/src/platform/packages/shared/kbn-es-query-server/index.ts b/src/platform/packages/shared/kbn-es-query-server/index.ts index 77a0cf69c8f3d..475e291bc5c25 100644 --- a/src/platform/packages/shared/kbn-es-query-server/index.ts +++ b/src/platform/packages/shared/kbn-es-query-server/index.ts @@ -9,7 +9,8 @@ export { timeRangeSchema } from './src/time_range'; export { querySchema } from './src/query'; -export { filterSchema } from './src/filter'; +export { appStateSchema, globalStateSchema, filterSchema } from './src/filter/stored_filter'; +export { simplifiedFilterSchema } from './src/filter/simplified_filter'; export type { TimeRange, @@ -19,4 +20,12 @@ export type { FilterMeta, AggregateQuery, Query, + SimplifiedFilter, + SimpleFilterCondition, + FilterGroup, + RawDSLFilter, + FilterOperator, + FilterValue, + RangeValue, + StoredFilterState, } from './src/types'; diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts new file mode 100644 index 0000000000000..54b38d9b73e64 --- /dev/null +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -0,0 +1,194 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +/** + * Validation Schemas for Simplified Filter Interface + * + * This module provides @kbn/config-schema validation schemas that correspond + * to the TypeScript types defined in simplified_filter_types.ts. + * + * These schemas are used for runtime validation of API requests and responses + * in * as Code APIs. + */ + +import { schema } from '@kbn/config-schema'; + +// ==================================================================== +// CORE FILTER OPERATOR AND VALUE SCHEMAS +// ==================================================================== + +/** + * Schema for supported filter operators + */ +export const filterOperatorSchema = schema.oneOf([ + schema.literal('is'), + schema.literal('is_not'), + schema.literal('is_one_of'), + schema.literal('is_not_one_of'), + schema.literal('exists'), + schema.literal('not_exists'), + schema.literal('range'), +]); + +/** + * Schema for range values used in numeric and date filters + */ +export const rangeValueSchema = schema.object({ + gte: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + lte: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + gt: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + lt: schema.maybe(schema.oneOf([schema.number(), schema.string()])), +}); + +/** + * Schema for all possible filter values + * Supports single values, arrays, and range objects + */ +export const filterValueSchema = schema.oneOf([ + schema.string(), + schema.number(), + schema.boolean(), + schema.arrayOf(schema.oneOf([schema.string(), schema.number(), schema.boolean()])), + rangeValueSchema, +]); + +// ==================================================================== +// BASE FILTER PROPERTIES (SHARED BY ALL SIMPLIFIED FILTERS) +// ==================================================================== + +/** + * Base properties shared by all simplified filters + */ +const baseFilterPropertiesSchema = { + id: schema.maybe(schema.string()), + pinned: schema.maybe(schema.boolean()), + disabled: schema.maybe(schema.boolean()), + controlledBy: schema.maybe(schema.string()), + indexPattern: schema.maybe(schema.string()), + negate: schema.maybe(schema.boolean()), + label: schema.maybe(schema.string()), +}; + +// ==================================================================== +// SIMPLE FILTER CONDITION SCHEMAS +// ==================================================================== + +/** + * Base schema for simple filter conditions + */ +const baseFilterConditionSchema = { + field: schema.string(), +}; + +// ==================================================================== +// DISCRIMINATED FILTER CONDITION SCHEMAS +// ==================================================================== + +/** + * Schema for filter conditions that require a value + */ +export const filterConditionWithValueSchema = schema.object({ + ...baseFilterConditionSchema, + operator: schema.oneOf([ + schema.literal('is'), + schema.literal('is_not'), + schema.literal('is_one_of'), + schema.literal('is_not_one_of'), + schema.literal('range'), + ]), + value: filterValueSchema, +}); + +/** + * Schema for filter conditions that check existence only + */ +export const filterConditionExistsSchema = schema.object({ + ...baseFilterConditionSchema, + operator: schema.oneOf([schema.literal('exists'), schema.literal('not_exists')]), + // value is intentionally omitted for exists/not_exists operators +}); + +/** + * Discriminated union schema for simple filter conditions + */ +export const simpleFilterConditionSchema = schema.oneOf([ + filterConditionWithValueSchema, + filterConditionExistsSchema, +]); + +// ==================================================================== +// FILTER GROUP SCHEMA (RECURSIVE) +// ==================================================================== + +/** + * Schema for logical filter groups with recursive structure + * Uses lazy schema to handle recursive references + * Note: Groups only contain logical structure (type, conditions) - no metadata properties + */ +export const filterGroupSchema = schema.object( + { + type: schema.oneOf([schema.literal('AND'), schema.literal('OR')]), + conditions: schema.arrayOf( + schema.oneOf([ + simpleFilterConditionSchema, + schema.lazy('filterGroup'), // Recursive reference + ]) + ), + }, + { meta: { id: 'filterGroup' } } +); + +// ==================================================================== +// RAW DSL FILTER SCHEMA +// ==================================================================== + +/** + * Schema for raw Elasticsearch Query DSL filters + */ +export const rawDSLFilterSchema = schema.object({ + query: schema.recordOf(schema.string(), schema.any()), +}); + +// ==================================================================== +// SIMPLIFIED FILTER DISCRIMINATED UNION SCHEMA +// ==================================================================== + +/** + * Schema for simple condition filters (Tier 1) + */ +export const simplifiedConditionFilterSchema = schema.object({ + ...baseFilterPropertiesSchema, + condition: simpleFilterConditionSchema, +}); + +/** + * Schema for grouped condition filters (Tier 2-3) + */ +export const simplifiedGroupFilterSchema = schema.object({ + ...baseFilterPropertiesSchema, + group: filterGroupSchema, +}); + +/** + * Schema for raw DSL filters (Tier 4) + */ +export const simplifiedDSLFilterSchema = schema.object({ + ...baseFilterPropertiesSchema, + dsl: rawDSLFilterSchema, +}); + +/** + * Main discriminated union schema for SimplifiedFilter + * Ensures exactly one of: condition, group, or dsl is present + */ +export const simplifiedFilterSchema = schema.oneOf([ + simplifiedConditionFilterSchema, + simplifiedGroupFilterSchema, + simplifiedDSLFilterSchema, +]); diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/stored_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/stored_filter.ts new file mode 100644 index 0000000000000..7a4b22f1c9c7e --- /dev/null +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/stored_filter.ts @@ -0,0 +1,62 @@ +/* + * 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", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { schema } from '@kbn/config-schema'; + +export const appStateSchema = schema.literal('appState'); +export const globalStateSchema = schema.literal('globalState'); + +const filterStateStoreSchema = schema.oneOf([appStateSchema, globalStateSchema], { + meta: { + description: + "Denote whether a filter is specific to an application's context (e.g. 'appState') or whether it should be applied globally (e.g. 'globalState').", + }, +}); + +export const filterMetaSchema = schema.object( + { + alias: schema.maybe(schema.nullable(schema.string())), + disabled: schema.maybe(schema.boolean()), + negate: schema.maybe(schema.boolean()), + controlledBy: schema.maybe( + schema.string({ meta: { description: 'Identifies the owner the filter.' } }) + ), + group: schema.maybe( + schema.string({ meta: { description: 'The group to which this filter belongs.' } }) + ), + relation: schema.maybe(schema.string()), + // field is missing from the Filter type, but is stored in SerializedSearchSourceFields + // see the todo in src/platform/packages/shared/kbn-es-query/src/filters/helpers/update_filter.ts + field: schema.maybe(schema.string()), + index: schema.maybe(schema.string()), + isMultiIndex: schema.maybe(schema.boolean()), + type: schema.maybe(schema.string()), + key: schema.maybe(schema.string()), + // We could consider creating FilterMetaParams as a schema to match the concrete Filter type. + // However, this is difficult because FilterMetaParams can be a `filterSchema` which is defined below. + // This would require a more complex schema definition that can handle recursive types. + // For now, we use `schema.any()` to allow flexibility in the params field. + params: schema.maybe(schema.any()), + value: schema.maybe(schema.string()), + }, + { unknowns: 'allow' } +); + +export const filterSchema = schema.object( + { + meta: filterMetaSchema, + query: schema.maybe(schema.recordOf(schema.string(), schema.any())), + $state: schema.maybe( + schema.object({ + store: filterStateStoreSchema, + }) + ), + }, + { meta: { id: 'kbn-es-query-server-filterSchema' } } +); diff --git a/src/platform/packages/shared/kbn-es-query-server/src/types.ts b/src/platform/packages/shared/kbn-es-query-server/src/types.ts index d2ba7e7575ab5..d3cd4e5ff84bd 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/types.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/types.ts @@ -15,7 +15,21 @@ import type { relativeTimeRangeSchema, } from './time_range'; import type { aggregateQuerySchema, querySchema } from './query'; -import type { filterSchema, filterMetaSchema } from './filter'; +import type { + filterSchema, + filterMetaSchema, + globalStateSchema, + appStateSchema, +} from './filter/stored_filter'; +import type { + simplifiedFilterSchema, + simpleFilterConditionSchema, + filterGroupSchema, + rawDSLFilterSchema, + filterOperatorSchema, + filterValueSchema, + rangeValueSchema, +} from './filter/simplified_filter'; export type TimeRange = Writable>; export type AbsoluteTimeRange = TypeOf; @@ -26,3 +40,17 @@ export type AggregateQuery = Writable>; export type Filter = Writable>; export type FilterMeta = Writable>; + +/** + * Schema-inferred types for Simplified Filter API + * + * These types are inferred from validation schemas and provide runtime validation compatibility. + */ +export type SimplifiedFilter = Writable>; +export type SimpleFilterCondition = Writable>; +export type FilterGroup = Writable>; +export type RawDSLFilter = Writable>; +export type FilterOperator = Writable>; +export type FilterValue = Writable>; +export type RangeValue = Writable>; +export type StoredFilterState = TypeOf | TypeOf; diff --git a/src/platform/packages/shared/kbn-es-query-server/tsconfig.json b/src/platform/packages/shared/kbn-es-query-server/tsconfig.json index 74c4453447454..50aad51a203f1 100644 --- a/src/platform/packages/shared/kbn-es-query-server/tsconfig.json +++ b/src/platform/packages/shared/kbn-es-query-server/tsconfig.json @@ -9,6 +9,5 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/utility-types", - "@kbn/es-query", ], } From 5989e982a90d16dbd1b25858feae971a8c76dff8 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 29 Oct 2025 16:36:28 -0400 Subject: [PATCH 02/12] Add descriptions to schemas --- .../shared/kbn-es-query-server/index.ts | 1 - .../src/filter/simplified_filter.ts | 180 +++++++++++------- .../shared/kbn-es-query-server/src/types.ts | 2 - 3 files changed, 113 insertions(+), 70 deletions(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/index.ts b/src/platform/packages/shared/kbn-es-query-server/index.ts index 475e291bc5c25..03cfa425cf868 100644 --- a/src/platform/packages/shared/kbn-es-query-server/index.ts +++ b/src/platform/packages/shared/kbn-es-query-server/index.ts @@ -24,7 +24,6 @@ export type { SimpleFilterCondition, FilterGroup, RawDSLFilter, - FilterOperator, FilterValue, RangeValue, StoredFilterState, diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index 54b38d9b73e64..420be555ae27d 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -10,10 +10,7 @@ /** * Validation Schemas for Simplified Filter Interface * - * This module provides @kbn/config-schema validation schemas that correspond - * to the TypeScript types defined in simplified_filter_types.ts. - * - * These schemas are used for runtime validation of API requests and responses + * These schemas are used for server validation of API requests and responses * in * as Code APIs. */ @@ -23,40 +20,46 @@ import { schema } from '@kbn/config-schema'; // CORE FILTER OPERATOR AND VALUE SCHEMAS // ==================================================================== -/** - * Schema for supported filter operators - */ -export const filterOperatorSchema = schema.oneOf([ - schema.literal('is'), - schema.literal('is_not'), - schema.literal('is_one_of'), - schema.literal('is_not_one_of'), - schema.literal('exists'), - schema.literal('not_exists'), - schema.literal('range'), -]); - /** * Schema for range values used in numeric and date filters */ export const rangeValueSchema = schema.object({ - gte: schema.maybe(schema.oneOf([schema.number(), schema.string()])), - lte: schema.maybe(schema.oneOf([schema.number(), schema.string()])), - gt: schema.maybe(schema.oneOf([schema.number(), schema.string()])), - lt: schema.maybe(schema.oneOf([schema.number(), schema.string()])), + gte: schema.maybe( + schema.oneOf([schema.number(), schema.string()], { + meta: { description: 'Greater than or equal to' }, + }) + ), + lte: schema.maybe( + schema.oneOf([schema.number(), schema.string()], { + meta: { description: 'Less than or equal to' }, + }) + ), + gt: schema.maybe( + schema.oneOf([schema.number(), schema.string()], { + meta: { description: 'Greater than' }, + }) + ), + lt: schema.maybe( + schema.oneOf([schema.number(), schema.string()], { + meta: { description: 'Less than' }, + }) + ), }); /** * Schema for all possible filter values * Supports single values, arrays, and range objects */ -export const filterValueSchema = schema.oneOf([ - schema.string(), - schema.number(), - schema.boolean(), - schema.arrayOf(schema.oneOf([schema.string(), schema.number(), schema.boolean()])), - rangeValueSchema, -]); +export const filterValueSchema = schema.oneOf( + [ + schema.string(), + schema.number(), + schema.boolean(), + schema.arrayOf(schema.oneOf([schema.string(), schema.number(), schema.boolean()])), + rangeValueSchema, + ], + { meta: { description: 'Possible filter values that could be single values, arrays, or ranges' } } +); // ==================================================================== // BASE FILTER PROPERTIES (SHARED BY ALL SIMPLIFIED FILTERS) @@ -66,13 +69,41 @@ export const filterValueSchema = schema.oneOf([ * Base properties shared by all simplified filters */ const baseFilterPropertiesSchema = { - id: schema.maybe(schema.string()), - pinned: schema.maybe(schema.boolean()), - disabled: schema.maybe(schema.boolean()), - controlledBy: schema.maybe(schema.string()), - indexPattern: schema.maybe(schema.string()), - negate: schema.maybe(schema.boolean()), - label: schema.maybe(schema.string()), + id: schema.maybe( + schema.string({ + meta: { description: 'Unique identifier for the filter' }, + }) + ), + pinned: schema.maybe( + schema.boolean({ + meta: { description: 'Whether the filter is pinned' }, + }) + ), + disabled: schema.maybe( + schema.boolean({ + meta: { description: 'Whether the filter is disabled' }, + }) + ), + controlledBy: schema.maybe( + schema.string({ + meta: { description: 'Owner that manages this filter' }, + }) + ), + indexPattern: schema.maybe( + schema.string({ + meta: { description: 'Data view ID that this filter applies to' }, + }) + ), + negate: schema.maybe( + schema.boolean({ + meta: { description: 'Whether to negate the filter condition' }, + }) + ), + label: schema.maybe( + schema.string({ + meta: { description: 'Human-readable label for the filter' }, + }) + ), }; // ==================================================================== @@ -83,7 +114,7 @@ const baseFilterPropertiesSchema = { * Base schema for simple filter conditions */ const baseFilterConditionSchema = { - field: schema.string(), + field: schema.string({ meta: { description: 'Field the filter applies to' } }), }; // ==================================================================== @@ -95,13 +126,16 @@ const baseFilterConditionSchema = { */ export const filterConditionWithValueSchema = schema.object({ ...baseFilterConditionSchema, - operator: schema.oneOf([ - schema.literal('is'), - schema.literal('is_not'), - schema.literal('is_one_of'), - schema.literal('is_not_one_of'), - schema.literal('range'), - ]), + operator: schema.oneOf( + [ + schema.literal('is'), + schema.literal('is_not'), + schema.literal('is_one_of'), + schema.literal('is_not_one_of'), + schema.literal('range'), + ], + { meta: { description: 'Filter operators that require a value' } } + ), value: filterValueSchema, }); @@ -110,17 +144,19 @@ export const filterConditionWithValueSchema = schema.object({ */ export const filterConditionExistsSchema = schema.object({ ...baseFilterConditionSchema, - operator: schema.oneOf([schema.literal('exists'), schema.literal('not_exists')]), + operator: schema.oneOf([schema.literal('exists'), schema.literal('not_exists')], { + meta: { description: 'Filter operators that check existence' }, + }), // value is intentionally omitted for exists/not_exists operators }); /** * Discriminated union schema for simple filter conditions */ -export const simpleFilterConditionSchema = schema.oneOf([ - filterConditionWithValueSchema, - filterConditionExistsSchema, -]); +export const simpleFilterConditionSchema = schema.oneOf( + [filterConditionWithValueSchema, filterConditionExistsSchema], + { meta: { description: 'A filter condition' } } +); // ==================================================================== // FILTER GROUP SCHEMA (RECURSIVE) @@ -141,7 +177,7 @@ export const filterGroupSchema = schema.object( ]) ), }, - { meta: { id: 'filterGroup' } } + { meta: { description: 'Grouped filters', id: 'filterGroup' } } ); // ==================================================================== @@ -152,7 +188,9 @@ export const filterGroupSchema = schema.object( * Schema for raw Elasticsearch Query DSL filters */ export const rawDSLFilterSchema = schema.object({ - query: schema.recordOf(schema.string(), schema.any()), + query: schema.recordOf(schema.string(), schema.any(), { + meta: { description: 'Elasticsearch Query DSL object' }, + }), }); // ==================================================================== @@ -162,33 +200,41 @@ export const rawDSLFilterSchema = schema.object({ /** * Schema for simple condition filters (Tier 1) */ -export const simplifiedConditionFilterSchema = schema.object({ - ...baseFilterPropertiesSchema, - condition: simpleFilterConditionSchema, -}); +export const simplifiedConditionFilterSchema = schema.object( + { + ...baseFilterPropertiesSchema, + condition: simpleFilterConditionSchema, + }, + { meta: { description: 'Simple condition filter' } } +); /** * Schema for grouped condition filters (Tier 2-3) */ -export const simplifiedGroupFilterSchema = schema.object({ - ...baseFilterPropertiesSchema, - group: filterGroupSchema, -}); +export const simplifiedGroupFilterSchema = schema.object( + { + ...baseFilterPropertiesSchema, + group: filterGroupSchema, + }, + { meta: { description: 'Grouped condition filter' } } +); /** * Schema for raw DSL filters (Tier 4) */ -export const simplifiedDSLFilterSchema = schema.object({ - ...baseFilterPropertiesSchema, - dsl: rawDSLFilterSchema, -}); +export const simplifiedDSLFilterSchema = schema.object( + { + ...baseFilterPropertiesSchema, + dsl: rawDSLFilterSchema, + }, + { meta: { description: 'Raw DSL filter' } } +); /** * Main discriminated union schema for SimplifiedFilter * Ensures exactly one of: condition, group, or dsl is present */ -export const simplifiedFilterSchema = schema.oneOf([ - simplifiedConditionFilterSchema, - simplifiedGroupFilterSchema, - simplifiedDSLFilterSchema, -]); +export const simplifiedFilterSchema = schema.oneOf( + [simplifiedConditionFilterSchema, simplifiedGroupFilterSchema, simplifiedDSLFilterSchema], + { meta: { description: 'A filter which can be a condition, group, or raw DSL' } } +); diff --git a/src/platform/packages/shared/kbn-es-query-server/src/types.ts b/src/platform/packages/shared/kbn-es-query-server/src/types.ts index d3cd4e5ff84bd..fffdcf0ec7138 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/types.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/types.ts @@ -26,7 +26,6 @@ import type { simpleFilterConditionSchema, filterGroupSchema, rawDSLFilterSchema, - filterOperatorSchema, filterValueSchema, rangeValueSchema, } from './filter/simplified_filter'; @@ -50,7 +49,6 @@ export type SimplifiedFilter = Writable>; export type SimpleFilterCondition = Writable>; export type FilterGroup = Writable>; export type RawDSLFilter = Writable>; -export type FilterOperator = Writable>; export type FilterValue = Writable>; export type RangeValue = Writable>; export type StoredFilterState = TypeOf | TypeOf; From f9007706dd6446711a42945f759bfc84840b51c4 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Thu, 30 Oct 2025 15:32:05 -0400 Subject: [PATCH 03/12] Remove id and rename indexPattern to dataViewId --- .../kbn-es-query-server/src/filter/simplified_filter.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index 420be555ae27d..5859a138bdcff 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -69,11 +69,6 @@ export const filterValueSchema = schema.oneOf( * Base properties shared by all simplified filters */ const baseFilterPropertiesSchema = { - id: schema.maybe( - schema.string({ - meta: { description: 'Unique identifier for the filter' }, - }) - ), pinned: schema.maybe( schema.boolean({ meta: { description: 'Whether the filter is pinned' }, @@ -89,7 +84,7 @@ const baseFilterPropertiesSchema = { meta: { description: 'Owner that manages this filter' }, }) ), - indexPattern: schema.maybe( + dataViewId: schema.maybe( schema.string({ meta: { description: 'Data view ID that this filter applies to' }, }) From fb0f1387aa83cc23fa4e7d67ef9d0576f41c840b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 31 Oct 2025 20:26:30 +0000 Subject: [PATCH 04/12] Changes from node scripts/lint_ts_projects --fix --- src/platform/packages/shared/kbn-es-query-server/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/packages/shared/kbn-es-query-server/tsconfig.json b/src/platform/packages/shared/kbn-es-query-server/tsconfig.json index 50aad51a203f1..74c4453447454 100644 --- a/src/platform/packages/shared/kbn-es-query-server/tsconfig.json +++ b/src/platform/packages/shared/kbn-es-query-server/tsconfig.json @@ -9,5 +9,6 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/utility-types", + "@kbn/es-query", ], } From b49449a22074109f69d1d8902f43aa4113696e87 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 08:35:40 -0500 Subject: [PATCH 05/12] Fix filter value schema arrays to be homogeneous --- .../kbn-es-query-server/src/filter/simplified_filter.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index 5859a138bdcff..404a57d586421 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -48,17 +48,18 @@ export const rangeValueSchema = schema.object({ /** * Schema for all possible filter values - * Supports single values, arrays, and range objects */ export const filterValueSchema = schema.oneOf( [ schema.string(), schema.number(), schema.boolean(), - schema.arrayOf(schema.oneOf([schema.string(), schema.number(), schema.boolean()])), + schema.arrayOf(schema.string()), + schema.arrayOf(schema.number()), + schema.arrayOf(schema.boolean()), rangeValueSchema, ], - { meta: { description: 'Possible filter values that could be single values, arrays, or ranges' } } + { meta: { description: 'Filter value - single value, array of homogeneous values, or range' } } ); // ==================================================================== From ab20892c593c4d15606fe0d63dfb35311ce97305 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 08:37:01 -0500 Subject: [PATCH 06/12] Fix casing inconsistency --- .../shared/kbn-es-query-server/src/filter/simplified_filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index 404a57d586421..1ed4ad379bd31 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -165,7 +165,7 @@ export const simpleFilterConditionSchema = schema.oneOf( */ export const filterGroupSchema = schema.object( { - type: schema.oneOf([schema.literal('AND'), schema.literal('OR')]), + type: schema.oneOf([schema.literal('and'), schema.literal('or')]), conditions: schema.arrayOf( schema.oneOf([ simpleFilterConditionSchema, From 48689fcad8187a17168879b26a41aaa5e4e5cb89 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 08:50:17 -0500 Subject: [PATCH 07/12] Naming consistency --- .../packages/shared/kbn-es-query-server/index.ts | 12 ++++++------ .../src/filter/simplified_filter.ts | 14 +++++++------- .../shared/kbn-es-query-server/src/types.ts | 14 +++++++------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/index.ts b/src/platform/packages/shared/kbn-es-query-server/index.ts index 03cfa425cf868..2d83084ea8e23 100644 --- a/src/platform/packages/shared/kbn-es-query-server/index.ts +++ b/src/platform/packages/shared/kbn-es-query-server/index.ts @@ -10,7 +10,7 @@ export { timeRangeSchema } from './src/time_range'; export { querySchema } from './src/query'; export { appStateSchema, globalStateSchema, filterSchema } from './src/filter/stored_filter'; -export { simplifiedFilterSchema } from './src/filter/simplified_filter'; +export { simpleFilterSchema } from './src/filter/simplified_filter'; export type { TimeRange, @@ -20,11 +20,11 @@ export type { FilterMeta, AggregateQuery, Query, - SimplifiedFilter, + SimpleFilter, SimpleFilterCondition, - FilterGroup, - RawDSLFilter, - FilterValue, - RangeValue, + SimpleFilterGroup, + SimpleDSLFilter, + SimpleFilterValue, + SimpleRangeValue, StoredFilterState, } from './src/types'; diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index 1ed4ad379bd31..fb30ffe951b64 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -190,13 +190,13 @@ export const rawDSLFilterSchema = schema.object({ }); // ==================================================================== -// SIMPLIFIED FILTER DISCRIMINATED UNION SCHEMA +// SIMPLE FILTER DISCRIMINATED UNION SCHEMA // ==================================================================== /** * Schema for simple condition filters (Tier 1) */ -export const simplifiedConditionFilterSchema = schema.object( +export const simpleConditionFilterSchema = schema.object( { ...baseFilterPropertiesSchema, condition: simpleFilterConditionSchema, @@ -207,7 +207,7 @@ export const simplifiedConditionFilterSchema = schema.object( /** * Schema for grouped condition filters (Tier 2-3) */ -export const simplifiedGroupFilterSchema = schema.object( +export const simpleGroupFilterSchema = schema.object( { ...baseFilterPropertiesSchema, group: filterGroupSchema, @@ -218,7 +218,7 @@ export const simplifiedGroupFilterSchema = schema.object( /** * Schema for raw DSL filters (Tier 4) */ -export const simplifiedDSLFilterSchema = schema.object( +export const simpleDSLFilterSchema = schema.object( { ...baseFilterPropertiesSchema, dsl: rawDSLFilterSchema, @@ -227,10 +227,10 @@ export const simplifiedDSLFilterSchema = schema.object( ); /** - * Main discriminated union schema for SimplifiedFilter + * Main discriminated union schema for SimpleFilter * Ensures exactly one of: condition, group, or dsl is present */ -export const simplifiedFilterSchema = schema.oneOf( - [simplifiedConditionFilterSchema, simplifiedGroupFilterSchema, simplifiedDSLFilterSchema], +export const simpleFilterSchema = schema.oneOf( + [simpleConditionFilterSchema, simpleGroupFilterSchema, simpleDSLFilterSchema], { meta: { description: 'A filter which can be a condition, group, or raw DSL' } } ); diff --git a/src/platform/packages/shared/kbn-es-query-server/src/types.ts b/src/platform/packages/shared/kbn-es-query-server/src/types.ts index fffdcf0ec7138..e796ec05e0b58 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/types.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/types.ts @@ -22,7 +22,7 @@ import type { appStateSchema, } from './filter/stored_filter'; import type { - simplifiedFilterSchema, + simpleFilterSchema, simpleFilterConditionSchema, filterGroupSchema, rawDSLFilterSchema, @@ -41,14 +41,14 @@ export type Filter = Writable>; export type FilterMeta = Writable>; /** - * Schema-inferred types for Simplified Filter API + * Schema-inferred types for Simple Filter API * * These types are inferred from validation schemas and provide runtime validation compatibility. */ -export type SimplifiedFilter = Writable>; +export type SimpleFilter = Writable>; export type SimpleFilterCondition = Writable>; -export type FilterGroup = Writable>; -export type RawDSLFilter = Writable>; -export type FilterValue = Writable>; -export type RangeValue = Writable>; +export type SimpleFilterGroup = Writable>; +export type SimpleDSLFilter = Writable>; +export type SimpleFilterValue = Writable>; +export type SimpleRangeValue = Writable>; export type StoredFilterState = TypeOf | TypeOf; From f13c0240b76eefd97b9cf277488ff8b9c5817838 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 08:59:05 -0500 Subject: [PATCH 08/12] Stricter discriminated filter conditions --- .../src/filter/simplified_filter.ts | 69 ++++++++++++------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index fb30ffe951b64..6d5a5bd2f5ed4 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -107,51 +107,74 @@ const baseFilterPropertiesSchema = { // ==================================================================== /** - * Base schema for simple filter conditions + * Common field property for all filter conditions */ -const baseFilterConditionSchema = { +const filterConditionFieldSchema = { field: schema.string({ meta: { description: 'Field the filter applies to' } }), }; -// ==================================================================== -// DISCRIMINATED FILTER CONDITION SCHEMAS -// ==================================================================== +/** + * Schema for 'is' and 'is_not' operators with single value + */ +const filterConditionIsSingleSchema = schema.object({ + ...filterConditionFieldSchema, + operator: schema.oneOf([schema.literal('is'), schema.literal('is_not')], { + meta: { description: 'Single value comparison operators' }, + }), + value: schema.oneOf([schema.string(), schema.number(), schema.boolean()], { + meta: { description: 'Single value for comparison' }, + }), +}); /** - * Schema for filter conditions that require a value + * Schema for 'is_one_of' and 'is_not_one_of' operators with array values */ -export const filterConditionWithValueSchema = schema.object({ - ...baseFilterConditionSchema, - operator: schema.oneOf( +const filterConditionIsOneOfSchema = schema.object({ + ...filterConditionFieldSchema, + operator: schema.oneOf([schema.literal('is_one_of'), schema.literal('is_not_one_of')], { + meta: { description: 'Array value comparison operators' }, + }), + value: schema.oneOf( [ - schema.literal('is'), - schema.literal('is_not'), - schema.literal('is_one_of'), - schema.literal('is_not_one_of'), - schema.literal('range'), + schema.arrayOf(schema.string()), + schema.arrayOf(schema.number()), + schema.arrayOf(schema.boolean()), ], - { meta: { description: 'Filter operators that require a value' } } + { meta: { description: 'Homogeneous array of values' } } ), - value: filterValueSchema, }); /** - * Schema for filter conditions that check existence only + * Schema for 'range' operator with range value */ -export const filterConditionExistsSchema = schema.object({ - ...baseFilterConditionSchema, +const filterConditionRangeSchema = schema.object({ + ...filterConditionFieldSchema, + operator: schema.literal('range'), + value: rangeValueSchema, +}); + +/** + * Schema for 'exists' and 'not_exists' operators without value + */ +const filterConditionExistsSchema = schema.object({ + ...filterConditionFieldSchema, operator: schema.oneOf([schema.literal('exists'), schema.literal('not_exists')], { - meta: { description: 'Filter operators that check existence' }, + meta: { description: 'Field existence check operators' }, }), // value is intentionally omitted for exists/not_exists operators }); /** - * Discriminated union schema for simple filter conditions + * Discriminated union schema for simple filter conditions with proper operator/value type combinations */ export const simpleFilterConditionSchema = schema.oneOf( - [filterConditionWithValueSchema, filterConditionExistsSchema], - { meta: { description: 'A filter condition' } } + [ + filterConditionIsSingleSchema, + filterConditionIsOneOfSchema, + filterConditionRangeSchema, + filterConditionExistsSchema, + ], + { meta: { description: 'A filter condition with strict operator/value type matching' } } ); // ==================================================================== From 539a159226cf44307cc54af63b44d1350278a80f Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 09:36:45 -0500 Subject: [PATCH 09/12] Document controlledBy field --- .../kbn-es-query-server/src/filter/simplified_filter.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts index 6d5a5bd2f5ed4..5b63f9ed3402f 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts @@ -82,7 +82,9 @@ const baseFilterPropertiesSchema = { ), controlledBy: schema.maybe( schema.string({ - meta: { description: 'Owner that manages this filter' }, + meta: { + description: 'Optional identifier for the component/plugin managing this filter', + }, }) ), dataViewId: schema.maybe( From 565848a5735aa344af894e3061399cf75dd2e5dd Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 09:51:01 -0500 Subject: [PATCH 10/12] Remove duplicate stored filter schema --- .../shared/kbn-es-query-server/src/filter.ts | 62 ------------------- 1 file changed, 62 deletions(-) delete mode 100644 src/platform/packages/shared/kbn-es-query-server/src/filter.ts diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter.ts deleted file mode 100644 index da687d2864e47..0000000000000 --- a/src/platform/packages/shared/kbn-es-query-server/src/filter.ts +++ /dev/null @@ -1,62 +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", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { schema } from '@kbn/config-schema'; -import { FilterStateStore } from '@kbn/es-query'; - -const filterStateStoreSchema = schema.oneOf( - [schema.literal(FilterStateStore.APP_STATE), schema.literal(FilterStateStore.GLOBAL_STATE)], - { - meta: { - description: - "Denote whether a filter is specific to an application's context (e.g. 'appState') or whether it should be applied globally (e.g. 'globalState').", - }, - } -); - -export const filterMetaSchema = schema.object( - { - alias: schema.maybe(schema.nullable(schema.string())), - disabled: schema.maybe(schema.boolean()), - negate: schema.maybe(schema.boolean()), - controlledBy: schema.maybe( - schema.string({ meta: { description: 'Identifies the owner the filter.' } }) - ), - group: schema.maybe( - schema.string({ meta: { description: 'The group to which this filter belongs.' } }) - ), - // field is missing from the Filter type, but is stored in SerializedSearchSourceFields - // see the todo in src/platform/packages/shared/kbn-es-query/src/filters/helpers/update_filter.ts - field: schema.maybe(schema.string()), - index: schema.maybe(schema.string()), - isMultiIndex: schema.maybe(schema.boolean()), - type: schema.maybe(schema.string()), - key: schema.maybe(schema.string()), - // We could consider creating FilterMetaParams as a schema to match the concrete Filter type. - // However, this is difficult because FilterMetaParams can be a `filterSchema` which is defined below. - // This would require a more complex schema definition that can handle recursive types. - // For now, we use `schema.any()` to allow flexibility in the params field. - params: schema.maybe(schema.any()), - value: schema.maybe(schema.string()), - }, - { unknowns: 'allow' } -); - -export const filterSchema = schema.object( - { - meta: filterMetaSchema, - query: schema.maybe(schema.recordOf(schema.string(), schema.any())), - $state: schema.maybe( - schema.object({ - store: filterStateStoreSchema, - }) - ), - }, - { meta: { id: 'kbn-es-query-server-filterSchema' } } -); From 7f85963685e173f66a6a5e4eaa2da38d9fabec1f Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:32:40 +0000 Subject: [PATCH 11/12] Changes from node scripts/lint_ts_projects --fix --- src/platform/packages/shared/kbn-es-query-server/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/packages/shared/kbn-es-query-server/tsconfig.json b/src/platform/packages/shared/kbn-es-query-server/tsconfig.json index 74c4453447454..50aad51a203f1 100644 --- a/src/platform/packages/shared/kbn-es-query-server/tsconfig.json +++ b/src/platform/packages/shared/kbn-es-query-server/tsconfig.json @@ -9,6 +9,5 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/utility-types", - "@kbn/es-query", ], } From 51c30b1c81838bde0d606547360ecd0ffe7fcd57 Mon Sep 17 00:00:00 2001 From: Nick Peihl Date: Wed, 5 Nov 2025 14:17:45 -0500 Subject: [PATCH 12/12] Rename path --- src/platform/packages/shared/kbn-es-query-server/index.ts | 2 +- .../src/filter/{simplified_filter.ts => simple_filter.ts} | 0 src/platform/packages/shared/kbn-es-query-server/src/types.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/platform/packages/shared/kbn-es-query-server/src/filter/{simplified_filter.ts => simple_filter.ts} (100%) diff --git a/src/platform/packages/shared/kbn-es-query-server/index.ts b/src/platform/packages/shared/kbn-es-query-server/index.ts index 2d83084ea8e23..12c2d39ec2b43 100644 --- a/src/platform/packages/shared/kbn-es-query-server/index.ts +++ b/src/platform/packages/shared/kbn-es-query-server/index.ts @@ -10,7 +10,7 @@ export { timeRangeSchema } from './src/time_range'; export { querySchema } from './src/query'; export { appStateSchema, globalStateSchema, filterSchema } from './src/filter/stored_filter'; -export { simpleFilterSchema } from './src/filter/simplified_filter'; +export { simpleFilterSchema } from './src/filter/simple_filter'; export type { TimeRange, diff --git a/src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts b/src/platform/packages/shared/kbn-es-query-server/src/filter/simple_filter.ts similarity index 100% rename from src/platform/packages/shared/kbn-es-query-server/src/filter/simplified_filter.ts rename to src/platform/packages/shared/kbn-es-query-server/src/filter/simple_filter.ts diff --git a/src/platform/packages/shared/kbn-es-query-server/src/types.ts b/src/platform/packages/shared/kbn-es-query-server/src/types.ts index e796ec05e0b58..222f908b558d9 100644 --- a/src/platform/packages/shared/kbn-es-query-server/src/types.ts +++ b/src/platform/packages/shared/kbn-es-query-server/src/types.ts @@ -28,7 +28,7 @@ import type { rawDSLFilterSchema, filterValueSchema, rangeValueSchema, -} from './filter/simplified_filter'; +} from './filter/simple_filter'; export type TimeRange = Writable>; export type AbsoluteTimeRange = TypeOf;