-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[Dashboards as code] Define schemas for As Code API filter interface #241198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
d264710
5989e98
f900770
5037bbe
fb0f138
b49449a
ab20892
48689fc
f13c024
539a159
565848a
7f85963
51c30b1
a2979a9
883b3b6
3cbf28b
00bd945
8aa68b4
e72bb46
6b5e025
f94f926
d1c1ea2
27870ca
36da619
7702432
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| /* | ||
| * 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 | ||
| * | ||
| * These schemas are used for server validation of API requests and responses | ||
| * in * as Code APIs. | ||
| */ | ||
|
|
||
| import { schema } from '@kbn/config-schema'; | ||
|
|
||
| // ==================================================================== | ||
| // CORE FILTER OPERATOR AND VALUE SCHEMAS | ||
| // ==================================================================== | ||
|
|
||
| /** | ||
| * Schema for range values used in numeric and date filters | ||
| */ | ||
| export const rangeValueSchema = schema.object({ | ||
| 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()])), | ||
davismcphee marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| rangeValueSchema, | ||
| ], | ||
| { meta: { description: 'Possible filter values that could be single values, arrays, or ranges' } } | ||
| ); | ||
|
|
||
| // ==================================================================== | ||
| // BASE FILTER PROPERTIES (SHARED BY ALL SIMPLIFIED FILTERS) | ||
| // ==================================================================== | ||
|
|
||
| /** | ||
| * Base properties shared by all simplified filters | ||
| */ | ||
| const baseFilterPropertiesSchema = { | ||
| 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' }, | ||
| }) | ||
| ), | ||
davismcphee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| dataViewId: 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' }, | ||
| }) | ||
| ), | ||
| }; | ||
|
|
||
| // ==================================================================== | ||
| // SIMPLE FILTER CONDITION SCHEMAS | ||
| // ==================================================================== | ||
|
|
||
| /** | ||
| * Base schema for simple filter conditions | ||
| */ | ||
| const baseFilterConditionSchema = { | ||
| field: schema.string({ meta: { description: 'Field the filter applies to' } }), | ||
| }; | ||
|
|
||
| // ==================================================================== | ||
| // 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'), | ||
davismcphee marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| schema.literal('range'), | ||
| ], | ||
| { meta: { description: 'Filter operators that require a value' } } | ||
| ), | ||
| value: filterValueSchema, | ||
| }); | ||
davismcphee marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Schema for filter conditions that check existence only | ||
| */ | ||
| export const filterConditionExistsSchema = schema.object({ | ||
| ...baseFilterConditionSchema, | ||
| 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], | ||
| { meta: { description: 'A filter condition' } } | ||
| ); | ||
|
|
||
| // ==================================================================== | ||
| // 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')]), | ||
davismcphee marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| conditions: schema.arrayOf( | ||
| schema.oneOf([ | ||
| simpleFilterConditionSchema, | ||
| schema.lazy('filterGroup'), // Recursive reference | ||
| ]) | ||
| ), | ||
| }, | ||
| { meta: { description: 'Grouped filters', 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(), { | ||
| meta: { description: 'Elasticsearch Query DSL object' }, | ||
| }), | ||
| }); | ||
|
|
||
| // ==================================================================== | ||
| // SIMPLIFIED FILTER DISCRIMINATED UNION SCHEMA | ||
| // ==================================================================== | ||
|
|
||
| /** | ||
| * Schema for simple condition filters (Tier 1) | ||
| */ | ||
| 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, | ||
| }, | ||
| { meta: { description: 'Grouped condition filter' } } | ||
| ); | ||
|
|
||
| /** | ||
| * Schema for raw DSL filters (Tier 4) | ||
| */ | ||
| 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], | ||
| { meta: { description: 'A filter which can be a condition, group, or raw DSL' } } | ||
| ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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' } } | ||
| ); | ||
Uh oh!
There was an error while loading. Please reload this page.